diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 909616f..0b0ed18 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -22,7 +22,7 @@ jobs: - name: Run flake8 run: flake8 py_models_parser/ tests/ - tox: + tests: runs-on: ubuntu-latest needs: [flake8] strategy: @@ -37,6 +37,9 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install tox tox-gh-actions - - name: Run tox - run: tox -e py$(echo ${{ matrix.python-version }} | tr -d '.') + pip install poetry + poetry install + env: + POETRY_VIRTUALENVS_CREATE: false + - name: Test with pytest + run: pytest tests/ -vv diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..bdc8416 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,100 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.0.0] - 2025-01-18 + +### Added + +**Pydantic 2.x Support** +- Union types with `|` operator (PEP 604): `int | None`, `str | None` +- `Field()` with validation constraints: `max_length`, `min_length`, `gt`, `ge`, `lt`, `le` +- `model_config` with `ConfigDict` handling +- Nested generic types: `list[dict[str, str]]`, `dict[str, list[int]]` + +**OpenAPI 3.0/Swagger Support** +- New `parse_openapi()` function to parse OpenAPI specs from string +- New `parse_openapi_file()` function to parse from YAML or JSON files +- Supports OpenAPI 3.0 `components/schemas` and Swagger 2.0 `definitions` +- Type mapping from OpenAPI types to Python types +- Support for `$ref`, `allOf`, `oneOf`, `anyOf` compositions + +**Python Version Support** +- Added support for Python 3.12 +- Added support for Python 3.13 + +**Documentation** +- Added ARCHITECTURE.md with project documentation + +### Changed + +**Breaking Changes** +- Dropped support for Python 3.7 +- Dropped support for Python 3.8 +- Minimum required Python version is now 3.9 + +**Dependencies** +- Added `pyyaml` dependency for OpenAPI parsing + +### Testing +- Added 8 new tests for Pydantic 2.x features +- Added 15 tests for OpenAPI parsing +- Updated CI to test Python 3.9, 3.10, 3.11, 3.12, 3.13 + +## [0.7.0] + +### Changed +- Updated to latest version of parsimonious +- Library now works with Python 3.11 + +## [0.6.0] + +### Added +- Support for [Encode ORM](https://github.com/encode/orm) models +- Support for [Piccolo ORM](https://piccolo-orm.readthedocs.io/en/latest/piccolo/schema/defining.html) models + +## [0.5.1] + +### Fixed +- Multiple parent names in "parents" output were sometimes joined in one string + +## [0.5.0] + +### Added +- Base support for PyDAL tables definitions +- Support for Python list syntax like `[]` + +## [0.4.0] + +### Fixed +- Return tuples (multiple values) are now parsed correctly +- Symbols like `*&^%$#!±~`§<>` no longer cause errors +- Classes without any args no longer cause errors + +## [0.3.0] + +### Added +- CLI command `pmp` with `-d`, `--dump` arguments +- Support for simple Django ORM models +- Base support for pure Python classes + +## [0.2.0] + +### Added +- Support for Dataclasses +- `parse_from_file()` method +- Correct handling of types with comma inside: `Union[dict, list]`, `Union[dict, list, tuple, anything]` + +## [0.1.1] + +### Added +- Base parser logic for: + - Pydantic models + - Python Enums + - SQLAlchemy Models + - GinoORM models + - TortoiseORM models +- Initial test suite diff --git a/CHANGELOG.txt b/CHANGELOG.txt deleted file mode 100644 index 49f4263..0000000 --- a/CHANGELOG.txt +++ /dev/null @@ -1,49 +0,0 @@ -**v1.0.0** -Breaking Changes: -1. Dropped support for Python 3.7 and 3.8 -2. Minimum required Python version is now 3.9 - -New Features: -1. Added support for Python 3.12 and 3.13 -2. Added OpenAPI 3.0/Swagger specification parser (parse_openapi, parse_openapi_file) -3. Added tox for multi-version testing -4. Added ARCHITECTURE.md with project documentation -5. Added pyyaml dependency for OpenAPI parsing - -**v0.7.0** -Updates: -1. Added support for latest version of parsimonious. -Library now works with Python 3.11. - - -**v0.6.0** -Features: -1. Added support for Encode ORM models https://github.com/encode/orm -2. Added support for Piccolo ORM models https://piccolo-orm.readthedocs.io/en/latest/piccolo/schema/defining.html - - -**v0.5.1** -Fixes: -1. Sometimes multiple parents names in "parents" output was joined in one string - fixed. - -**v0.5.0** -1. Added base support for Pydal tables definitions -2. Added support for python list syntax like [] - -**v0.4.0** -1. return tuples (multiple values) is parsed correctly now -2. symbols like `*&^%$#!±~`§<>` now does not cause any errors -3. classes without any args does not cause an error anymore - -**v0.3.0** -1. Added cli - `pmp` command with args -d, --dump -2. Added support for simple Django ORM models -3. Added base support for pure Python Classes - -**v0.2.0** -1. Added support for Dataclasses -2. Added parse_from_file method -3. Added correct work with types with comma inside, like: Union[dict, list] or Union[dict, list, tuple, anything] - -**v0.1.1** -1. Added base parser logic & tests for Pydantic, Enums, SQLAlchemy Models, GinoORM models, TortoiseORM models diff --git a/README.md b/README.md index 702c118..bfb44fa 100644 --- a/README.md +++ b/README.md @@ -1,229 +1,170 @@ -## Py-Models-Parser +# Py-Models-Parser -![badge1](https://img.shields.io/pypi/v/py-models-parser) ![badge2](https://img.shields.io/pypi/l/py-models-parser) ![badge3](https://img.shields.io/pypi/pyversions/py-models-parser) ![workflow](https://github.com/xnuinside/py-models-parser/actions/workflows/main.yml/badge.svg) +[![PyPI version](https://img.shields.io/pypi/v/py-models-parser)](https://pypi.org/project/py-models-parser/) +[![License](https://img.shields.io/pypi/l/py-models-parser)](https://github.com/xnuinside/py-models-parser/blob/main/LICENSE) +[![Python versions](https://img.shields.io/pypi/pyversions/py-models-parser)](https://pypi.org/project/py-models-parser/) +[![Tests](https://github.com/xnuinside/py-models-parser/actions/workflows/main.yml/badge.svg)](https://github.com/xnuinside/py-models-parser/actions/workflows/main.yml) -It's as second Parser that done by me, first is a https://github.com/xnuinside/simple-ddl-parser for SQL DDL with different dialects. +It's a second parser that I've created. The first is [simple-ddl-parser](https://github.com/xnuinside/simple-ddl-parser) for SQL DDL with different dialects. Py-Models-Parser can parse & extract information from models & table definitions: -- Sqlalchemy ORM (https://docs.sqlalchemy.org/en/14/orm/), -- Gino ORM (https://python-gino.org/), -- Tortoise ORM (https://tortoise-orm.readthedocs.io/en/latest/), -- Encode ORM (https://github.com/encode/orm) -- Django ORM Model (https://docs.djangoproject.com/en/3.2/topics/db/queries/), -- Pydantic (https://pydantic-docs.helpmanual.io/), -- Python Enum (https://docs.python.org/3/library/enum.html), -- Pony ORM (https://ponyorm.org/), -- Piccolo ORM models (https://piccolo-orm.readthedocs.io/en/latest/piccolo/schema/defining.html), -- Pydal Tables definitions (http://www.web2py.com/books/default/chapter/29/06/the-database-abstraction-layer#The-DAL-A-quick-tour), -- Python Dataclasses (https://docs.python.org/3/library/dataclasses.html), -- pure Python Classes (https://docs.python.org/3/tutorial/classes.html#class-objects) +- [SQLAlchemy ORM](https://docs.sqlalchemy.org/en/14/orm/) +- [Gino ORM](https://python-gino.org/) +- [Tortoise ORM](https://tortoise-orm.readthedocs.io/en/latest/) +- [Encode ORM](https://github.com/encode/orm) +- [Django ORM](https://docs.djangoproject.com/en/3.2/topics/db/queries/) +- [Pydantic v1 and v2](https://docs.pydantic.dev/) +- [Python Enum](https://docs.python.org/3/library/enum.html) +- [Pony ORM](https://ponyorm.org/) +- [Piccolo ORM](https://piccolo-orm.readthedocs.io/en/latest/piccolo/schema/defining.html) +- [PyDAL Tables](http://www.web2py.com/books/default/chapter/29/06/the-database-abstraction-layer#The-DAL-A-quick-tour) +- [Python Dataclasses](https://docs.python.org/3/library/dataclasses.html) +- [Pure Python Classes](https://docs.python.org/3/tutorial/classes.html#class-objects) +- [OpenAPI 3.0/Swagger](https://swagger.io/specification/) -Number of supported models will be increased, check 'TODO' section, if you want to have support of different models types - please open the issue. +Number of supported models will be increased. Check the 'TODO' section. If you want support for different model types, please open an issue. -Py-Models-Parser written with PEG parser and it's python implementation - parsimonious. It's pretty new and I did not cover all possible test cases, so if you will have an issue - please just open an issue in this case with example, I will fix it as soon as possible. +Py-Models-Parser is written with PEG parser and its Python implementation - parsimonious. It's pretty new and I did not cover all possible test cases, so if you have an issue - please just open an issue with an example, I will fix it as soon as possible. -Py-Models-Parser take as input different Python code with Models and provide output in standard form: +**NOTE:** This is a text parser, so it doesn't import or load your code. The parser works with source code as text, not objects in Python. So to run the parser you DO NOT NEED to install dependencies for models that you're trying to parse - only the models themselves. -```python +## Output Format - [ - 'name': 'ModelName', - 'parents': ['BaseModel'], # class parents that defined in (), for example: `class MaterialType(str, Enum):` parents - str, Enum - 'attrs': +Py-Models-Parser takes different Python code with Models as input and provides output in a standard form: + +```python +[ { - 'type': 'integer', - 'name': 'attr_name', - 'default': 'default_value', + 'name': 'ModelName', + 'parents': ['BaseModel'], # class parents defined in () + 'attrs': [ + { + 'name': 'attr_name', + 'type': 'integer', + 'default': 'default_value', + 'properties': { + # additional info like foreign_key, server_default, etc. + } + } + ], 'properties': { - ... + 'table_name': '...' # model-level properties } - }, - 'properties': { - 'table_name': ... } - ] +] ``` -For ORM models 'attrs' contains Columns of course. - -3 keys - 'type', 'name', 'default' exists in parse result 'attrs' of all Models -'properties' key contains additional information for attribut or column depend on Model type, for example, in ORM models it can contains 'foreign_key' key if this column used ForeignKey, or 'server_default' if it is a SqlAlchemy model or GinoORM. +For ORM models, 'attrs' contains columns. Three keys - 'type', 'name', 'default' - exist in all parsed model attrs. The 'properties' key contains additional information depending on Model type. -Model level 'properties' contains information relative to model, for example, if it ORM model - table_name - -NOTE: it's is a text parser, so it don't import or load your code, parser work with source code as text, not objects in Python. So to run parser you DO NOT NEED install dependencies for models, that you tries to parse - only models. - -## How to install +## Installation ```bash - - pip install py-models-parser - +pip install py-models-parser ``` -## How to use - -Library detect automaticaly that type of models you tries to parse. You can check a lot of examples in test/ folder on the GitHub +## Usage -1. You can parse models from python string: +### Parse from string ```python - from py_models_parser import parse -models_str = """from gino import Gino +models_str = """ +from gino import Gino db = Gino() - class OrderItems(db.Model): - __tablename__ = 'order_items' product_no = db.Column(db.Integer(), db.ForeignKey('products.product_no'), ondelete="RESTRICT", primary_key=True) order_id = db.Column(db.Integer(), db.ForeignKey('orders.order_id'), ondelete="CASCADE", primary_key=True) - type = db.Column(db.Integer(), db.ForeignKey('types.type_id'), ondelete="RESTRICT", onupdate="CASCADE") - - """ -result = parse(models_str) +""" +result = parse(models_str) ``` -2. Parse models from file: +### Parse from file ```python +from py_models_parser import parse_from_file - from py_models_parser import parse_from_file - - - file_path = "path/to/your/models.py" - # for example: tests/data/dataclass_defaults.py - result = parse_from_file(file_path) +result = parse_from_file("path/to/your/models.py") ``` -3. Parse models from file with command line - -```bash - - pmp path_to_models.py - - # for example: pmp tests/data/dataclass_defaults.py +### Parse OpenAPI/Swagger specifications +```python +from py_models_parser import parse_openapi, parse_openapi_file + +# Parse from string +openapi_spec = """ +openapi: "3.0.0" +components: + schemas: + User: + type: object + properties: + id: + type: integer + name: + type: string +""" +result = parse_openapi(openapi_spec) + +# Or parse from file (supports both YAML and JSON) +result = parse_openapi_file("path/to/openapi.yaml") ``` -Output from cli can be dumped in 'output_models.json' file - use flag '-d' '--dump' if you want to change target file name, provide it after argument like '-d target_file.json' +### CLI ```bash +pmp path_to_models.py - # example how to dump output from cli - - pmp path_to_models.py -d target_file.json - +# Dump output to JSON file +pmp path_to_models.py -d target_file.json ``` -### Output example - -You can find a lot of output examples in tests - https://github.com/xnuinside/py-models-parser/tree/main/tests +## Output Example -For model from point 1 (above) library will produce the result: +For the GinoORM model above, the library produces: ```python - - [ - { - "attrs": [ - { - "default": None, - "name": "product_no", - "properties": { - "foreign_key": "'products.product_no'", - "ondelete": '"RESTRICT"', - "primary_key": "True", - }, - "type": "db.Integer()", - }, - { - "default": None, - "name": "order_id", - "properties": { - "foreign_key": "'orders.order_id'", - "ondelete": '"CASCADE"', - "primary_key": "True", - }, - "type": "db.Integer()", +[ + { + "name": "OrderItems", + "parents": ["db.Model"], + "attrs": [ + { + "name": "product_no", + "type": "db.Integer()", + "default": None, + "properties": { + "foreign_key": "'products.product_no'", + "ondelete": '"RESTRICT"', + "primary_key": "True", }, - { - "default": None, - "name": "type", - "properties": { - "foreign_key": "'types.type_id'", - "ondelete": '"RESTRICT"', - "onupdate": '"CASCADE"', - }, - "type": "db.Integer()", + }, + { + "name": "order_id", + "type": "db.Integer()", + "default": None, + "properties": { + "foreign_key": "'orders.order_id'", + "ondelete": '"CASCADE"', + "primary_key": "True", }, - ], - "name": "OrderItems", - "parents": ["db.Model"], - "properties": {"table_name": "'order_items'"}, - } - ] + }, + ], + "properties": {"table_name": "'order_items'"}, + } +] ``` -## TODO: in next Release +You can find more output examples in [tests](https://github.com/xnuinside/py-models-parser/tree/main/tests). + +## TODO 1. Add more tests for supported models 2. Add support for SQLAlchemy Core Tables - - -## Changelog -**v1.0.0** -Breaking Changes: -1. Dropped support for Python 3.7 and 3.8 -2. Minimum required Python version is now 3.9 - -New Features: -1. Added support for Python 3.12 and 3.13 -2. Added OpenAPI 3.0/Swagger specification parser (parse_openapi, parse_openapi_file) -3. Added tox for multi-version testing -4. Added ARCHITECTURE.md with project documentation -5. Added pyyaml dependency for OpenAPI parsing - -**v0.7.0** -Updates: -1. Added support for latest version of parsimonious. -Library now works with Python 3.11. - - -**v0.6.0** -Features: -1. Added support for Encode ORM models https://github.com/encode/orm -2. Added support for Piccolo ORM models https://piccolo-orm.readthedocs.io/en/latest/piccolo/schema/defining.html - - -**v0.5.1** -Fixes: -1. Sometimes multiple parents names in "parents" output was joined in one string - fixed. - -**v0.5.0** -1. Added base support for Pydal tables definitions -2. Added support for python list syntax like [] - -**v0.4.0** -1. return tuples (multiple values) is parsed correctly now -2. symbols like `*&^%$#!±~`§<>` now does not cause any errors -3. classes without any args does not cause an error anymore - -**v0.3.0** -1. Added cli - `pmp` command with args -d, --dump -2. Added support for simple Django ORM models -3. Added base support for pure Python Classes - -**v0.2.0** -1. Added support for Dataclasses -2. Added parse_from_file method -3. Added correct work with types with comma inside, like: Union[dict, list] or Union[dict, list, tuple, anything] - -**v0.1.1** -1. Added base parser logic & tests for Pydantic, Enums, SQLAlchemy Models, GinoORM models, TortoiseORM models diff --git a/docs/README.rst b/docs/README.rst deleted file mode 100644 index 64feefa..0000000 --- a/docs/README.rst +++ /dev/null @@ -1,295 +0,0 @@ - -Py-Models-Parser ----------------- - - -.. image:: https://img.shields.io/pypi/v/py-models-parser - :target: https://img.shields.io/pypi/v/py-models-parser - :alt: badge1 - -.. image:: https://img.shields.io/pypi/l/py-models-parser - :target: https://img.shields.io/pypi/l/py-models-parser - :alt: badge2 - -.. image:: https://img.shields.io/pypi/pyversions/py-models-parser - :target: https://img.shields.io/pypi/pyversions/py-models-parser - :alt: badge3 - -.. image:: https://github.com/xnuinside/py-models-parser/actions/workflows/main.yml/badge.svg - :target: https://github.com/xnuinside/py-models-parser/actions/workflows/main.yml/badge.svg - :alt: workflow - - -It's as second Parser that done by me, first is a https://github.com/xnuinside/simple-ddl-parser for SQL DDL with different dialects. - -Py-Models-Parser can parse & extract information from models & table definitions: - - -* Sqlalchemy ORM (https://docs.sqlalchemy.org/en/14/orm/), -* Gino ORM (https://python-gino.org/), -* Tortoise ORM (https://tortoise-orm.readthedocs.io/en/latest/), -* Encode ORM (https://github.com/encode/orm) -* Django ORM Model (https://docs.djangoproject.com/en/3.2/topics/db/queries/), -* Pydantic (https://pydantic-docs.helpmanual.io/), -* Python Enum (https://docs.python.org/3/library/enum.html), -* Pony ORM (https://ponyorm.org/), -* Piccolo ORM models (https://piccolo-orm.readthedocs.io/en/latest/piccolo/schema/defining.html), -* Pydal Tables definitions (http://www.web2py.com/books/default/chapter/29/06/the-database-abstraction-layer#The-DAL-A-quick-tour), -* Python Dataclasses (https://docs.python.org/3/library/dataclasses.html), -* pure Python Classes (https://docs.python.org/3/tutorial/classes.html#class-objects), -* OpenAPI 3.0/Swagger specifications (https://swagger.io/specification/) - -Number of supported models will be increased, check 'TODO' section, if you want to have support of different models types - please open the issue. - -Py-Models-Parser written with PEG parser and it's python implementation - parsimonious. It's pretty new and I did not cover all possible test cases, so if you will have an issue - please just open an issue in this case with example, I will fix it as soon as possible. - -Py-Models-Parser take as input different Python code with Models and provide output in standard form: - -.. code-block:: python - - - [ - 'name': 'ModelName', - 'parents': ['BaseModel'], # class parents that defined in (), for example: `class MaterialType(str, Enum):` parents - str, Enum - 'attrs': - { - 'type': 'integer', - 'name': 'attr_name', - 'default': 'default_value', - 'properties': { - ... - } - }, - 'properties': { - 'table_name': ... - } - ] - -For ORM models 'attrs' contains Columns of course. - -3 keys - 'type', 'name', 'default' exists in parse result 'attrs' of all Models -'properties' key contains additional information for attribut or column depend on Model type, for example, in ORM models it can contains 'foreign_key' key if this column used ForeignKey, or 'server_default' if it is a SqlAlchemy model or GinoORM. - -Model level 'properties' contains information relative to model, for example, if it ORM model - table_name - -NOTE: it's is a text parser, so it don't import or load your code, parser work with source code as text, not objects in Python. So to run parser you DO NOT NEED install dependencies for models, that you tries to parse - only models. - -How to install --------------- - -.. code-block:: bash - - - pip install py-models-parser - -How to use ----------- - -Library detect automaticaly that type of models you tries to parse. You can check a lot of examples in test/ folder on the GitHub - - -#. You can parse models from python string: - -.. code-block:: python - - - from py_models_parser import parse - - models_str = """from gino import Gino - - db = Gino() - - - class OrderItems(db.Model): - - __tablename__ = 'order_items' - - product_no = db.Column(db.Integer(), db.ForeignKey('products.product_no'), ondelete="RESTRICT", primary_key=True) - order_id = db.Column(db.Integer(), db.ForeignKey('orders.order_id'), ondelete="CASCADE", primary_key=True) - type = db.Column(db.Integer(), db.ForeignKey('types.type_id'), ondelete="RESTRICT", onupdate="CASCADE") - - """ - result = parse(models_str) - - -#. Parse models from file: - -.. code-block:: python - - - from py_models_parser import parse_from_file - - - file_path = "path/to/your/models.py" - # for example: tests/data/dataclass_defaults.py - result = parse_from_file(file_path) - - -#. Parse OpenAPI/Swagger specifications: - -.. code-block:: python - - - from py_models_parser import parse_openapi, parse_openapi_file - - # Parse from string - openapi_spec = """ - openapi: "3.0.0" - components: - schemas: - User: - type: object - properties: - id: - type: integer - name: - type: string - """ - result = parse_openapi(openapi_spec) - - # Or parse from file (supports both YAML and JSON) - result = parse_openapi_file("path/to/openapi.yaml") - - -#. Parse models from file with command line - -.. code-block:: bash - - - pmp path_to_models.py - - # for example: pmp tests/data/dataclass_defaults.py - -Output from cli can be dumped in 'output_models.json' file - use flag '-d' '--dump' if you want to change target file name, provide it after argument like '-d target_file.json' - -.. code-block:: bash - - - # example how to dump output from cli - - pmp path_to_models.py -d target_file.json - -Output example -^^^^^^^^^^^^^^ - -You can find a lot of output examples in tests - https://github.com/xnuinside/py-models-parser/tree/main/tests - -For model from point 1 (above) library will produce the result: - -.. code-block:: python - - - [ - { - "attrs": [ - { - "default": None, - "name": "product_no", - "properties": { - "foreign_key": "'products.product_no'", - "ondelete": '"RESTRICT"', - "primary_key": "True", - }, - "type": "db.Integer()", - }, - { - "default": None, - "name": "order_id", - "properties": { - "foreign_key": "'orders.order_id'", - "ondelete": '"CASCADE"', - "primary_key": "True", - }, - "type": "db.Integer()", - }, - { - "default": None, - "name": "type", - "properties": { - "foreign_key": "'types.type_id'", - "ondelete": '"RESTRICT"', - "onupdate": '"CASCADE"', - }, - "type": "db.Integer()", - }, - ], - "name": "OrderItems", - "parents": ["db.Model"], - "properties": {"table_name": "'order_items'"}, - } - ] - -TODO: in next Release ---------------------- - - -#. Add more tests for supported models -#. Add support for SQLAlchemy Core Tables - -Changelog ---------- - -**v1.0.0** -Breaking Changes: - -#. Dropped support for Python 3.7 and 3.8 -#. Minimum required Python version is now 3.9 - -New Features: - -#. Added support for Python 3.12 and 3.13 -#. Added OpenAPI 3.0/Swagger specification parser (parse_openapi, parse_openapi_file) -#. Added tox for multi-version testing -#. Added ARCHITECTURE.md with project documentation - -**v0.7.0** -Updates: - - -#. Added support for latest version of parsimonious. - Library now works with Python 3.11. - -**v0.6.0** -Features: - - -#. Added support for Encode ORM models https://github.com/encode/orm -#. Added support for Piccolo ORM models https://piccolo-orm.readthedocs.io/en/latest/piccolo/schema/defining.html - -**v0.5.1** -Fixes: - - -#. Sometimes multiple parents names in "parents" output was joined in one string - fixed. - -**v0.5.0** - - -#. Added base support for Pydal tables definitions -#. Added support for python list syntax like [] - -**v0.4.0** - - -#. return tuples (multiple values) is parsed correctly now -#. symbols like ``*&^%$#!±~``\ §<>` now does not cause any errors -#. classes without any args does not cause an error anymore - -**v0.3.0** - - -#. Added cli - ``pmp`` command with args -d, --dump -#. Added support for simple Django ORM models -#. Added base support for pure Python Classes - -**v0.2.0** - - -#. Added support for Dataclasses -#. Added parse_from_file method -#. Added correct work with types with comma inside, like: Union[dict, list] or Union[dict, list, tuple, anything] - -**v0.1.1** - - -#. Added base parser logic & tests for Pydantic, Enums, SQLAlchemy Models, GinoORM models, TortoiseORM models diff --git a/poetry.lock b/poetry.lock index 0956a85..88d5e66 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. [[package]] name = "bleach" @@ -6,6 +6,7 @@ version = "6.0.0" description = "An easy safelist-based HTML-sanitizing tool." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "bleach-6.0.0-py3-none-any.whl", hash = "sha256:33c16e3353dbd13028ab4799a0f89a83f113405c766e9c122df8a06f5b85b3f4"}, {file = "bleach-6.0.0.tar.gz", hash = "sha256:1a1a85c1595e07d8db14c5f09f09e6433502c51c595970edc090551f0db99414"}, @@ -24,6 +25,7 @@ version = "2023.7.22" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, @@ -35,6 +37,8 @@ version = "1.15.1" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = "*" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" files = [ {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, @@ -111,6 +115,7 @@ version = "3.2.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7.0" +groups = ["dev"] files = [ {file = "charset-normalizer-3.2.0.tar.gz", hash = "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace"}, {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710"}, @@ -195,6 +200,8 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev"] +markers = "sys_platform == \"win32\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -206,6 +213,8 @@ version = "41.0.3" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" files = [ {file = "cryptography-41.0.3-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:652627a055cb52a84f8c448185922241dd5217443ca194d5739b44612c5e6507"}, {file = "cryptography-41.0.3-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:8f09daa483aedea50d249ef98ed500569841d6498aa9c9f4b0531b9964658922"}, @@ -251,6 +260,7 @@ version = "0.20.1" description = "Docutils -- Python Documentation Utilities" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "docutils-0.20.1-py3-none-any.whl", hash = "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6"}, {file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"}, @@ -262,6 +272,8 @@ version = "1.1.3" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" +groups = ["dev"] +markers = "python_version < \"3.11\"" files = [ {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"}, {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"}, @@ -276,6 +288,7 @@ version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.5" +groups = ["dev"] files = [ {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, @@ -287,37 +300,20 @@ version = "6.7.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.7" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\" or python_version == \"3.9\"" files = [ {file = "importlib_metadata-6.7.0-py3-none-any.whl", hash = "sha256:cb52082e659e97afc5dac71e79de97d8681de3aa07ff18578330904a9d18e5b5"}, {file = "importlib_metadata-6.7.0.tar.gz", hash = "sha256:1aaf550d4f73e5d6783e7acb77aec43d49da8017410afae93822cc9cca98c4d4"}, ] [package.dependencies] -typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} zipp = ">=0.5" [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] - -[[package]] -name = "importlib-resources" -version = "5.12.0" -description = "Read resources from Python packages" -optional = false -python-versions = ">=3.7" -files = [ - {file = "importlib_resources-5.12.0-py3-none-any.whl", hash = "sha256:7b1deeebbf351c7578e09bf2f63fa2ce8b5ffec296e0d349139d43cca061a81a"}, - {file = "importlib_resources-5.12.0.tar.gz", hash = "sha256:4be82589bf5c1d7999aedf2a45159d10cb3ca4f19b2271f8792bc8e6da7b22f6"}, -] - -[package.dependencies] -zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] +testing = ["flufl.flake8", "importlib-resources (>=1.3) ; python_version < \"3.9\"", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7) ; platform_python_implementation != \"PyPy\"", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1) ; platform_python_implementation != \"PyPy\"", "pytest-perf (>=0.9.2)", "pytest-ruff"] [[package]] name = "iniconfig" @@ -325,6 +321,7 @@ version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, @@ -336,6 +333,8 @@ version = "3.2.3" description = "Utility functions for Python class constructs" optional = false python-versions = ">=3.7" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" files = [ {file = "jaraco.classes-3.2.3-py3-none-any.whl", hash = "sha256:2353de3288bc6b82120752201c6b1c1a14b058267fa424ed5ce5984e3b922158"}, {file = "jaraco.classes-3.2.3.tar.gz", hash = "sha256:89559fa5c1d3c34eff6f631ad80bb21f378dbcbb35dd161fd2c6b93f5be2f98a"}, @@ -346,7 +345,7 @@ more-itertools = "*" [package.extras] docs = ["jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] -testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] +testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7) ; platform_python_implementation != \"PyPy\"", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1) ; platform_python_implementation != \"PyPy\""] [[package]] name = "jeepney" @@ -354,6 +353,8 @@ version = "0.8.0" description = "Low-level, pure Python DBus protocol wrapper." optional = false python-versions = ">=3.7" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" files = [ {file = "jeepney-0.8.0-py3-none-any.whl", hash = "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755"}, {file = "jeepney-0.8.0.tar.gz", hash = "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806"}, @@ -361,7 +362,7 @@ files = [ [package.extras] test = ["async-timeout", "pytest", "pytest-asyncio (>=0.17)", "pytest-trio", "testpath", "trio"] -trio = ["async_generator", "trio"] +trio = ["async_generator ; python_version == \"3.6\"", "trio"] [[package]] name = "keyring" @@ -369,6 +370,8 @@ version = "24.1.1" description = "Store and access your passwords safely." optional = false python-versions = ">=3.7" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" files = [ {file = "keyring-24.1.1-py3-none-any.whl", hash = "sha256:bc402c5e501053098bcbd149c4ddbf8e36c6809e572c2d098d4961e88d4c270d"}, {file = "keyring-24.1.1.tar.gz", hash = "sha256:3d44a48fa9a254f6c72879d7c88604831ebdaac6ecb0b214308b02953502c510"}, @@ -376,7 +379,6 @@ files = [ [package.dependencies] importlib-metadata = {version = ">=4.11.4", markers = "python_version < \"3.12\""} -importlib-resources = {version = "*", markers = "python_version < \"3.9\""} "jaraco.classes" = "*" jeepney = {version = ">=0.4.2", markers = "sys_platform == \"linux\""} pywin32-ctypes = {version = ">=0.2.0", markers = "sys_platform == \"win32\""} @@ -385,21 +387,7 @@ SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""} [package.extras] completion = ["shtab"] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-ruff"] - -[[package]] -name = "m2r" -version = "0.3.1" -description = "Markdown and reStructuredText in a single file." -optional = false -python-versions = "*" -files = [ - {file = "m2r-0.3.1.tar.gz", hash = "sha256:aafb67fc49cfb1d89e46a3443ac747e15f4bb42df20ed04f067ad9efbee256ab"}, -] - -[package.dependencies] -docutils = "*" -mistune = "<2" +testing = ["pytest (>=6)", "pytest-black (>=0.3.7) ; platform_python_implementation != \"PyPy\"", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1) ; platform_python_implementation != \"PyPy\"", "pytest-ruff"] [[package]] name = "markdown-it-py" @@ -407,6 +395,7 @@ version = "2.2.0" description = "Python port of markdown-it. Markdown parsing, done right!" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "markdown-it-py-2.2.0.tar.gz", hash = "sha256:7c9a5e412688bc771c67432cbfebcdd686c93ce6484913dccf06cb5a0bea35a1"}, {file = "markdown_it_py-2.2.0-py3-none-any.whl", hash = "sha256:5a35f8d1870171d9acc47b99612dc146129b631baf04970128b568f190d0cc30"}, @@ -414,7 +403,6 @@ files = [ [package.dependencies] mdurl = ">=0.1,<1.0" -typing_extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""} [package.extras] benchmarking = ["psutil", "pytest", "pytest-benchmark"] @@ -432,28 +420,20 @@ version = "0.1.2" description = "Markdown URL utilities" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, ] -[[package]] -name = "mistune" -version = "0.8.4" -description = "The fastest markdown parser in pure Python" -optional = false -python-versions = "*" -files = [ - {file = "mistune-0.8.4-py2.py3-none-any.whl", hash = "sha256:88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"}, - {file = "mistune-0.8.4.tar.gz", hash = "sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e"}, -] - [[package]] name = "more-itertools" version = "9.1.0" description = "More routines for operating on iterables, beyond itertools" optional = false python-versions = ">=3.7" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" files = [ {file = "more-itertools-9.1.0.tar.gz", hash = "sha256:cabaa341ad0389ea83c17a94566a53ae4c9d07349861ecb14dc6d0345cf9ac5d"}, {file = "more_itertools-9.1.0-py3-none-any.whl", hash = "sha256:d2bc7f02446e86a68911e58ded76d6561eea00cddfb2a91e7019bbb586c799f3"}, @@ -465,6 +445,7 @@ version = "23.1" description = "Core utilities for Python packages" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, @@ -476,6 +457,7 @@ version = "0.10.0" description = "(Soon to be) the fastest pure-Python PEG parser I could muster" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "parsimonious-0.10.0-py3-none-any.whl", hash = "sha256:982ab435fabe86519b57f6b35610aa4e4e977e9f02a14353edf4bbc75369fc0f"}, {file = "parsimonious-0.10.0.tar.gz", hash = "sha256:8281600da180ec8ae35427a4ab4f7b82bfec1e3d1e52f80cb60ea82b9512501c"}, @@ -490,6 +472,7 @@ version = "1.9.6" description = "Query metadata from sdists / bdists / installed packages." optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "pkginfo-1.9.6-py3-none-any.whl", hash = "sha256:4b7a555a6d5a22169fcc9cf7bfd78d296b0361adad412a346c1226849af5e546"}, {file = "pkginfo-1.9.6.tar.gz", hash = "sha256:8fd5896e8718a4372f0ea9cc9d96f6417c9b986e23a4d116dda26b62cc29d046"}, @@ -500,21 +483,19 @@ testing = ["pytest", "pytest-cov"] [[package]] name = "pluggy" -version = "1.2.0" +version = "1.6.0" description = "plugin and hook calling mechanisms for python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" +groups = ["dev"] files = [ - {file = "pluggy-1.2.0-py3-none-any.whl", hash = "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849"}, - {file = "pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3"}, + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, ] -[package.dependencies] -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} - [package.extras] dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] +testing = ["coverage", "pytest", "pytest-benchmark"] [[package]] name = "pycparser" @@ -522,6 +503,8 @@ version = "2.21" description = "C parser in Python" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" files = [ {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, @@ -533,36 +516,38 @@ version = "2.16.1" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "Pygments-2.16.1-py3-none-any.whl", hash = "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692"}, {file = "Pygments-2.16.1.tar.gz", hash = "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29"}, ] [package.extras] -plugins = ["importlib-metadata"] +plugins = ["importlib-metadata ; python_version < \"3.8\""] [[package]] name = "pytest" -version = "7.4.0" +version = "8.4.2" description = "pytest: simple powerful testing with Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" +groups = ["dev"] files = [ - {file = "pytest-7.4.0-py3-none-any.whl", hash = "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32"}, - {file = "pytest-7.4.0.tar.gz", hash = "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a"}, + {file = "pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79"}, + {file = "pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01"}, ] [package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} -iniconfig = "*" -packaging = "*" -pluggy = ">=0.12,<2.0" -tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} +colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1", markers = "python_version < \"3.11\""} +iniconfig = ">=1" +packaging = ">=20" +pluggy = ">=1.5,<2" +pygments = ">=2.7.2" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] -testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] [[package]] name = "pywin32-ctypes" @@ -570,17 +555,103 @@ version = "0.2.2" description = "A (partial) reimplementation of pywin32 using ctypes/cffi" optional = false python-versions = ">=3.6" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"win32\"" files = [ {file = "pywin32-ctypes-0.2.2.tar.gz", hash = "sha256:3426e063bdd5fd4df74a14fa3cf80a0b42845a87e1d1e81f6549f9daec593a60"}, {file = "pywin32_ctypes-0.2.2-py3-none-any.whl", hash = "sha256:bf490a1a709baf35d688fe0ecf980ed4de11d2b3e37b51e5442587a75d9957e7"}, ] +[[package]] +name = "pyyaml" +version = "6.0.3" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6"}, + {file = "PyYAML-6.0.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369"}, + {file = "PyYAML-6.0.3-cp38-cp38-win32.whl", hash = "sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295"}, + {file = "PyYAML-6.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69"}, + {file = "pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e"}, + {file = "pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4"}, + {file = "pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b"}, + {file = "pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea"}, + {file = "pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be"}, + {file = "pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7"}, + {file = "pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0"}, + {file = "pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007"}, + {file = "pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f"}, +] + [[package]] name = "readme-renderer" version = "37.3" description = "readme_renderer is a library for rendering \"readme\" descriptions for Warehouse" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "readme_renderer-37.3-py3-none-any.whl", hash = "sha256:f67a16caedfa71eef48a31b39708637a6f4664c4394801a7b0d6432d13907343"}, {file = "readme_renderer-37.3.tar.gz", hash = "sha256:cd653186dfc73055656f090f227f5cb22a046d7f71a841dfa305f55c9a513273"}, @@ -600,6 +671,7 @@ version = "2023.8.8" description = "Alternative regular expression module, to replace re." optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "regex-2023.8.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:88900f521c645f784260a8d346e12a1590f79e96403971241e64c3a265c8ecdb"}, {file = "regex-2023.8.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3611576aff55918af2697410ff0293d6071b7e00f4b09e005d614686ac4cd57c"}, @@ -697,6 +769,7 @@ version = "2.31.0" description = "Python HTTP for Humans." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, @@ -718,6 +791,7 @@ version = "1.0.0" description = "A utility belt for advanced users of python-requests" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["dev"] files = [ {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"}, {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"}, @@ -732,6 +806,7 @@ version = "2.0.0" description = "Validating URI References per RFC 3986" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd"}, {file = "rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c"}, @@ -746,6 +821,7 @@ version = "13.5.2" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.7.0" +groups = ["dev"] files = [ {file = "rich-13.5.2-py3-none-any.whl", hash = "sha256:146a90b3b6b47cac4a73c12866a499e9817426423f57c5a66949c086191a8808"}, {file = "rich-13.5.2.tar.gz", hash = "sha256:fb9d6c0a0f643c99eed3875b5377a184132ba9be4d61516a55273d3554d75a39"}, @@ -754,7 +830,6 @@ files = [ [package.dependencies] markdown-it-py = ">=2.2.0" pygments = ">=2.13.0,<3.0.0" -typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""} [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] @@ -765,6 +840,8 @@ version = "3.3.3" description = "Python bindings to FreeDesktop.org Secret Service API" optional = false python-versions = ">=3.6" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" files = [ {file = "SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99"}, {file = "SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77"}, @@ -780,6 +857,7 @@ version = "1.16.0" description = "Python 2 and 3 compatibility utilities" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +groups = ["dev"] files = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, @@ -791,6 +869,8 @@ version = "2.0.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.7" +groups = ["dev"] +markers = "python_version < \"3.11\"" files = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, @@ -798,18 +878,20 @@ files = [ [[package]] name = "twine" -version = "4.0.2" +version = "6.0.1" description = "Collection of utilities for publishing packages on PyPI" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" +groups = ["dev"] files = [ - {file = "twine-4.0.2-py3-none-any.whl", hash = "sha256:929bc3c280033347a00f847236564d1c52a3e61b1ac2516c97c48f3ceab756d8"}, - {file = "twine-4.0.2.tar.gz", hash = "sha256:9e102ef5fdd5a20661eb88fad46338806c3bd32cf1db729603fe3697b1bc83c8"}, + {file = "twine-6.0.1-py3-none-any.whl", hash = "sha256:9c6025b203b51521d53e200f4a08b116dee7500a38591668c6a6033117bdc218"}, + {file = "twine-6.0.1.tar.gz", hash = "sha256:36158b09df5406e1c9c1fb8edb24fc2be387709443e7376689b938531582ee27"}, ] [package.dependencies] -importlib-metadata = ">=3.6" -keyring = ">=15.1" +importlib-metadata = {version = ">=3.6", markers = "python_version < \"3.10\""} +keyring = {version = ">=15.1", markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\""} +packaging = "*" pkginfo = ">=1.8.1" readme-renderer = ">=35.0" requests = ">=2.20" @@ -818,16 +900,8 @@ rfc3986 = ">=1.4.0" rich = ">=12.0.0" urllib3 = ">=1.26.0" -[[package]] -name = "typing-extensions" -version = "4.7.1" -description = "Backported and Experimental Type Hints for Python 3.7+" -optional = false -python-versions = ">=3.7" -files = [ - {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, - {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, -] +[package.extras] +keyring = ["keyring (>=15.1)"] [[package]] name = "urllib3" @@ -835,13 +909,14 @@ version = "2.0.4" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "urllib3-2.0.4-py3-none-any.whl", hash = "sha256:de7df1803967d2c2a98e4b11bb7d6bd9210474c46e8a0401514e3a42a75ebde4"}, {file = "urllib3-2.0.4.tar.gz", hash = "sha256:8d22f86aae8ef5e410d4f539fde9ce6b2113a001bb4d189e0aed70642d602b11"}, ] [package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] @@ -852,6 +927,7 @@ version = "0.5.1" description = "Character encoding aliases for legacy web content" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, @@ -863,6 +939,8 @@ version = "3.15.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.7" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\" or python_version == \"3.9\"" files = [ {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, @@ -870,9 +948,9 @@ files = [ [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] +testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7) ; platform_python_implementation != \"PyPy\"", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8 ; python_version < \"3.12\"", "pytest-mypy (>=0.9.1) ; platform_python_implementation != \"PyPy\""] [metadata] -lock-version = "2.0" -python-versions = "^3.7" -content-hash = "af086694608bb54c170af5ab51dd55e029fd2ee5e650ddcfb44482fb398e3c84" +lock-version = "2.1" +python-versions = "^3.9" +content-hash = "c7145ced535febcd72c45e30a1e1abb9ceab0cd856c23719abd2d40b8f5794ff" diff --git a/py_models_parser/grammar.py b/py_models_parser/grammar.py index beaa809..1536f1b 100644 --- a/py_models_parser/grammar.py +++ b/py_models_parser/grammar.py @@ -12,20 +12,23 @@ class = class_def attr_def* funct_def* class_def = intend? class_name args? ":"* ws? attr_def = intend? id type? ("=" (right_part))* ws? right_part = (id args_in_brackets) / string / args / call_result / args_in_brackets / id / text - type = ":" ( (id args_in_brackets) / id) + type = ":" union_type + union_type = type_part (ws? "|" ws? type_part)* + type_part = (id args_in_brackets) / id string = one_quote_str / double_quotes_str one_quote_str = ~"\'[^\']+\'"i double_quotes_str = ~'"[^\"]+"'i list = "[" (call_result / attr_def / args / id / text)* ","* "]" funct_def = intend? "def" id args? ":"* ws? - args_in_brackets = "[" ((id/string)* ","* )* "]" + args_in_brackets = "[" ((type_with_brackets / id / string)* ","* )* "]" + type_with_brackets = id args_in_brackets args = "(" (( list / call_result / args / attr_def / id )* ","* )* ")" call_result = id args ws? class_name = "class" id id = (((dot_id / text)+ ) * / dot_id / text) ws? dot_id = (text ".")*text intend = " " / "\t" / "\n" - text = !"class" ~r"['_A-Z 0-9{}_\"\-\/\$<%>\+\-\w*&^%$#!±~`§]*"i + text = !"class" ~r"['_A-Z 0-9{}_\"\-\/\$<%>\+\-\w*&^%$#!±~`§@]*"i ws = ~"\\s*" emptyline = ws+ """ diff --git a/py_models_parser/visitor.py b/py_models_parser/visitor.py index 825b62b..9a2946b 100644 --- a/py_models_parser/visitor.py +++ b/py_models_parser/visitor.py @@ -147,6 +147,7 @@ def visit_attr_def(self, node, visited_children): left = node.children[1].children[0].children[0].text.strip() default = None _type = None + annotation_type = None if "def " in left: attr = {"attr": {"name": None, "type": _type, "default": default}} return attr @@ -160,10 +161,13 @@ def visit_attr_def(self, node, visited_children): if "default" in children[-1][-1]: attr["attr"]["default"] = children[-1][-1]["default"] attr["attr"]["properties"] = children[-1][-1]["properties"] - if children[-1][-1]["type"] is not None: + # Only override type if not already set from annotation + if children[-1][-1]["type"] and not annotation_type: attr["attr"]["type"] = children[-1][-1]["type"] elif isinstance(children[-1], dict) and "type" in children[-1]: - attr["attr"]["type"] = children[-1]["type"] + # Type from annotation (e.g., ": str") + annotation_type = children[-1]["type"] + attr["attr"]["type"] = annotation_type return attr @staticmethod @@ -177,6 +181,9 @@ def _process_attr(attr: Dict, final_item: Dict) -> Dict: final_item["properties"][attr["name"]] = attr["default"] elif "table_args" in attr["name"]: final_item["properties"][attr["name"]] = attr["type"] or attr["default"] + elif attr["name"] == "model_config": + # Pydantic v2 model configuration + final_item["properties"]["model_config"] = attr["default"] else: if final_item["properties"].get("init") is not None: final_item["properties"]["init"].append(attr) diff --git a/pyproject.toml b/pyproject.toml index 12855d9..dafd141 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ version = "1.0.0" description = "Parser for Different Python Models (Pydantic, Enums, ORMs: Tortoise, SqlAlchemy, GinoORM, PonyORM, Pydal tables) to extract information about columns(attrs), model, table args,etc in one format." authors = ["Iuliia Volkova "] license = "MIT" -readme = "docs/README.rst" +readme = "README.md" homepage = "https://github.com/xnuinside/py-models-parser" repository = "https://github.com/xnuinside/py-models-parser" classifiers = [ @@ -25,18 +25,18 @@ classifiers = [ [tool.poetry.dependencies] python = "^3.9" parsimonious = "^0.10.0" -pyyaml = "^6.0" +pyyaml = "^6.0.1" -[tool.poetry.dev-dependencies] -pytest = "^7.4" -twine = "^4.0" -m2r = "^0.3.1" -tox = "^4.0" +[tool.poetry.group.dev.dependencies] +pytest = "^8.0" +twine = "^6.0" [tool.poetry.scripts] pmp = 'py_models_parser.cli:main' - [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" + +[tool.pytest.ini_options] +testpaths = ["tests"] diff --git a/tests/test_pydantic_v2.py b/tests/test_pydantic_v2.py new file mode 100644 index 0000000..732da9b --- /dev/null +++ b/tests/test_pydantic_v2.py @@ -0,0 +1,173 @@ +"""Tests for Pydantic v2 models parsing.""" +from py_models_parser import parse + + +def test_pydantic_v2_basic(): + """Test basic Pydantic v2 model.""" + models_str = """ + class User(BaseModel): + id: int + name: str + email: str = "test@example.com" + """ + result = parse(models_str) + + assert len(result) == 1 + assert result[0]["name"] == "User" + assert result[0]["parents"] == ["BaseModel"] + assert len(result[0]["attrs"]) == 3 + + id_attr = result[0]["attrs"][0] + assert id_attr["name"] == "id" + assert id_attr["type"] == "int" + + email_attr = result[0]["attrs"][2] + assert email_attr["name"] == "email" + assert email_attr["default"] == '"test@example.com"' + + +def test_pydantic_v2_union_types(): + """Test Python 3.10+ union type syntax (PEP 604).""" + models_str = """ + class Config(BaseModel): + name: str + value: int | None = None + tags: list[str] | None = None + data: dict[str, int] | list[int] | None = None + """ + result = parse(models_str) + + assert len(result) == 1 + assert result[0]["name"] == "Config" + + value_attr = next(a for a in result[0]["attrs"] if a["name"] == "value") + assert value_attr["type"] == "int | None" + assert value_attr["default"] == "None" + + tags_attr = next(a for a in result[0]["attrs"] if a["name"] == "tags") + assert tags_attr["type"] == "list[str] | None" + + +def test_pydantic_v2_field_with_constraints(): + """Test Pydantic v2 Field() with validation constraints.""" + models_str = """ + class Product(BaseModel): + name: str = Field(max_length=100, description="Product name") + price: float = Field(gt=0, le=10000) + quantity: int = Field(default=0, ge=0) + """ + result = parse(models_str) + + assert len(result) == 1 + + name_attr = next(a for a in result[0]["attrs"] if a["name"] == "name") + assert name_attr["type"] == "str" + assert name_attr["properties"]["max_length"] == "100" + assert name_attr["properties"]["description"] == '"Product name"' + + price_attr = next(a for a in result[0]["attrs"] if a["name"] == "price") + assert price_attr["type"] == "float" + assert price_attr["properties"]["gt"] == "0" + assert price_attr["properties"]["le"] == "10000" + + quantity_attr = next(a for a in result[0]["attrs"] if a["name"] == "quantity") + assert quantity_attr["type"] == "int" + assert quantity_attr["properties"]["ge"] == "0" + + +def test_pydantic_v2_model_config(): + """Test Pydantic v2 model_config with ConfigDict.""" + models_str = """ + class Settings(BaseModel): + model_config = ConfigDict(str_max_length=100, frozen=True) + + name: str + debug: bool = False + """ + result = parse(models_str) + + assert len(result) == 1 + assert result[0]["name"] == "Settings" + assert "model_config" in result[0]["properties"] + assert "ConfigDict" in result[0]["properties"]["model_config"] + assert "frozen=True" in result[0]["properties"]["model_config"] + + # Ensure model_config is not in attrs + attr_names = [a["name"] for a in result[0]["attrs"]] + assert "model_config" not in attr_names + + +def test_pydantic_v2_field_with_default(): + """Test Pydantic v2 Field() with default value.""" + models_str = """ + class Order(BaseModel): + status: str = Field(default="pending") + items: list[str] = Field(default_factory=list) + """ + result = parse(models_str) + + status_attr = next(a for a in result[0]["attrs"] if a["name"] == "status") + assert status_attr["type"] == "str" + assert status_attr["default"] == '"pending"' + + +def test_pydantic_v2_optional_fields(): + """Test various optional field syntaxes.""" + models_str = """ + class Profile(BaseModel): + username: str + bio: str | None = None + age: int | None = None + avatar_url: str | None = Field(default=None, max_length=500) + """ + result = parse(models_str) + + bio_attr = next(a for a in result[0]["attrs"] if a["name"] == "bio") + assert bio_attr["type"] == "str | None" + assert bio_attr["default"] == "None" + + avatar_attr = next(a for a in result[0]["attrs"] if a["name"] == "avatar_url") + assert avatar_attr["type"] == "str | None" + assert avatar_attr["properties"]["max_length"] == "500" + + +def test_pydantic_v2_generic_types(): + """Test generic types like list[str], dict[str, int].""" + models_str = """ + class Container(BaseModel): + items: list[str] + mapping: dict[str, int] + nested: list[dict[str, str]] + """ + result = parse(models_str) + + items_attr = next(a for a in result[0]["attrs"] if a["name"] == "items") + assert items_attr["type"] == "list[str]" + + mapping_attr = next(a for a in result[0]["attrs"] if a["name"] == "mapping") + assert mapping_attr["type"] == "dict[str, int]" + + +def test_pydantic_v2_complex_model(): + """Test complex Pydantic v2 model with multiple features.""" + models_str = """ + class ComplexModel(BaseModel): + model_config = ConfigDict( + str_strip_whitespace=True, + validate_default=True + ) + + id: int + name: str = Field(min_length=1, max_length=100) + email: str | None = None + tags: list[str] = [] + metadata: dict[str, str] | None = Field(default=None) + """ + result = parse(models_str) + + assert len(result) == 1 + assert result[0]["name"] == "ComplexModel" + assert "model_config" in result[0]["properties"] + + # Check attrs count (should not include model_config) + assert len(result[0]["attrs"]) == 5