schemas-dataclass


Nameschemas-dataclass JSON
Version 0.0.1 PyPI version JSON
download
home_pageNone
SummaryPython 2/3 兼容的 DataClass 库,支持完整的数据校验功能和自定义错误消息
upload_time2025-08-23 06:20:20
maintainerNone
docs_urlNone
authorNone
requires_python>=3.8
licenseNone
keywords dataclass fields python2 python3 schema validation
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Schemas DataClass

[![Python Version](https://img.shields.io/badge/python-2.7%2B%2C%203.4%2B-blue.svg)](https://pypi.org/project/schemas-dataclass/)
[![License](https://img.shields.io/badge/license-GPLv3-green.svg)](LICENSE)
[![CI](https://github.com/b40yd/schemas-python/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/b40yd/schemas-python/actions/workflows/ci.yml)
[![Coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/b40yd/9d999999999999999999999999999999/raw/coverage.json)](#)

一个专为 Python 2/3 兼容设计的 DataClass 库,支持完整的数据校验功能、装饰器语法和自定义错误消息。

## 🚀 快速开始

### 安装

```bash
pip install schemas-dataclass
```

### 基础使用

```python
from schemas_dataclass import StringField, NumberField, dataclass, ValidationError

@dataclass
class User(object):
    name = StringField(min_length=2, max_length=50)
    age = NumberField(minvalue=0, maxvalue=120)
    email = StringField(
        regex=r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    )

# 创建用户
user = User(name="Alice", age=25, email="alice@example.com")
print(user.to_dict())  # {'name': 'Alice', 'age': 25, 'email': 'alice@example.com'}
```

### 自定义错误消息

```python
@dataclass
class User(object):
    name = StringField(
        min_length=2,
        error_messages={
            'required': '姓名是必填项',
            'min_length': '姓名至少需要 {min_length} 个字符'
        }
    )

try:
    user = User(name="A")  # 太短
except ValidationError as e:
    print(e.message)  # 输出: 姓名至少需要 2 个字符
```

## 📚 文档索引

- **[安装和使用](#安装和使用)** - 快速上手指南
- **[完整示例](#完整示例)** - 丰富的使用示例
- **[API 参考](#api-参考)** - 详细的 API 文档
- **[测试](#测试)** - 如何运行测试
- **[项目结构说明](PROJECT_STRUCTURE.md)** - 项目文件结构和开发指南
- **[更新日志](CHANGELOG.md)** - 版本更新记录和功能变更
- **[自定义错误消息详细文档](CUSTOM_ERROR_MESSAGES.md)** - 完整的自定义错误消息使用指南
- **[贡献指南](#贡献指南)** - 如何参与项目开发

## 安装和使用

### 安装方式

#### 从 PyPI 安装(推荐)

```bash
pip install schemas-dataclass
```

#### 从源码安装

```bash
git clone https://github.com/schemas/dataclass.git
cd dataclass
python setup.py install
```

#### 开发环境安装

```bash
git clone https://github.com/schemas/dataclass.git
cd dataclass
pip install -e .
pip install -r requirements-dev.txt
```

### 基础使用指南

```python
from schemas_dataclass import StringField, NumberField, ListField, dataclass

@dataclass
class User(object):
    name = StringField(min_length=2, max_length=50)
    age = NumberField(minvalue=0, maxvalue=120)
    email = StringField(
        regex=r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    )
    tags = ListField(item_type=str, required=False)

# 创建和使用
user = User(
    name="Alice",
    age=25,
    email="alice@example.com",
    tags=["developer", "python"]
)

print(user.name)        # Alice
print(user['age'])      # 25
print(user.get('email')) # alice@example.com
print(user.to_dict())   # 转换为字典
```

## 核心特性

### 🔧 字段类型支持

- **StringField**: 字符串字段
  - 长度验证 (`min_length`, `max_length`)
  - 正则表达式验证 (`regex`)
  - 枚举验证 (`choices`)
  - 自定义错误消息支持
  
- **NumberField**: 数字字段(支持int、float、long)
  - 范围验证 (`minvalue`, `maxvalue`)
  - 枚举验证 (`choices`)
  - 自定义错误消息支持
  
- **ListField**: 数组字段
  - 长度验证 (`min_length`, `max_length`)
  - 支持嵌套类型验证 (`item_type`)
  - 支持字符串、数字、dataclass模型嵌套
  - 自定义错误消息支持

### 🌍 自定义错误消息

- **多语言支持**: 支持中文、英文等多语言错误消息
- **模板格式化**: 支持 `{参数名}` 格式的参数替换
- **完整覆盖**: 支持所有验证类型的自定义错误消息
- **向后兼容**: 不影响现有代码,可选使用

```python
# 自定义错误消息示例
@dataclass
class User(object):
    name = StringField(
        min_length=3,
        max_length=20,
        error_messages={
            'required': '用户名是必填项',
            'min_length': '用户名至少需要 {min_length} 个字符',
            'max_length': '用户名不能超过 {max_length} 个字符'
        }
    )
```

### 🎯 装饰器语法

```python
@dataclass
class User(object):
    name = StringField(min_length=1, max_length=100)
    age = NumberField(minvalue=0, maxvalue=150)
```

### 🔍 自定义验证装饰器

```python
@dataclass
class Product(object):
    name = StringField()
    price = NumberField()
    
    @validate("name")
    def validate_name_custom(self, name):
        if not name.isalnum():
            raise ValidationError("Name must be alphanumeric")
    
    @validate("price")
    def validate_price_custom(self, price):
        if price <= 0:
            raise ValidationError("Price must be positive")
```

### 🔧 自定义get方法

```python
@dataclass
class BlogPost(object):
    title = StringField()
    status = StringField(default='draft')
    
    def get_title(self):
        """自定义获取标题的方法"""
        title = self.__dict__.get('title', '')
        status = self.__dict__.get('status', 'draft')
        return "[{0}] {1}".format(status.upper(), title)
```

## 完整示例

### 📁 示例文件

项目提供了丰富的示例文件,位于 `examples/` 目录:

- **[基础使用示例](examples/basic_usage.py)** - 字段类型、dataclass 基础功能
- **[自定义错误消息示例](examples/custom_error_messages.py)** - 多语言错误消息、模板格式化
- **[高级功能示例](examples/advanced_features.py)** - 自定义验证、嵌套 dataclass、条件验证
- **[实际应用示例](examples/real_world_examples.py)** - 用户管理、电商产品、博客系统

### 🚀 运行示例

```bash
# 基础使用示例
python examples/basic_usage.py

# 自定义错误消息示例
python examples/custom_error_messages.py

# 高级功能示例
python examples/advanced_features.py

# 实际应用示例
python examples/real_world_examples.py
```

### 💡 快速示例

#### 用户管理系统

```python
from schemas_dataclass import StringField, NumberField, ListField, dataclass, validate

@dataclass
class User(object):
    username = StringField(
        min_length=3,
        max_length=20,
        regex=r'^[a-zA-Z][a-zA-Z0-9_]*$',
        error_messages={
            'required': '用户名是必填项',
            'min_length': '用户名至少需要 {min_length} 个字符',
            'regex': '用户名必须以字母开头,只能包含字母、数字和下划线'
        }
    )
    
    email = StringField(
        regex=r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
        error_messages={
            'required': '邮箱地址是必填项',
            'regex': '请输入有效的邮箱地址'
        }
    )
    
    age = NumberField(
        minvalue=13,
        maxvalue=120,
        error_messages={
            'minvalue': '年龄不能小于 {minvalue} 岁',
            'maxvalue': '年龄不能大于 {maxvalue} 岁'
        }
    )
    
    tags = ListField(
        item_type=str,
        required=False,
        max_length=10,
        error_messages={
            'max_length': '标签数量不能超过 {max_length} 个'
        }
    )
    
    @validate("username")
    def validate_username_not_reserved(self, username):
        """检查用户名是否为保留词"""
        reserved = ['admin', 'root', 'system']
        if username.lower() in reserved:
            raise ValidationError(f"用户名 '{username}' 是系统保留词")

# 使用示例
user = User(
    username="alice_dev",
    email="alice@example.com",
    age=28,
    tags=["developer", "python"]
)

print("用户: {}".format(user.username))
print("邮箱: {}".format(user.email))
print("年龄: {}".format(user.age))
print("标签: {}".format(user.tags))
```

## API 参考

> **重要变更说明**: 从版本 2.0 开始,所有字段默认为可选 (`required=False`)。如需必填字段,请显式设置 `required=True`。

### 字段类型

#### StringField

```python
StringField(
    default=None,           # 默认值
    alias=None,            # 字段别名
    required=False,        # 是否必填 (默认为 False)
    min_length=None,       # 最小长度
    max_length=None,       # 最大长度
    regex=None,            # 正则表达式
    choices=None,          # 枚举选项
    error_messages=None    # 自定义错误消息
)
```

#### NumberField

```python
NumberField(
    default=None,           # 默认值
    alias=None,            # 字段别名
    required=False,        # 是否必填 (默认为 False)
    minvalue=None,         # 最小值
    maxvalue=None,         # 最大值
    choices=None,          # 枚举选项
    error_messages=None    # 自定义错误消息
)
```

#### ListField

```python
ListField(
    default=None,           # 默认值
    alias=None,            # 字段别名
    required=False,        # 是否必填 (默认为 False)
    min_length=None,       # 最小长度
    max_length=None,       # 最大长度
    item_type=None,        # 列表项类型
    error_messages=None    # 自定义错误消息
)
```

### 装饰器

#### @dataclass

```python
@dataclass
class MyClass(object):
    field1 = StringField()
    field2 = NumberField()
```

#### @validate

```python
@dataclass
class MyClass(object):
    field1 = StringField()

    @validate("field1")
    def validate_field1(self, value):
        # 自定义验证逻辑
        if not condition:
            raise ValidationError("Custom validation failed")
```

### 错误消息键

#### 通用错误消息键

- `required`: 必填字段为空
- `invalid_type`: 类型不匹配

#### StringField 错误消息键

- `min_length`: 长度小于最小值
- `max_length`: 长度大于最大值
- `regex`: 正则表达式匹配失败
- `choices`: 值不在枚举选项中

#### NumberField 错误消息键

- `minvalue`: 数值小于最小值
- `maxvalue`: 数值大于最大值
- `choices`: 值不在枚举选项中

#### ListField 错误消息键

- `min_length`: 列表长度小于最小值
- `max_length`: 列表长度大于最大值
- `invalid_list_item`: 列表项类型不匹配

## 测试

### 运行测试

```bash
# 运行所有测试
pytest

# 运行特定测试文件
pytest tests/test_fields.py

# 运行带覆盖率的测试
pytest --cov=schemas_dataclass

# 运行特定标记的测试
pytest -m "unit"
pytest -m "integration"
pytest -m "error_messages"
```

### 测试结构

```
tests/
├── conftest.py                    # pytest 配置和 fixtures
├── test_fields.py                 # 字段类型测试
├── test_custom_error_messages.py  # 自定义错误消息测试
├── test_dataclass.py             # dataclass 功能测试
└── test_integration.py           # 集成测试
```

### 测试覆盖

- **25+ 个测试用例**,覆盖所有功能点
- **100% 测试通过率**
- **向后兼容性验证**
- **多语言错误消息测试**
- **复杂场景边界测试**

## 验证特性

### 字符串验证

- 长度验证:`min_length`, `max_length`
- 正则表达式验证:`regex`
- 枚举验证:`choices`
- 自定义错误消息:支持所有验证类型

### 数字验证

- 范围验证:`minvalue`, `maxvalue`
- 枚举验证:`choices`
- 类型验证:自动支持int、float、long(Python 2)
- 自定义错误消息:支持所有验证类型

### 数组验证

- 长度验证:`min_length`, `max_length`
- 项类型验证:`item_type`
- 支持嵌套:字符串、数字、dataclass模型
- 自定义错误消息:支持列表项类型错误

### DataClass字段支持

- 支持dataclass作为字段类型
- 自动实例化和验证
- 重新赋值时重新创建对象
- 支持嵌套的to_dict()转换

### 自定义验证

- 使用`@validate("field_name")`装饰器
- 在基础验证之后执行
- 支持多个自定义验证函数

### 自定义错误消息特性

- **多语言支持**:完全支持中文、英文等多语言错误消息
- **模板格式化**:支持 `{参数名}` 格式的参数替换,如 `{min_length}`, `{maxvalue}` 等
- **完整覆盖**:支持所有验证类型的自定义错误消息
- **向后兼容**:不影响现有代码,可选使用
- **健壮性**:格式化失败时优雅降级,返回原始模板
- **零性能影响**:不使用自定义消息时性能与原版本完全相同

#### 支持的错误消息类型

- **通用**: `required`, `invalid_type`
- **StringField**: `min_length`, `max_length`, `regex`, `choices`
- **NumberField**: `minvalue`, `maxvalue`, `choices`
- **ListField**: `min_length`, `max_length`, `invalid_list_item`

## 兼容性

- **Python 2.7+**: 完全支持
- **Python 3.4+**: 完全支持
- **PyPy**: 支持
- **Jython**: 理论支持(未测试)

## 性能

- **零依赖**: 仅使用 Python 标准库
- **轻量级**: 核心代码不到 1000 行
- **高性能**: 验证速度快,内存占用低
- **可扩展**: 易于添加新的字段类型和验证规则

## 贡献指南

欢迎贡献代码!请遵循以下步骤:

1. Fork 项目
2. 创建功能分支 (`git checkout -b feature/amazing-feature`)
3. 添加测试用例
4. 确保所有测试通过 (`pytest`)
5. 更新相关文档
6. 提交更改 (`git commit -m 'Add amazing feature'`)
7. 推送到分支 (`git push origin feature/amazing-feature`)
8. 创建 Pull Request

### 开发环境设置

```bash
git clone https://github.com/schemas/dataclass.git
cd dataclass
pip install -e .
pip install -r requirements-dev.txt
```

### 代码规范

- 遵循 PEP 8 代码风格
- 添加适当的文档字符串
- 为新功能添加测试用例
- 保持 Python 2/3 兼容性

## 许可证

本项目采用 GNU General Public License v3.0 许可证。详见 [LICENSE](LICENSE) 文件。

## 更新日志

查看 [CHANGELOG.md](CHANGELOG.md) 了解详细的版本更新记录。

---

**注意**: 本库完全兼容 Python 2.7 和 Python 3.x,自定义错误消息功能为可选特性,不影响现有代码的使用。

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "schemas-dataclass",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": "Schemas DataClass Team <bb.qnyd@gmail.com>",
    "keywords": "dataclass, fields, python2, python3, schema, validation",
    "author": null,
    "author_email": "Schemas DataClass Team <bb.qnyd@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/46/87/780af591c6fb0ce9420705ae2e3860833efb4feeecb97533113a03508eb5/schemas_dataclass-0.0.1.tar.gz",
    "platform": null,
    "description": "# Schemas DataClass\n\n[![Python Version](https://img.shields.io/badge/python-2.7%2B%2C%203.4%2B-blue.svg)](https://pypi.org/project/schemas-dataclass/)\n[![License](https://img.shields.io/badge/license-GPLv3-green.svg)](LICENSE)\n[![CI](https://github.com/b40yd/schemas-python/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/b40yd/schemas-python/actions/workflows/ci.yml)\n[![Coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/b40yd/9d999999999999999999999999999999/raw/coverage.json)](#)\n\n\u4e00\u4e2a\u4e13\u4e3a Python 2/3 \u517c\u5bb9\u8bbe\u8ba1\u7684 DataClass \u5e93\uff0c\u652f\u6301\u5b8c\u6574\u7684\u6570\u636e\u6821\u9a8c\u529f\u80fd\u3001\u88c5\u9970\u5668\u8bed\u6cd5\u548c\u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\u3002\n\n## \ud83d\ude80 \u5feb\u901f\u5f00\u59cb\n\n### \u5b89\u88c5\n\n```bash\npip install schemas-dataclass\n```\n\n### \u57fa\u7840\u4f7f\u7528\n\n```python\nfrom schemas_dataclass import StringField, NumberField, dataclass, ValidationError\n\n@dataclass\nclass User(object):\n    name = StringField(min_length=2, max_length=50)\n    age = NumberField(minvalue=0, maxvalue=120)\n    email = StringField(\n        regex=r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$'\n    )\n\n# \u521b\u5efa\u7528\u6237\nuser = User(name=\"Alice\", age=25, email=\"alice@example.com\")\nprint(user.to_dict())  # {'name': 'Alice', 'age': 25, 'email': 'alice@example.com'}\n```\n\n### \u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\n\n```python\n@dataclass\nclass User(object):\n    name = StringField(\n        min_length=2,\n        error_messages={\n            'required': '\u59d3\u540d\u662f\u5fc5\u586b\u9879',\n            'min_length': '\u59d3\u540d\u81f3\u5c11\u9700\u8981 {min_length} \u4e2a\u5b57\u7b26'\n        }\n    )\n\ntry:\n    user = User(name=\"A\")  # \u592a\u77ed\nexcept ValidationError as e:\n    print(e.message)  # \u8f93\u51fa: \u59d3\u540d\u81f3\u5c11\u9700\u8981 2 \u4e2a\u5b57\u7b26\n```\n\n## \ud83d\udcda \u6587\u6863\u7d22\u5f15\n\n- **[\u5b89\u88c5\u548c\u4f7f\u7528](#\u5b89\u88c5\u548c\u4f7f\u7528)** - \u5feb\u901f\u4e0a\u624b\u6307\u5357\n- **[\u5b8c\u6574\u793a\u4f8b](#\u5b8c\u6574\u793a\u4f8b)** - \u4e30\u5bcc\u7684\u4f7f\u7528\u793a\u4f8b\n- **[API \u53c2\u8003](#api-\u53c2\u8003)** - \u8be6\u7ec6\u7684 API \u6587\u6863\n- **[\u6d4b\u8bd5](#\u6d4b\u8bd5)** - \u5982\u4f55\u8fd0\u884c\u6d4b\u8bd5\n- **[\u9879\u76ee\u7ed3\u6784\u8bf4\u660e](PROJECT_STRUCTURE.md)** - \u9879\u76ee\u6587\u4ef6\u7ed3\u6784\u548c\u5f00\u53d1\u6307\u5357\n- **[\u66f4\u65b0\u65e5\u5fd7](CHANGELOG.md)** - \u7248\u672c\u66f4\u65b0\u8bb0\u5f55\u548c\u529f\u80fd\u53d8\u66f4\n- **[\u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\u8be6\u7ec6\u6587\u6863](CUSTOM_ERROR_MESSAGES.md)** - \u5b8c\u6574\u7684\u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\u4f7f\u7528\u6307\u5357\n- **[\u8d21\u732e\u6307\u5357](#\u8d21\u732e\u6307\u5357)** - \u5982\u4f55\u53c2\u4e0e\u9879\u76ee\u5f00\u53d1\n\n## \u5b89\u88c5\u548c\u4f7f\u7528\n\n### \u5b89\u88c5\u65b9\u5f0f\n\n#### \u4ece PyPI \u5b89\u88c5\uff08\u63a8\u8350\uff09\n\n```bash\npip install schemas-dataclass\n```\n\n#### \u4ece\u6e90\u7801\u5b89\u88c5\n\n```bash\ngit clone https://github.com/schemas/dataclass.git\ncd dataclass\npython setup.py install\n```\n\n#### \u5f00\u53d1\u73af\u5883\u5b89\u88c5\n\n```bash\ngit clone https://github.com/schemas/dataclass.git\ncd dataclass\npip install -e .\npip install -r requirements-dev.txt\n```\n\n### \u57fa\u7840\u4f7f\u7528\u6307\u5357\n\n```python\nfrom schemas_dataclass import StringField, NumberField, ListField, dataclass\n\n@dataclass\nclass User(object):\n    name = StringField(min_length=2, max_length=50)\n    age = NumberField(minvalue=0, maxvalue=120)\n    email = StringField(\n        regex=r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$'\n    )\n    tags = ListField(item_type=str, required=False)\n\n# \u521b\u5efa\u548c\u4f7f\u7528\nuser = User(\n    name=\"Alice\",\n    age=25,\n    email=\"alice@example.com\",\n    tags=[\"developer\", \"python\"]\n)\n\nprint(user.name)        # Alice\nprint(user['age'])      # 25\nprint(user.get('email')) # alice@example.com\nprint(user.to_dict())   # \u8f6c\u6362\u4e3a\u5b57\u5178\n```\n\n## \u6838\u5fc3\u7279\u6027\n\n### \ud83d\udd27 \u5b57\u6bb5\u7c7b\u578b\u652f\u6301\n\n- **StringField**: \u5b57\u7b26\u4e32\u5b57\u6bb5\n  - \u957f\u5ea6\u9a8c\u8bc1 (`min_length`, `max_length`)\n  - \u6b63\u5219\u8868\u8fbe\u5f0f\u9a8c\u8bc1 (`regex`)\n  - \u679a\u4e3e\u9a8c\u8bc1 (`choices`)\n  - \u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\u652f\u6301\n  \n- **NumberField**: \u6570\u5b57\u5b57\u6bb5\uff08\u652f\u6301int\u3001float\u3001long\uff09\n  - \u8303\u56f4\u9a8c\u8bc1 (`minvalue`, `maxvalue`)\n  - \u679a\u4e3e\u9a8c\u8bc1 (`choices`)\n  - \u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\u652f\u6301\n  \n- **ListField**: \u6570\u7ec4\u5b57\u6bb5\n  - \u957f\u5ea6\u9a8c\u8bc1 (`min_length`, `max_length`)\n  - \u652f\u6301\u5d4c\u5957\u7c7b\u578b\u9a8c\u8bc1 (`item_type`)\n  - \u652f\u6301\u5b57\u7b26\u4e32\u3001\u6570\u5b57\u3001dataclass\u6a21\u578b\u5d4c\u5957\n  - \u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\u652f\u6301\n\n### \ud83c\udf0d \u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\n\n- **\u591a\u8bed\u8a00\u652f\u6301**: \u652f\u6301\u4e2d\u6587\u3001\u82f1\u6587\u7b49\u591a\u8bed\u8a00\u9519\u8bef\u6d88\u606f\n- **\u6a21\u677f\u683c\u5f0f\u5316**: \u652f\u6301 `{\u53c2\u6570\u540d}` \u683c\u5f0f\u7684\u53c2\u6570\u66ff\u6362\n- **\u5b8c\u6574\u8986\u76d6**: \u652f\u6301\u6240\u6709\u9a8c\u8bc1\u7c7b\u578b\u7684\u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\n- **\u5411\u540e\u517c\u5bb9**: \u4e0d\u5f71\u54cd\u73b0\u6709\u4ee3\u7801\uff0c\u53ef\u9009\u4f7f\u7528\n\n```python\n# \u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\u793a\u4f8b\n@dataclass\nclass User(object):\n    name = StringField(\n        min_length=3,\n        max_length=20,\n        error_messages={\n            'required': '\u7528\u6237\u540d\u662f\u5fc5\u586b\u9879',\n            'min_length': '\u7528\u6237\u540d\u81f3\u5c11\u9700\u8981 {min_length} \u4e2a\u5b57\u7b26',\n            'max_length': '\u7528\u6237\u540d\u4e0d\u80fd\u8d85\u8fc7 {max_length} \u4e2a\u5b57\u7b26'\n        }\n    )\n```\n\n### \ud83c\udfaf \u88c5\u9970\u5668\u8bed\u6cd5\n\n```python\n@dataclass\nclass User(object):\n    name = StringField(min_length=1, max_length=100)\n    age = NumberField(minvalue=0, maxvalue=150)\n```\n\n### \ud83d\udd0d \u81ea\u5b9a\u4e49\u9a8c\u8bc1\u88c5\u9970\u5668\n\n```python\n@dataclass\nclass Product(object):\n    name = StringField()\n    price = NumberField()\n    \n    @validate(\"name\")\n    def validate_name_custom(self, name):\n        if not name.isalnum():\n            raise ValidationError(\"Name must be alphanumeric\")\n    \n    @validate(\"price\")\n    def validate_price_custom(self, price):\n        if price <= 0:\n            raise ValidationError(\"Price must be positive\")\n```\n\n### \ud83d\udd27 \u81ea\u5b9a\u4e49get\u65b9\u6cd5\n\n```python\n@dataclass\nclass BlogPost(object):\n    title = StringField()\n    status = StringField(default='draft')\n    \n    def get_title(self):\n        \"\"\"\u81ea\u5b9a\u4e49\u83b7\u53d6\u6807\u9898\u7684\u65b9\u6cd5\"\"\"\n        title = self.__dict__.get('title', '')\n        status = self.__dict__.get('status', 'draft')\n        return \"[{0}] {1}\".format(status.upper(), title)\n```\n\n## \u5b8c\u6574\u793a\u4f8b\n\n### \ud83d\udcc1 \u793a\u4f8b\u6587\u4ef6\n\n\u9879\u76ee\u63d0\u4f9b\u4e86\u4e30\u5bcc\u7684\u793a\u4f8b\u6587\u4ef6\uff0c\u4f4d\u4e8e `examples/` \u76ee\u5f55\uff1a\n\n- **[\u57fa\u7840\u4f7f\u7528\u793a\u4f8b](examples/basic_usage.py)** - \u5b57\u6bb5\u7c7b\u578b\u3001dataclass \u57fa\u7840\u529f\u80fd\n- **[\u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\u793a\u4f8b](examples/custom_error_messages.py)** - \u591a\u8bed\u8a00\u9519\u8bef\u6d88\u606f\u3001\u6a21\u677f\u683c\u5f0f\u5316\n- **[\u9ad8\u7ea7\u529f\u80fd\u793a\u4f8b](examples/advanced_features.py)** - \u81ea\u5b9a\u4e49\u9a8c\u8bc1\u3001\u5d4c\u5957 dataclass\u3001\u6761\u4ef6\u9a8c\u8bc1\n- **[\u5b9e\u9645\u5e94\u7528\u793a\u4f8b](examples/real_world_examples.py)** - \u7528\u6237\u7ba1\u7406\u3001\u7535\u5546\u4ea7\u54c1\u3001\u535a\u5ba2\u7cfb\u7edf\n\n### \ud83d\ude80 \u8fd0\u884c\u793a\u4f8b\n\n```bash\n# \u57fa\u7840\u4f7f\u7528\u793a\u4f8b\npython examples/basic_usage.py\n\n# \u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\u793a\u4f8b\npython examples/custom_error_messages.py\n\n# \u9ad8\u7ea7\u529f\u80fd\u793a\u4f8b\npython examples/advanced_features.py\n\n# \u5b9e\u9645\u5e94\u7528\u793a\u4f8b\npython examples/real_world_examples.py\n```\n\n### \ud83d\udca1 \u5feb\u901f\u793a\u4f8b\n\n#### \u7528\u6237\u7ba1\u7406\u7cfb\u7edf\n\n```python\nfrom schemas_dataclass import StringField, NumberField, ListField, dataclass, validate\n\n@dataclass\nclass User(object):\n    username = StringField(\n        min_length=3,\n        max_length=20,\n        regex=r'^[a-zA-Z][a-zA-Z0-9_]*$',\n        error_messages={\n            'required': '\u7528\u6237\u540d\u662f\u5fc5\u586b\u9879',\n            'min_length': '\u7528\u6237\u540d\u81f3\u5c11\u9700\u8981 {min_length} \u4e2a\u5b57\u7b26',\n            'regex': '\u7528\u6237\u540d\u5fc5\u987b\u4ee5\u5b57\u6bcd\u5f00\u5934\uff0c\u53ea\u80fd\u5305\u542b\u5b57\u6bcd\u3001\u6570\u5b57\u548c\u4e0b\u5212\u7ebf'\n        }\n    )\n    \n    email = StringField(\n        regex=r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$',\n        error_messages={\n            'required': '\u90ae\u7bb1\u5730\u5740\u662f\u5fc5\u586b\u9879',\n            'regex': '\u8bf7\u8f93\u5165\u6709\u6548\u7684\u90ae\u7bb1\u5730\u5740'\n        }\n    )\n    \n    age = NumberField(\n        minvalue=13,\n        maxvalue=120,\n        error_messages={\n            'minvalue': '\u5e74\u9f84\u4e0d\u80fd\u5c0f\u4e8e {minvalue} \u5c81',\n            'maxvalue': '\u5e74\u9f84\u4e0d\u80fd\u5927\u4e8e {maxvalue} \u5c81'\n        }\n    )\n    \n    tags = ListField(\n        item_type=str,\n        required=False,\n        max_length=10,\n        error_messages={\n            'max_length': '\u6807\u7b7e\u6570\u91cf\u4e0d\u80fd\u8d85\u8fc7 {max_length} \u4e2a'\n        }\n    )\n    \n    @validate(\"username\")\n    def validate_username_not_reserved(self, username):\n        \"\"\"\u68c0\u67e5\u7528\u6237\u540d\u662f\u5426\u4e3a\u4fdd\u7559\u8bcd\"\"\"\n        reserved = ['admin', 'root', 'system']\n        if username.lower() in reserved:\n            raise ValidationError(f\"\u7528\u6237\u540d '{username}' \u662f\u7cfb\u7edf\u4fdd\u7559\u8bcd\")\n\n# \u4f7f\u7528\u793a\u4f8b\nuser = User(\n    username=\"alice_dev\",\n    email=\"alice@example.com\",\n    age=28,\n    tags=[\"developer\", \"python\"]\n)\n\nprint(\"\u7528\u6237: {}\".format(user.username))\nprint(\"\u90ae\u7bb1: {}\".format(user.email))\nprint(\"\u5e74\u9f84: {}\".format(user.age))\nprint(\"\u6807\u7b7e: {}\".format(user.tags))\n```\n\n## API \u53c2\u8003\n\n> **\u91cd\u8981\u53d8\u66f4\u8bf4\u660e**: \u4ece\u7248\u672c 2.0 \u5f00\u59cb\uff0c\u6240\u6709\u5b57\u6bb5\u9ed8\u8ba4\u4e3a\u53ef\u9009 (`required=False`)\u3002\u5982\u9700\u5fc5\u586b\u5b57\u6bb5\uff0c\u8bf7\u663e\u5f0f\u8bbe\u7f6e `required=True`\u3002\n\n### \u5b57\u6bb5\u7c7b\u578b\n\n#### StringField\n\n```python\nStringField(\n    default=None,           # \u9ed8\u8ba4\u503c\n    alias=None,            # \u5b57\u6bb5\u522b\u540d\n    required=False,        # \u662f\u5426\u5fc5\u586b (\u9ed8\u8ba4\u4e3a False)\n    min_length=None,       # \u6700\u5c0f\u957f\u5ea6\n    max_length=None,       # \u6700\u5927\u957f\u5ea6\n    regex=None,            # \u6b63\u5219\u8868\u8fbe\u5f0f\n    choices=None,          # \u679a\u4e3e\u9009\u9879\n    error_messages=None    # \u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\n)\n```\n\n#### NumberField\n\n```python\nNumberField(\n    default=None,           # \u9ed8\u8ba4\u503c\n    alias=None,            # \u5b57\u6bb5\u522b\u540d\n    required=False,        # \u662f\u5426\u5fc5\u586b (\u9ed8\u8ba4\u4e3a False)\n    minvalue=None,         # \u6700\u5c0f\u503c\n    maxvalue=None,         # \u6700\u5927\u503c\n    choices=None,          # \u679a\u4e3e\u9009\u9879\n    error_messages=None    # \u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\n)\n```\n\n#### ListField\n\n```python\nListField(\n    default=None,           # \u9ed8\u8ba4\u503c\n    alias=None,            # \u5b57\u6bb5\u522b\u540d\n    required=False,        # \u662f\u5426\u5fc5\u586b (\u9ed8\u8ba4\u4e3a False)\n    min_length=None,       # \u6700\u5c0f\u957f\u5ea6\n    max_length=None,       # \u6700\u5927\u957f\u5ea6\n    item_type=None,        # \u5217\u8868\u9879\u7c7b\u578b\n    error_messages=None    # \u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\n)\n```\n\n### \u88c5\u9970\u5668\n\n#### @dataclass\n\n```python\n@dataclass\nclass MyClass(object):\n    field1 = StringField()\n    field2 = NumberField()\n```\n\n#### @validate\n\n```python\n@dataclass\nclass MyClass(object):\n    field1 = StringField()\n\n    @validate(\"field1\")\n    def validate_field1(self, value):\n        # \u81ea\u5b9a\u4e49\u9a8c\u8bc1\u903b\u8f91\n        if not condition:\n            raise ValidationError(\"Custom validation failed\")\n```\n\n### \u9519\u8bef\u6d88\u606f\u952e\n\n#### \u901a\u7528\u9519\u8bef\u6d88\u606f\u952e\n\n- `required`: \u5fc5\u586b\u5b57\u6bb5\u4e3a\u7a7a\n- `invalid_type`: \u7c7b\u578b\u4e0d\u5339\u914d\n\n#### StringField \u9519\u8bef\u6d88\u606f\u952e\n\n- `min_length`: \u957f\u5ea6\u5c0f\u4e8e\u6700\u5c0f\u503c\n- `max_length`: \u957f\u5ea6\u5927\u4e8e\u6700\u5927\u503c\n- `regex`: \u6b63\u5219\u8868\u8fbe\u5f0f\u5339\u914d\u5931\u8d25\n- `choices`: \u503c\u4e0d\u5728\u679a\u4e3e\u9009\u9879\u4e2d\n\n#### NumberField \u9519\u8bef\u6d88\u606f\u952e\n\n- `minvalue`: \u6570\u503c\u5c0f\u4e8e\u6700\u5c0f\u503c\n- `maxvalue`: \u6570\u503c\u5927\u4e8e\u6700\u5927\u503c\n- `choices`: \u503c\u4e0d\u5728\u679a\u4e3e\u9009\u9879\u4e2d\n\n#### ListField \u9519\u8bef\u6d88\u606f\u952e\n\n- `min_length`: \u5217\u8868\u957f\u5ea6\u5c0f\u4e8e\u6700\u5c0f\u503c\n- `max_length`: \u5217\u8868\u957f\u5ea6\u5927\u4e8e\u6700\u5927\u503c\n- `invalid_list_item`: \u5217\u8868\u9879\u7c7b\u578b\u4e0d\u5339\u914d\n\n## \u6d4b\u8bd5\n\n### \u8fd0\u884c\u6d4b\u8bd5\n\n```bash\n# \u8fd0\u884c\u6240\u6709\u6d4b\u8bd5\npytest\n\n# \u8fd0\u884c\u7279\u5b9a\u6d4b\u8bd5\u6587\u4ef6\npytest tests/test_fields.py\n\n# \u8fd0\u884c\u5e26\u8986\u76d6\u7387\u7684\u6d4b\u8bd5\npytest --cov=schemas_dataclass\n\n# \u8fd0\u884c\u7279\u5b9a\u6807\u8bb0\u7684\u6d4b\u8bd5\npytest -m \"unit\"\npytest -m \"integration\"\npytest -m \"error_messages\"\n```\n\n### \u6d4b\u8bd5\u7ed3\u6784\n\n```\ntests/\n\u251c\u2500\u2500 conftest.py                    # pytest \u914d\u7f6e\u548c fixtures\n\u251c\u2500\u2500 test_fields.py                 # \u5b57\u6bb5\u7c7b\u578b\u6d4b\u8bd5\n\u251c\u2500\u2500 test_custom_error_messages.py  # \u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\u6d4b\u8bd5\n\u251c\u2500\u2500 test_dataclass.py             # dataclass \u529f\u80fd\u6d4b\u8bd5\n\u2514\u2500\u2500 test_integration.py           # \u96c6\u6210\u6d4b\u8bd5\n```\n\n### \u6d4b\u8bd5\u8986\u76d6\n\n- **25+ \u4e2a\u6d4b\u8bd5\u7528\u4f8b**\uff0c\u8986\u76d6\u6240\u6709\u529f\u80fd\u70b9\n- **100% \u6d4b\u8bd5\u901a\u8fc7\u7387**\n- **\u5411\u540e\u517c\u5bb9\u6027\u9a8c\u8bc1**\n- **\u591a\u8bed\u8a00\u9519\u8bef\u6d88\u606f\u6d4b\u8bd5**\n- **\u590d\u6742\u573a\u666f\u8fb9\u754c\u6d4b\u8bd5**\n\n## \u9a8c\u8bc1\u7279\u6027\n\n### \u5b57\u7b26\u4e32\u9a8c\u8bc1\n\n- \u957f\u5ea6\u9a8c\u8bc1\uff1a`min_length`, `max_length`\n- \u6b63\u5219\u8868\u8fbe\u5f0f\u9a8c\u8bc1\uff1a`regex`\n- \u679a\u4e3e\u9a8c\u8bc1\uff1a`choices`\n- \u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\uff1a\u652f\u6301\u6240\u6709\u9a8c\u8bc1\u7c7b\u578b\n\n### \u6570\u5b57\u9a8c\u8bc1\n\n- \u8303\u56f4\u9a8c\u8bc1\uff1a`minvalue`, `maxvalue`\n- \u679a\u4e3e\u9a8c\u8bc1\uff1a`choices`\n- \u7c7b\u578b\u9a8c\u8bc1\uff1a\u81ea\u52a8\u652f\u6301int\u3001float\u3001long\uff08Python 2\uff09\n- \u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\uff1a\u652f\u6301\u6240\u6709\u9a8c\u8bc1\u7c7b\u578b\n\n### \u6570\u7ec4\u9a8c\u8bc1\n\n- \u957f\u5ea6\u9a8c\u8bc1\uff1a`min_length`, `max_length`\n- \u9879\u7c7b\u578b\u9a8c\u8bc1\uff1a`item_type`\n- \u652f\u6301\u5d4c\u5957\uff1a\u5b57\u7b26\u4e32\u3001\u6570\u5b57\u3001dataclass\u6a21\u578b\n- \u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\uff1a\u652f\u6301\u5217\u8868\u9879\u7c7b\u578b\u9519\u8bef\n\n### DataClass\u5b57\u6bb5\u652f\u6301\n\n- \u652f\u6301dataclass\u4f5c\u4e3a\u5b57\u6bb5\u7c7b\u578b\n- \u81ea\u52a8\u5b9e\u4f8b\u5316\u548c\u9a8c\u8bc1\n- \u91cd\u65b0\u8d4b\u503c\u65f6\u91cd\u65b0\u521b\u5efa\u5bf9\u8c61\n- \u652f\u6301\u5d4c\u5957\u7684to_dict()\u8f6c\u6362\n\n### \u81ea\u5b9a\u4e49\u9a8c\u8bc1\n\n- \u4f7f\u7528`@validate(\"field_name\")`\u88c5\u9970\u5668\n- \u5728\u57fa\u7840\u9a8c\u8bc1\u4e4b\u540e\u6267\u884c\n- \u652f\u6301\u591a\u4e2a\u81ea\u5b9a\u4e49\u9a8c\u8bc1\u51fd\u6570\n\n### \u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\u7279\u6027\n\n- **\u591a\u8bed\u8a00\u652f\u6301**\uff1a\u5b8c\u5168\u652f\u6301\u4e2d\u6587\u3001\u82f1\u6587\u7b49\u591a\u8bed\u8a00\u9519\u8bef\u6d88\u606f\n- **\u6a21\u677f\u683c\u5f0f\u5316**\uff1a\u652f\u6301 `{\u53c2\u6570\u540d}` \u683c\u5f0f\u7684\u53c2\u6570\u66ff\u6362\uff0c\u5982 `{min_length}`, `{maxvalue}` \u7b49\n- **\u5b8c\u6574\u8986\u76d6**\uff1a\u652f\u6301\u6240\u6709\u9a8c\u8bc1\u7c7b\u578b\u7684\u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\n- **\u5411\u540e\u517c\u5bb9**\uff1a\u4e0d\u5f71\u54cd\u73b0\u6709\u4ee3\u7801\uff0c\u53ef\u9009\u4f7f\u7528\n- **\u5065\u58ee\u6027**\uff1a\u683c\u5f0f\u5316\u5931\u8d25\u65f6\u4f18\u96c5\u964d\u7ea7\uff0c\u8fd4\u56de\u539f\u59cb\u6a21\u677f\n- **\u96f6\u6027\u80fd\u5f71\u54cd**\uff1a\u4e0d\u4f7f\u7528\u81ea\u5b9a\u4e49\u6d88\u606f\u65f6\u6027\u80fd\u4e0e\u539f\u7248\u672c\u5b8c\u5168\u76f8\u540c\n\n#### \u652f\u6301\u7684\u9519\u8bef\u6d88\u606f\u7c7b\u578b\n\n- **\u901a\u7528**: `required`, `invalid_type`\n- **StringField**: `min_length`, `max_length`, `regex`, `choices`\n- **NumberField**: `minvalue`, `maxvalue`, `choices`\n- **ListField**: `min_length`, `max_length`, `invalid_list_item`\n\n## \u517c\u5bb9\u6027\n\n- **Python 2.7+**: \u5b8c\u5168\u652f\u6301\n- **Python 3.4+**: \u5b8c\u5168\u652f\u6301\n- **PyPy**: \u652f\u6301\n- **Jython**: \u7406\u8bba\u652f\u6301\uff08\u672a\u6d4b\u8bd5\uff09\n\n## \u6027\u80fd\n\n- **\u96f6\u4f9d\u8d56**: \u4ec5\u4f7f\u7528 Python \u6807\u51c6\u5e93\n- **\u8f7b\u91cf\u7ea7**: \u6838\u5fc3\u4ee3\u7801\u4e0d\u5230 1000 \u884c\n- **\u9ad8\u6027\u80fd**: \u9a8c\u8bc1\u901f\u5ea6\u5feb\uff0c\u5185\u5b58\u5360\u7528\u4f4e\n- **\u53ef\u6269\u5c55**: \u6613\u4e8e\u6dfb\u52a0\u65b0\u7684\u5b57\u6bb5\u7c7b\u578b\u548c\u9a8c\u8bc1\u89c4\u5219\n\n## \u8d21\u732e\u6307\u5357\n\n\u6b22\u8fce\u8d21\u732e\u4ee3\u7801\uff01\u8bf7\u9075\u5faa\u4ee5\u4e0b\u6b65\u9aa4\uff1a\n\n1. Fork \u9879\u76ee\n2. \u521b\u5efa\u529f\u80fd\u5206\u652f (`git checkout -b feature/amazing-feature`)\n3. \u6dfb\u52a0\u6d4b\u8bd5\u7528\u4f8b\n4. \u786e\u4fdd\u6240\u6709\u6d4b\u8bd5\u901a\u8fc7 (`pytest`)\n5. \u66f4\u65b0\u76f8\u5173\u6587\u6863\n6. \u63d0\u4ea4\u66f4\u6539 (`git commit -m 'Add amazing feature'`)\n7. \u63a8\u9001\u5230\u5206\u652f (`git push origin feature/amazing-feature`)\n8. \u521b\u5efa Pull Request\n\n### \u5f00\u53d1\u73af\u5883\u8bbe\u7f6e\n\n```bash\ngit clone https://github.com/schemas/dataclass.git\ncd dataclass\npip install -e .\npip install -r requirements-dev.txt\n```\n\n### \u4ee3\u7801\u89c4\u8303\n\n- \u9075\u5faa PEP 8 \u4ee3\u7801\u98ce\u683c\n- \u6dfb\u52a0\u9002\u5f53\u7684\u6587\u6863\u5b57\u7b26\u4e32\n- \u4e3a\u65b0\u529f\u80fd\u6dfb\u52a0\u6d4b\u8bd5\u7528\u4f8b\n- \u4fdd\u6301 Python 2/3 \u517c\u5bb9\u6027\n\n## \u8bb8\u53ef\u8bc1\n\n\u672c\u9879\u76ee\u91c7\u7528 GNU General Public License v3.0 \u8bb8\u53ef\u8bc1\u3002\u8be6\u89c1 [LICENSE](LICENSE) \u6587\u4ef6\u3002\n\n## \u66f4\u65b0\u65e5\u5fd7\n\n\u67e5\u770b [CHANGELOG.md](CHANGELOG.md) \u4e86\u89e3\u8be6\u7ec6\u7684\u7248\u672c\u66f4\u65b0\u8bb0\u5f55\u3002\n\n---\n\n**\u6ce8\u610f**: \u672c\u5e93\u5b8c\u5168\u517c\u5bb9 Python 2.7 \u548c Python 3.x\uff0c\u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\u529f\u80fd\u4e3a\u53ef\u9009\u7279\u6027\uff0c\u4e0d\u5f71\u54cd\u73b0\u6709\u4ee3\u7801\u7684\u4f7f\u7528\u3002\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "Python 2/3 \u517c\u5bb9\u7684 DataClass \u5e93\uff0c\u652f\u6301\u5b8c\u6574\u7684\u6570\u636e\u6821\u9a8c\u529f\u80fd\u548c\u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f",
    "version": "0.0.1",
    "project_urls": {
        "Bug Reports": "https://github.com/b40yd/schemas-python/issues",
        "Changelog": "https://github.com/b40yd/schemas-python/blob/main/CHANGELOG.md",
        "Documentation": "https://github.com/b40yd/schemas-python/blob/main/README.md",
        "Homepage": "https://github.com/b40yd/schemas-python",
        "Repository": "https://github.com/b40yd/schemas-python.git"
    },
    "split_keywords": [
        "dataclass",
        " fields",
        " python2",
        " python3",
        " schema",
        " validation"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "9f13485c06d4b1a9a7951077a3727c3b91fa338e21f00f6d8343dfb23710ba26",
                "md5": "a5ebb4b9e9e7e2b11b37a200f2f09de2",
                "sha256": "8c193bde414dc9fd7355bb11cc1054419fab97f2ebc2920cf8e567aaeff5b30a"
            },
            "downloads": -1,
            "filename": "schemas_dataclass-0.0.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "a5ebb4b9e9e7e2b11b37a200f2f09de2",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 26734,
            "upload_time": "2025-08-23T06:20:18",
            "upload_time_iso_8601": "2025-08-23T06:20:18.542174Z",
            "url": "https://files.pythonhosted.org/packages/9f/13/485c06d4b1a9a7951077a3727c3b91fa338e21f00f6d8343dfb23710ba26/schemas_dataclass-0.0.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "4687780af591c6fb0ce9420705ae2e3860833efb4feeecb97533113a03508eb5",
                "md5": "2bc047daeb5ad11082b85ffba10bdba4",
                "sha256": "61bb53d899f721aad8e62bb03c4b9c02003f879cc1d45b4afaf8b53f97c8f544"
            },
            "downloads": -1,
            "filename": "schemas_dataclass-0.0.1.tar.gz",
            "has_sig": false,
            "md5_digest": "2bc047daeb5ad11082b85ffba10bdba4",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 54710,
            "upload_time": "2025-08-23T06:20:20",
            "upload_time_iso_8601": "2025-08-23T06:20:20.584561Z",
            "url": "https://files.pythonhosted.org/packages/46/87/780af591c6fb0ce9420705ae2e3860833efb4feeecb97533113a03508eb5/schemas_dataclass-0.0.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-08-23 06:20:20",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "b40yd",
    "github_project": "schemas-python",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "requirements": [],
    "tox": true,
    "lcname": "schemas-dataclass"
}
        
Elapsed time: 0.85525s