pyopenapi-gen 0.20.1__tar.gz → 0.21.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pyopenapi-gen might be problematic. Click here for more details.
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/CHANGELOG.md +40 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/PKG-INFO +1 -1
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/coverage.xml +39 -28
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/pyproject.toml +2 -2
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/__init__.py +1 -1
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/utils.py +21 -2
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/test_utils.py +245 -0
- pyopenapi_gen-0.21.0/tests/integration/test_field_mapping_e2e.py +146 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/.bandit +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/.cursor/mcp.json +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/.cursor/rules/architecture.mdc +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/.cursor/rules/coding-conventions.mdc +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/.cursor/rules/project-goal.mdc +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/.cursor/rules/testing.mdc +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/.github/BRANCH_PROTECTION.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/.github/CLAUDE_CONFIGURATION.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/.github/CODEOWNERS +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/.github/dependabot.yml +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/.github/workflows/ci.yml +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/.github/workflows/claude-auto-approve.yml +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/.github/workflows/claude-review-trigger.yml +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/.github/workflows/claude.yml +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/.github/workflows/pr-checks.yml +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/.github/workflows/production-release.yml.backup +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/.github/workflows/promote-to-staging.yml +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/.github/workflows/semantic-release.yml +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/.github/workflows/staging-publish.yml +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/.github/workflows/testpypi-publish.yml +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/.gitignore +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/.python-version +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/.vscode/settings.json +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/CLAUDE.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/CONTRIBUTING.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/LICENSE +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/Makefile +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/README.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/docs/README.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/docs/architecture.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/docs/endpoint_visitor.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/docs/helpers.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/docs/ir_models.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/docs/line_writer.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/docs/loader.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/docs/model_visitor.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/docs/release-automation.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/docs/render_context.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/docs/unified_type_resolution.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/input/business_swagger.json +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/input/minimal_swagger.json +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/input/minimal_syntax_test.json +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/input/test_name_collision_spec.json +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/mkdocs.yml +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/poetry.lock +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/scripts/sync_version_to_init.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/scripts/validate_version_sync.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/__main__.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/cli.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/context/CLAUDE.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/context/file_manager.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/context/import_collector.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/context/render_context.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/CLAUDE.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/__init__.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/auth/base.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/auth/plugins.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/exceptions.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/http_status_codes.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/http_transport.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/loader/__init__.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/loader/loader.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/loader/operations/__init__.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/loader/operations/parser.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/loader/operations/post_processor.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/loader/operations/request_body.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/loader/parameters/__init__.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/loader/parameters/parser.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/loader/responses/__init__.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/loader/responses/parser.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/loader/schemas/__init__.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/loader/schemas/extractor.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/pagination.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/parsing/__init__.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/parsing/common/__init__.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/parsing/common/ref_resolution/__init__.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/parsing/common/ref_resolution/helpers/__init__.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/parsing/common/ref_resolution/helpers/cyclic_properties.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/parsing/common/ref_resolution/helpers/direct_cycle.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/parsing/common/ref_resolution/helpers/existing_schema.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/parsing/common/ref_resolution/helpers/list_response.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/parsing/common/ref_resolution/helpers/missing_ref.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/parsing/common/ref_resolution/helpers/new_schema.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/parsing/common/ref_resolution/helpers/stripped_suffix.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/parsing/common/ref_resolution/resolve_schema_ref.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/parsing/common/type_parser.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/parsing/context.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/parsing/cycle_helpers.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/parsing/keywords/__init__.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/parsing/keywords/all_of_parser.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/parsing/keywords/any_of_parser.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/parsing/keywords/array_items_parser.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/parsing/keywords/one_of_parser.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/parsing/keywords/properties_parser.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/parsing/schema_finalizer.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/parsing/schema_parser.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/parsing/transformers/__init__.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/parsing/transformers/inline_enum_extractor.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/parsing/transformers/inline_object_promoter.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/parsing/unified_cycle_detection.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/postprocess_manager.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/schemas.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/streaming_helpers.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/telemetry.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/warning_collector.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/writers/code_writer.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/writers/documentation_writer.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/writers/line_writer.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core/writers/python_construct_renderer.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/core_package_template/README.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/emit/models_emitter.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/emitters/CLAUDE.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/emitters/client_emitter.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/emitters/core_emitter.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/emitters/docs_emitter.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/emitters/endpoints_emitter.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/emitters/exceptions_emitter.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/emitters/models_emitter.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/generator/CLAUDE.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/generator/client_generator.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/helpers/CLAUDE.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/helpers/__init__.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/helpers/endpoint_utils.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/helpers/type_cleaner.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/helpers/type_helper.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/helpers/type_resolution/__init__.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/helpers/type_resolution/array_resolver.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/helpers/type_resolution/composition_resolver.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/helpers/type_resolution/finalizer.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/helpers/type_resolution/named_resolver.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/helpers/type_resolution/object_resolver.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/helpers/type_resolution/primitive_resolver.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/helpers/type_resolution/resolver.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/helpers/url_utils.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/http_types.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/ir.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/py.typed +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/types/CLAUDE.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/types/__init__.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/types/contracts/__init__.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/types/contracts/protocols.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/types/contracts/types.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/types/resolvers/__init__.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/types/resolvers/reference_resolver.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/types/resolvers/response_resolver.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/types/resolvers/schema_resolver.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/types/services/__init__.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/types/services/type_service.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/types/strategies/__init__.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/types/strategies/response_strategy.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/visit/CLAUDE.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/visit/client_visitor.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/visit/docs_visitor.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/visit/endpoint/__init__.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/visit/endpoint/endpoint_visitor.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/visit/endpoint/generators/__init__.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/visit/endpoint/generators/docstring_generator.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/visit/endpoint/generators/endpoint_method_generator.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/visit/endpoint/generators/overload_generator.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/visit/endpoint/generators/request_generator.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/visit/endpoint/generators/response_handler_generator.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/visit/endpoint/generators/signature_generator.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/visit/endpoint/generators/url_args_generator.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/visit/endpoint/processors/__init__.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/visit/endpoint/processors/import_analyzer.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/visit/endpoint/processors/parameter_processor.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/visit/exception_visitor.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/visit/model/__init__.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/visit/model/alias_generator.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/visit/model/dataclass_generator.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/visit/model/enum_generator.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/visit/model/model_visitor.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/src/pyopenapi_gen/visit/visitor.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/__init__.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/api/test_programmatic_api.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/auth/auth_analysis.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/auth/test_auth_base.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/auth/test_auth_plugins.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/cli/cli_analysis.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/cli/test_cli_backup_diff.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/cli/test_cli_edge_cases.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/cli/test_cli_edge_cases_comprehensive.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/cli/test_cli_internal_utils.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/cli/test_http_pagination_cli.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/context/context_analysis.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/context/test_core_import_path.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/context/test_file_manager.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/context/test_import_collector.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/context/test_render_context.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/context/test_render_context_imports.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/context/test_render_context_relative_paths.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/core_analysis.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/loader/parameters/test_inline_enum_array_params.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/loader/test_extractor.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/loader/test_top_level_enum_extraction.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/parsing/common/ref_resolution/helpers/helpers_analysis.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/parsing/common/ref_resolution/helpers/test_cyclic_properties.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/parsing/common/ref_resolution/helpers/test_direct_cycle.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/parsing/common/ref_resolution/helpers/test_existing_schema.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/parsing/common/ref_resolution/helpers/test_list_response.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/parsing/common/ref_resolution/helpers/test_missing_ref.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/parsing/common/ref_resolution/helpers/test_new_schema.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/parsing/common/ref_resolution/helpers/test_stripped_suffix.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/parsing/keywords/__init__.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/parsing/keywords/keywords_analysis.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/parsing/keywords/test_all_of_parser.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/parsing/keywords/test_any_of_parser.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/parsing/keywords/test_array_items_parser.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/parsing/keywords/test_one_of_parser.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/parsing/keywords/test_properties_parser.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/parsing/parsing_analysis.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/parsing/test_context.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/parsing/test_cycle_detection.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/parsing/test_cycle_helpers.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/parsing/test_improved_schema_naming.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/parsing/test_inline_enum_extractor.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/parsing/test_inline_object_promoter.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/parsing/test_logging.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/parsing/test_ref_resolver.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/parsing/test_schema_finalizer.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/parsing/test_schema_parser.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/parsing/test_schema_parser_list_response.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/parsing/test_type_parser.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/test_dataclass_serialization.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/test_detect_circular_imports.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/test_edge_cases_integration.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/test_edge_cases_systematic.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/test_exceptions_module.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/test_forward_references.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/test_http_transport.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/test_import_resolution.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/test_ir.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/test_ir_schema.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/test_loader.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/test_loader_extensive.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/test_loader_invalid_refs.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/test_loader_malformed.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/test_loader_media_types.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/test_pagination.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/test_parsing_context.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/test_protocol_defaults.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/test_schema_parser_specific_case.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/test_schemas.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/test_simple_self_ref_check.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/test_streaming_helpers.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/test_telemetry.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/test_telemetry_client.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/test_warning_collector.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/writers/test_code_writer.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/writers/test_documentation_writer.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/writers/test_line_writer.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/writers/test_python_construct_renderer_json_wizard.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/core/writers/writers_analysis.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/emitters/emitters_analysis.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/emitters/test_client_emitter.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/emitters/test_docs_emitter.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/emitters/test_duplicate_operations.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/emitters/test_endpoints_emitter.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/emitters/test_exceptions_emitter.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/emitters/test_list_response_generation.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/emitters/test_models_emitter.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/end_to_end/test_dataclass_serialization_e2e.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/examples/test_developer_experience_demo.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/generation/generation_analysis.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/generation/test_external_core_package.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/generation_issues/specs/minimal_addmessage_like.json +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/generation_issues/test_addmessage_like_issues.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/generation_issues/test_agent_include_parameter_typing.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/generation_issues/test_message_batch_response_issue.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/generation_issues/test_overload_naming_issues.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/helpers/helpers_analysis.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/helpers/test_array_resolver.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/helpers/test_endpoint_utils.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/helpers/test_endpoint_utils_extended.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/helpers/test_get_endpoint_return_types.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/helpers/test_named_resolver.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/helpers/test_named_type_resolver.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/helpers/test_object_resolver.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/helpers/test_put_endpoint_return_types.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/helpers/test_type_cleaner.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/helpers/test_type_helper.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/helpers/test_url_utils.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/helpers/test_utils_helpers.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/integration/test_generated_code_structure.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/integrations/integrations_analysis.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/integrations/test_business_swagger_message_type.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/integrations/test_end_to_end_business_swagger.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/integrations/test_end_to_end_petstore.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/integrations/test_name_collisions.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/specs/response_unwrapping_spec.yaml +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/test_analysis_overview.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/test_init.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/types/__init__.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/types/test_business_swagger_integration.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/types/test_contracts_types.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/types/test_missing_imports_bug.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/types/test_reference_resolver.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/types/test_response_resolver.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/types/test_response_strategy.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/types/test_response_strategy_simplified.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/types/test_schema_resolver.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/types/test_schema_resolver_enums.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/types/test_type_service.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/visit/__init__.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/visit/endpoint/__init__.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/visit/endpoint/generators/__init__.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/visit/endpoint/generators/generators_analysis.md +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/visit/endpoint/generators/test_dataclass_integration.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/visit/endpoint/generators/test_docstring_generator.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/visit/endpoint/generators/test_endpoint_method_generator.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/visit/endpoint/generators/test_match_case_response.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/visit/endpoint/generators/test_overload_generator.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/visit/endpoint/generators/test_request_generator.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/visit/endpoint/generators/test_response_handler_generator.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/visit/endpoint/generators/test_response_handler_generator_strategy.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/visit/endpoint/generators/test_signature_generator.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/visit/endpoint/generators/test_url_args_generator.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/visit/endpoint/processors/__init__.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/visit/endpoint/processors/test_import_analyzer.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/visit/endpoint/processors/test_import_analyzer.py.bak +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/visit/endpoint/processors/test_parameter_processor.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/visit/endpoint/test_endpoint_visitor.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/visit/model/__init__.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/visit/model/test_alias_generator.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/visit/model/test_dataclass_generator.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/visit/model/test_dataclass_generator_json_wizard.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/visit/model/test_enum_generator.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/visit/model/test_json_value_integration.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/visit/model/test_json_value_wrapper.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/visit/test_client_visitor.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/visit/test_model_visitor.py +0 -0
- {pyopenapi_gen-0.20.1 → pyopenapi_gen-0.21.0}/tests/visit/test_visitor.py +0 -0
|
@@ -1,6 +1,46 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
## v0.21.0 (2025-10-23)
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
- **serialization**: Respect BaseSchema field mappings in DataclassSerializer
|
|
9
|
+
([`ef5d365`](https://github.com/mindhiveoy/pyopenapi_gen/commit/ef5d3656008417bbae17e6370e33e7b0c12cefd6))
|
|
10
|
+
|
|
11
|
+
BREAKING CHANGE: DataclassSerializer now correctly maps Python snake_case field names to API
|
|
12
|
+
camelCase field names when serializing BaseSchema instances.
|
|
13
|
+
|
|
14
|
+
Problem: DataclassSerializer.serialize() was using Python field names (snake_case) directly as
|
|
15
|
+
dictionary keys, ignoring the field name mappings defined in
|
|
16
|
+
BaseSchema.Meta.key_transform_with_load. This caused API requests to send incorrect field names,
|
|
17
|
+
breaking communication with camelCase APIs.
|
|
18
|
+
|
|
19
|
+
Solution: Modified DataclassSerializer._serialize_with_tracking() to detect BaseSchema instances and
|
|
20
|
+
use their to_dict() method, which properly handles field name mapping. Falls back to original
|
|
21
|
+
behavior for plain dataclasses to maintain backwards compatibility.
|
|
22
|
+
|
|
23
|
+
Changes: - Updated DataclassSerializer._serialize_with_tracking() in core/utils.py - Added 5
|
|
24
|
+
comprehensive unit tests for field mapping scenarios - Added 2 end-to-end integration tests with
|
|
25
|
+
business_swagger.json - All existing tests pass with 88.42% coverage (exceeds 85% requirement)
|
|
26
|
+
|
|
27
|
+
Example: Before: {"data_source_id": "123"} ❌
|
|
28
|
+
|
|
29
|
+
After: {"dataSourceId": "123"} ✅
|
|
30
|
+
|
|
31
|
+
Fixes critical bug affecting all generated clients with camelCase APIs.
|
|
32
|
+
|
|
33
|
+
### Chores
|
|
34
|
+
|
|
35
|
+
- **release**: Sync __init__.py version [skip ci]
|
|
36
|
+
([`446d496`](https://github.com/mindhiveoy/pyopenapi_gen/commit/446d4963a1e8428da7b6476088104aac599dae8f))
|
|
37
|
+
|
|
38
|
+
### Breaking Changes
|
|
39
|
+
|
|
40
|
+
- **serialization**: Dataclassserializer now correctly maps Python snake_case field names to API
|
|
41
|
+
camelCase field names when serializing BaseSchema instances.
|
|
42
|
+
|
|
43
|
+
|
|
4
44
|
## v0.20.1 (2025-10-21)
|
|
5
45
|
|
|
6
46
|
### Bug Fixes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyopenapi-gen
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.21.0
|
|
4
4
|
Summary: Modern, async-first Python client generator for OpenAPI specifications with advanced cycle detection and unified type resolution
|
|
5
5
|
Project-URL: Homepage, https://github.com/your-org/pyopenapi-gen
|
|
6
6
|
Project-URL: Documentation, https://github.com/your-org/pyopenapi-gen/blob/main/README.md
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<?xml version="1.0" ?>
|
|
2
|
-
<coverage version="7.10.7" timestamp="
|
|
2
|
+
<coverage version="7.10.7" timestamp="1761233680435" lines-valid="6518" lines-covered="5763" line-rate="0.8842" branches-covered="0" branches-valid="0" branch-rate="0" complexity="0">
|
|
3
3
|
<!-- Generated by coverage.py: https://coverage.readthedocs.io/en/7.10.7 -->
|
|
4
4
|
<!-- Based on https://raw.githubusercontent.com/cobertura/web/master/htdocs/xml/coverage-04.dtd -->
|
|
5
5
|
<sources>
|
|
@@ -652,7 +652,7 @@
|
|
|
652
652
|
</class>
|
|
653
653
|
</classes>
|
|
654
654
|
</package>
|
|
655
|
-
<package name="pyopenapi_gen.core" line-rate="0.
|
|
655
|
+
<package name="pyopenapi_gen.core" line-rate="0.836" branch-rate="0" complexity="0">
|
|
656
656
|
<classes>
|
|
657
657
|
<class name="__init__.py" filename="pyopenapi_gen/core/__init__.py" complexity="0" line-rate="1" branch-rate="0">
|
|
658
658
|
<methods/>
|
|
@@ -1053,7 +1053,7 @@
|
|
|
1053
1053
|
<line number="69" hits="1"/>
|
|
1054
1054
|
</lines>
|
|
1055
1055
|
</class>
|
|
1056
|
-
<class name="utils.py" filename="pyopenapi_gen/core/utils.py" complexity="0" line-rate="0.
|
|
1056
|
+
<class name="utils.py" filename="pyopenapi_gen/core/utils.py" complexity="0" line-rate="0.9392" branch-rate="0">
|
|
1057
1057
|
<methods/>
|
|
1058
1058
|
<lines>
|
|
1059
1059
|
<line number="6" hits="1"/>
|
|
@@ -1173,59 +1173,70 @@
|
|
|
1173
1173
|
<line number="313" hits="1"/>
|
|
1174
1174
|
<line number="320" hits="1"/>
|
|
1175
1175
|
<line number="321" hits="1"/>
|
|
1176
|
-
<line number="339" hits="1"/>
|
|
1177
1176
|
<line number="341" hits="1"/>
|
|
1178
|
-
<line number="
|
|
1177
|
+
<line number="343" hits="1"/>
|
|
1179
1178
|
<line number="344" hits="1"/>
|
|
1180
|
-
<line number="
|
|
1181
|
-
<line number="
|
|
1182
|
-
<line number="
|
|
1183
|
-
<line number="
|
|
1179
|
+
<line number="346" hits="1"/>
|
|
1180
|
+
<line number="349" hits="1"/>
|
|
1181
|
+
<line number="350" hits="1"/>
|
|
1182
|
+
<line number="353" hits="1"/>
|
|
1184
1183
|
<line number="354" hits="1"/>
|
|
1185
|
-
<line number="
|
|
1186
|
-
<line number="
|
|
1187
|
-
<line number="
|
|
1188
|
-
<line number="
|
|
1189
|
-
<line number="
|
|
1190
|
-
<line number="
|
|
1191
|
-
<line number="
|
|
1192
|
-
<line number="368" hits="1"/>
|
|
1193
|
-
<line number="369" hits="1"/>
|
|
1184
|
+
<line number="356" hits="1"/>
|
|
1185
|
+
<line number="357" hits="1"/>
|
|
1186
|
+
<line number="358" hits="0"/>
|
|
1187
|
+
<line number="361" hits="1"/>
|
|
1188
|
+
<line number="362" hits="1"/>
|
|
1189
|
+
<line number="365" hits="1"/>
|
|
1190
|
+
<line number="366" hits="1"/>
|
|
1194
1191
|
<line number="370" hits="1"/>
|
|
1195
1192
|
<line number="371" hits="1"/>
|
|
1196
1193
|
<line number="372" hits="1"/>
|
|
1197
1194
|
<line number="374" hits="1"/>
|
|
1198
|
-
<line number="375" hits="1"/>
|
|
1199
1195
|
<line number="376" hits="1"/>
|
|
1200
1196
|
<line number="377" hits="1"/>
|
|
1201
1197
|
<line number="378" hits="1"/>
|
|
1198
|
+
<line number="379" hits="1"/>
|
|
1202
1199
|
<line number="380" hits="1"/>
|
|
1200
|
+
<line number="381" hits="1"/>
|
|
1203
1201
|
<line number="383" hits="1"/>
|
|
1204
|
-
<line number="
|
|
1202
|
+
<line number="386" hits="1"/>
|
|
1205
1203
|
<line number="387" hits="1"/>
|
|
1206
1204
|
<line number="388" hits="1"/>
|
|
1207
1205
|
<line number="389" hits="1"/>
|
|
1208
1206
|
<line number="390" hits="1"/>
|
|
1209
1207
|
<line number="391" hits="1"/>
|
|
1210
|
-
<line number="392" hits="1"/>
|
|
1211
1208
|
<line number="393" hits="1"/>
|
|
1209
|
+
<line number="394" hits="1"/>
|
|
1210
|
+
<line number="395" hits="1"/>
|
|
1212
1211
|
<line number="396" hits="1"/>
|
|
1213
1212
|
<line number="397" hits="1"/>
|
|
1214
|
-
<line number="
|
|
1213
|
+
<line number="399" hits="1"/>
|
|
1215
1214
|
<line number="402" hits="1"/>
|
|
1216
1215
|
<line number="403" hits="1"/>
|
|
1217
|
-
<line number="404" hits="1"/>
|
|
1218
|
-
<line number="405" hits="1"/>
|
|
1219
1216
|
<line number="406" hits="1"/>
|
|
1220
1217
|
<line number="407" hits="1"/>
|
|
1221
1218
|
<line number="408" hits="1"/>
|
|
1222
1219
|
<line number="409" hits="1"/>
|
|
1223
1220
|
<line number="410" hits="1"/>
|
|
1224
1221
|
<line number="411" hits="1"/>
|
|
1225
|
-
<line number="
|
|
1226
|
-
<line number="
|
|
1227
|
-
<line number="
|
|
1228
|
-
<line number="419" hits="
|
|
1222
|
+
<line number="412" hits="1"/>
|
|
1223
|
+
<line number="415" hits="1"/>
|
|
1224
|
+
<line number="416" hits="1"/>
|
|
1225
|
+
<line number="419" hits="1"/>
|
|
1226
|
+
<line number="421" hits="1"/>
|
|
1227
|
+
<line number="422" hits="1"/>
|
|
1228
|
+
<line number="423" hits="1"/>
|
|
1229
|
+
<line number="424" hits="1"/>
|
|
1230
|
+
<line number="425" hits="1"/>
|
|
1231
|
+
<line number="426" hits="1"/>
|
|
1232
|
+
<line number="427" hits="1"/>
|
|
1233
|
+
<line number="428" hits="1"/>
|
|
1234
|
+
<line number="429" hits="1"/>
|
|
1235
|
+
<line number="430" hits="1"/>
|
|
1236
|
+
<line number="432" hits="1"/>
|
|
1237
|
+
<line number="435" hits="0"/>
|
|
1238
|
+
<line number="436" hits="0"/>
|
|
1239
|
+
<line number="438" hits="0"/>
|
|
1229
1240
|
</lines>
|
|
1230
1241
|
</class>
|
|
1231
1242
|
<class name="warning_collector.py" filename="pyopenapi_gen/core/warning_collector.py" complexity="0" line-rate="1" branch-rate="0">
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pyopenapi-gen"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.21.0"
|
|
8
8
|
description = "Modern, async-first Python client generator for OpenAPI specifications with advanced cycle detection and unified type resolution"
|
|
9
9
|
authors = [{ name = "Mindhive Oy", email = "contact@mindhive.fi" }]
|
|
10
10
|
maintainers = [{ name = "Ville Venäläinen | Mindhive Oy", email = "ville@mindhive.fi" }]
|
|
@@ -140,7 +140,7 @@ pyopenapi-gen = "pyopenapi_gen.cli:app"
|
|
|
140
140
|
|
|
141
141
|
[tool.commitizen]
|
|
142
142
|
name = "cz_conventional_commits"
|
|
143
|
-
version = "0.
|
|
143
|
+
version = "0.21.0"
|
|
144
144
|
version_files = [
|
|
145
145
|
"pyproject.toml:version",
|
|
146
146
|
"src/pyopenapi_gen/__init__.py:__version__"
|
|
@@ -50,7 +50,7 @@ __all__ = [
|
|
|
50
50
|
]
|
|
51
51
|
|
|
52
52
|
# Semantic version of the generator core – automatically managed by semantic-release.
|
|
53
|
-
__version__: str = "0.
|
|
53
|
+
__version__: str = "0.21.0"
|
|
54
54
|
|
|
55
55
|
# ---------------------------------------------------------------------------
|
|
56
56
|
# Lazy-loading and autocompletion support (This part remains)
|
|
@@ -328,10 +328,12 @@ class DataclassSerializer:
|
|
|
328
328
|
The serialized object with dataclasses converted to dictionaries.
|
|
329
329
|
|
|
330
330
|
Handles:
|
|
331
|
-
-
|
|
331
|
+
- BaseSchema instances: Uses to_dict() method with field name mapping (e.g., snake_case -> camelCase)
|
|
332
|
+
- Regular dataclass instances: Converted to dictionaries using field names
|
|
332
333
|
- Lists: Recursively serialize each item
|
|
333
334
|
- Dictionaries: Recursively serialize values
|
|
334
335
|
- datetime: Convert to ISO format string
|
|
336
|
+
- Enums: Convert to their value
|
|
335
337
|
- Primitives: Return unchanged
|
|
336
338
|
- None values: Excluded from output
|
|
337
339
|
"""
|
|
@@ -363,7 +365,24 @@ class DataclassSerializer:
|
|
|
363
365
|
if isinstance(obj, Enum) and not isinstance(obj, type):
|
|
364
366
|
return obj.value
|
|
365
367
|
|
|
366
|
-
# Handle
|
|
368
|
+
# Handle BaseSchema instances (respects field name mappings)
|
|
369
|
+
# Check for BaseSchema by looking for both to_dict and _get_field_mappings methods
|
|
370
|
+
if hasattr(obj, "to_dict") and hasattr(obj, "_get_field_mappings") and callable(obj.to_dict):
|
|
371
|
+
visited.add(obj_id)
|
|
372
|
+
try:
|
|
373
|
+
# Use BaseSchema's to_dict() which handles field name mapping
|
|
374
|
+
result_dict = obj.to_dict(exclude_none=True)
|
|
375
|
+
# Recursively serialize nested objects in the result
|
|
376
|
+
serialized_result = {}
|
|
377
|
+
for key, value in result_dict.items():
|
|
378
|
+
serialized_value = DataclassSerializer._serialize_with_tracking(value, visited)
|
|
379
|
+
if serialized_value is not None:
|
|
380
|
+
serialized_result[key] = serialized_value
|
|
381
|
+
return serialized_result
|
|
382
|
+
finally:
|
|
383
|
+
visited.discard(obj_id)
|
|
384
|
+
|
|
385
|
+
# Handle regular dataclass instances (no field mapping)
|
|
367
386
|
if dataclasses.is_dataclass(obj) and not isinstance(obj, type):
|
|
368
387
|
visited.add(obj_id)
|
|
369
388
|
try:
|
|
@@ -357,3 +357,248 @@ def test_import_collector_sibling_directory_import() -> None:
|
|
|
357
357
|
assert "from ..models.bar import Baz" in statements
|
|
358
358
|
for stmt in statements:
|
|
359
359
|
assert "/" not in stmt, f"Slash found in import statement: {stmt}"
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
# DataclassSerializer Tests
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def test_dataclass_serializer__baseschema_with_mappings__uses_api_field_names() -> None:
|
|
366
|
+
"""Test DataclassSerializer respects BaseSchema field name mappings.
|
|
367
|
+
|
|
368
|
+
Scenario:
|
|
369
|
+
- Create a BaseSchema dataclass with field mappings (snake_case -> camelCase)
|
|
370
|
+
- Serialize the instance using DataclassSerializer.serialize()
|
|
371
|
+
|
|
372
|
+
Expected Outcome:
|
|
373
|
+
- The serialized dictionary should use API field names (camelCase)
|
|
374
|
+
- Should not use Python field names (snake_case)
|
|
375
|
+
"""
|
|
376
|
+
# Arrange
|
|
377
|
+
from dataclasses import dataclass
|
|
378
|
+
|
|
379
|
+
from pyopenapi_gen.core.schemas import BaseSchema
|
|
380
|
+
from pyopenapi_gen.core.utils import DataclassSerializer
|
|
381
|
+
|
|
382
|
+
@dataclass
|
|
383
|
+
class DocumentUpdate(BaseSchema):
|
|
384
|
+
"""Test schema with field mappings."""
|
|
385
|
+
|
|
386
|
+
data_source_id: str
|
|
387
|
+
mime_type: str | None = None
|
|
388
|
+
last_modified: str | None = None
|
|
389
|
+
|
|
390
|
+
class Meta:
|
|
391
|
+
"""Field mappings."""
|
|
392
|
+
|
|
393
|
+
key_transform_with_load = {
|
|
394
|
+
"dataSourceId": "data_source_id",
|
|
395
|
+
"mimeType": "mime_type",
|
|
396
|
+
"lastModified": "last_modified",
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
obj = DocumentUpdate(data_source_id="source-123", mime_type="text/html", last_modified="2024-10-23")
|
|
400
|
+
|
|
401
|
+
# Act
|
|
402
|
+
result = DataclassSerializer.serialize(obj)
|
|
403
|
+
|
|
404
|
+
# Assert
|
|
405
|
+
assert isinstance(result, dict)
|
|
406
|
+
assert "dataSourceId" in result # camelCase (API name)
|
|
407
|
+
assert "mimeType" in result
|
|
408
|
+
assert "lastModified" in result
|
|
409
|
+
assert "data_source_id" not in result # snake_case (Python name) should NOT be present
|
|
410
|
+
assert "mime_type" not in result
|
|
411
|
+
assert "last_modified" not in result
|
|
412
|
+
assert result["dataSourceId"] == "source-123"
|
|
413
|
+
assert result["mimeType"] == "text/html"
|
|
414
|
+
assert result["lastModified"] == "2024-10-23"
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
def test_dataclass_serializer__nested_baseschema__maps_recursively() -> None:
|
|
418
|
+
"""Test DataclassSerializer handles nested BaseSchema instances with field mappings.
|
|
419
|
+
|
|
420
|
+
Scenario:
|
|
421
|
+
- Create nested BaseSchema dataclasses with field mappings
|
|
422
|
+
- Serialize the parent instance
|
|
423
|
+
|
|
424
|
+
Expected Outcome:
|
|
425
|
+
- Both parent and nested objects should use API field names
|
|
426
|
+
- Nested field mappings should be respected recursively
|
|
427
|
+
"""
|
|
428
|
+
# Arrange
|
|
429
|
+
from dataclasses import dataclass
|
|
430
|
+
|
|
431
|
+
from pyopenapi_gen.core.schemas import BaseSchema
|
|
432
|
+
from pyopenapi_gen.core.utils import DataclassSerializer
|
|
433
|
+
|
|
434
|
+
@dataclass
|
|
435
|
+
class Address(BaseSchema):
|
|
436
|
+
"""Nested schema with field mappings."""
|
|
437
|
+
|
|
438
|
+
street_name: str
|
|
439
|
+
postal_code: str
|
|
440
|
+
|
|
441
|
+
class Meta:
|
|
442
|
+
"""Field mappings."""
|
|
443
|
+
|
|
444
|
+
key_transform_with_load = {"streetName": "street_name", "postalCode": "postal_code"}
|
|
445
|
+
|
|
446
|
+
@dataclass
|
|
447
|
+
class User(BaseSchema):
|
|
448
|
+
"""Parent schema with field mappings."""
|
|
449
|
+
|
|
450
|
+
user_id: str
|
|
451
|
+
full_name: str
|
|
452
|
+
home_address: Address
|
|
453
|
+
|
|
454
|
+
class Meta:
|
|
455
|
+
"""Field mappings."""
|
|
456
|
+
|
|
457
|
+
key_transform_with_load = {
|
|
458
|
+
"userId": "user_id",
|
|
459
|
+
"fullName": "full_name",
|
|
460
|
+
"homeAddress": "home_address",
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
address = Address(street_name="Main St", postal_code="12345")
|
|
464
|
+
user = User(user_id="user-123", full_name="John Doe", home_address=address)
|
|
465
|
+
|
|
466
|
+
# Act
|
|
467
|
+
result = DataclassSerializer.serialize(user)
|
|
468
|
+
|
|
469
|
+
# Assert
|
|
470
|
+
assert isinstance(result, dict)
|
|
471
|
+
assert "userId" in result
|
|
472
|
+
assert "fullName" in result
|
|
473
|
+
assert "homeAddress" in result
|
|
474
|
+
assert isinstance(result["homeAddress"], dict)
|
|
475
|
+
assert "streetName" in result["homeAddress"]
|
|
476
|
+
assert "postalCode" in result["homeAddress"]
|
|
477
|
+
assert result["userId"] == "user-123"
|
|
478
|
+
assert result["homeAddress"]["streetName"] == "Main St"
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
def test_dataclass_serializer__plain_dataclass__still_works() -> None:
|
|
482
|
+
"""Test DataclassSerializer backwards compatibility with plain dataclasses.
|
|
483
|
+
|
|
484
|
+
Scenario:
|
|
485
|
+
- Create a plain dataclass (not BaseSchema)
|
|
486
|
+
- Serialize the instance
|
|
487
|
+
|
|
488
|
+
Expected Outcome:
|
|
489
|
+
- Should use field.name directly (no field mapping)
|
|
490
|
+
- Backwards compatibility maintained
|
|
491
|
+
"""
|
|
492
|
+
# Arrange
|
|
493
|
+
from dataclasses import dataclass
|
|
494
|
+
|
|
495
|
+
from pyopenapi_gen.core.utils import DataclassSerializer
|
|
496
|
+
|
|
497
|
+
@dataclass
|
|
498
|
+
class PlainModel:
|
|
499
|
+
"""Plain dataclass without BaseSchema."""
|
|
500
|
+
|
|
501
|
+
field_one: str
|
|
502
|
+
field_two: int
|
|
503
|
+
|
|
504
|
+
obj = PlainModel(field_one="value", field_two=42)
|
|
505
|
+
|
|
506
|
+
# Act
|
|
507
|
+
result = DataclassSerializer.serialize(obj)
|
|
508
|
+
|
|
509
|
+
# Assert
|
|
510
|
+
assert isinstance(result, dict)
|
|
511
|
+
assert "field_one" in result
|
|
512
|
+
assert "field_two" in result
|
|
513
|
+
assert result["field_one"] == "value"
|
|
514
|
+
assert result["field_two"] == 42
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
def test_dataclass_serializer__exclude_none_handling() -> None:
|
|
518
|
+
"""Test DataclassSerializer excludes None values when using BaseSchema.
|
|
519
|
+
|
|
520
|
+
Scenario:
|
|
521
|
+
- Create a BaseSchema instance with some None values
|
|
522
|
+
- Serialize the instance
|
|
523
|
+
|
|
524
|
+
Expected Outcome:
|
|
525
|
+
- None values should be excluded from the result
|
|
526
|
+
- Non-None values should be present with correct field names
|
|
527
|
+
"""
|
|
528
|
+
# Arrange
|
|
529
|
+
from dataclasses import dataclass
|
|
530
|
+
|
|
531
|
+
from pyopenapi_gen.core.schemas import BaseSchema
|
|
532
|
+
from pyopenapi_gen.core.utils import DataclassSerializer
|
|
533
|
+
|
|
534
|
+
@dataclass
|
|
535
|
+
class OptionalFields(BaseSchema):
|
|
536
|
+
"""Schema with optional fields."""
|
|
537
|
+
|
|
538
|
+
required_field: str
|
|
539
|
+
optional_field: str | None = None
|
|
540
|
+
another_optional: str | None = None
|
|
541
|
+
|
|
542
|
+
class Meta:
|
|
543
|
+
"""Field mappings."""
|
|
544
|
+
|
|
545
|
+
key_transform_with_load = {
|
|
546
|
+
"requiredField": "required_field",
|
|
547
|
+
"optionalField": "optional_field",
|
|
548
|
+
"anotherOptional": "another_optional",
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
obj = OptionalFields(required_field="present", optional_field=None, another_optional=None)
|
|
552
|
+
|
|
553
|
+
# Act
|
|
554
|
+
result = DataclassSerializer.serialize(obj)
|
|
555
|
+
|
|
556
|
+
# Assert
|
|
557
|
+
assert isinstance(result, dict)
|
|
558
|
+
assert "requiredField" in result
|
|
559
|
+
assert "optionalField" not in result # None values excluded
|
|
560
|
+
assert "anotherOptional" not in result
|
|
561
|
+
assert result["requiredField"] == "present"
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
def test_dataclass_serializer__list_of_baseschema__maps_all_items() -> None:
|
|
565
|
+
"""Test DataclassSerializer handles lists of BaseSchema instances.
|
|
566
|
+
|
|
567
|
+
Scenario:
|
|
568
|
+
- Create a list of BaseSchema instances with field mappings
|
|
569
|
+
- Serialize the list
|
|
570
|
+
|
|
571
|
+
Expected Outcome:
|
|
572
|
+
- Each item in the list should have field names mapped
|
|
573
|
+
- All items should use API field names
|
|
574
|
+
"""
|
|
575
|
+
# Arrange
|
|
576
|
+
from dataclasses import dataclass
|
|
577
|
+
|
|
578
|
+
from pyopenapi_gen.core.schemas import BaseSchema
|
|
579
|
+
from pyopenapi_gen.core.utils import DataclassSerializer
|
|
580
|
+
|
|
581
|
+
@dataclass
|
|
582
|
+
class Item(BaseSchema):
|
|
583
|
+
"""Schema with field mappings."""
|
|
584
|
+
|
|
585
|
+
item_id: str
|
|
586
|
+
item_name: str
|
|
587
|
+
|
|
588
|
+
class Meta:
|
|
589
|
+
"""Field mappings."""
|
|
590
|
+
|
|
591
|
+
key_transform_with_load = {"itemId": "item_id", "itemName": "item_name"}
|
|
592
|
+
|
|
593
|
+
items = [Item(item_id="1", item_name="First"), Item(item_id="2", item_name="Second")]
|
|
594
|
+
|
|
595
|
+
# Act
|
|
596
|
+
result = DataclassSerializer.serialize(items)
|
|
597
|
+
|
|
598
|
+
# Assert
|
|
599
|
+
assert isinstance(result, list)
|
|
600
|
+
assert len(result) == 2
|
|
601
|
+
assert all("itemId" in item for item in result)
|
|
602
|
+
assert all("itemName" in item for item in result)
|
|
603
|
+
assert result[0]["itemId"] == "1"
|
|
604
|
+
assert result[1]["itemName"] == "Second"
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"""Integration test for field name mapping in generated clients.
|
|
2
|
+
|
|
3
|
+
Tests that the DataclassSerializer correctly maps Python snake_case field names
|
|
4
|
+
to API camelCase field names when serializing request bodies.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import tempfile
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
import pytest
|
|
11
|
+
|
|
12
|
+
from pyopenapi_gen import generate_client
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_field_mapping__business_swagger_updateDocument__serializes_to_camelCase() -> None:
|
|
16
|
+
"""End-to-end test for field mapping with business_swagger.json.
|
|
17
|
+
|
|
18
|
+
Scenario:
|
|
19
|
+
- Generate client from business_swagger.json
|
|
20
|
+
- Import DocumentUpdate model from generated client
|
|
21
|
+
- Create instance with snake_case attributes
|
|
22
|
+
- Serialize using DataclassSerializer
|
|
23
|
+
- Verify output uses camelCase API field names
|
|
24
|
+
|
|
25
|
+
Expected Outcome:
|
|
26
|
+
- Generated code should include field mappings in Meta class
|
|
27
|
+
- DataclassSerializer should use camelCase keys (not snake_case)
|
|
28
|
+
- Validates the fix for the field mapping bug
|
|
29
|
+
"""
|
|
30
|
+
# Arrange - Generate client in temporary directory
|
|
31
|
+
spec_path = "input/business_swagger.json"
|
|
32
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
33
|
+
project_root = Path(tmpdir)
|
|
34
|
+
|
|
35
|
+
# Act - Generate client
|
|
36
|
+
generate_client(
|
|
37
|
+
spec_path=spec_path,
|
|
38
|
+
project_root=str(project_root),
|
|
39
|
+
output_package="businessapi",
|
|
40
|
+
force=True,
|
|
41
|
+
no_postprocess=True, # Skip formatting for speed
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
# Import generated model dynamically
|
|
45
|
+
import sys
|
|
46
|
+
|
|
47
|
+
sys.path.insert(0, str(project_root))
|
|
48
|
+
from businessapi.core.utils import DataclassSerializer
|
|
49
|
+
from businessapi.models.document_update import DocumentUpdate
|
|
50
|
+
|
|
51
|
+
# Create instance with snake_case attributes
|
|
52
|
+
doc_update = DocumentUpdate(
|
|
53
|
+
url="https://example.com/doc",
|
|
54
|
+
data_source_id="source-123",
|
|
55
|
+
mime_type="text/html",
|
|
56
|
+
last_modified="2024-10-23T12:00:00Z",
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# Act - Serialize using DataclassSerializer
|
|
60
|
+
result = DataclassSerializer.serialize(doc_update)
|
|
61
|
+
|
|
62
|
+
# Assert - Should use camelCase API field names
|
|
63
|
+
assert isinstance(result, dict)
|
|
64
|
+
|
|
65
|
+
# Check that camelCase keys are present
|
|
66
|
+
assert "dataSourceId" in result, "Should use camelCase 'dataSourceId' (API field name)"
|
|
67
|
+
assert "mimeType" in result, "Should use camelCase 'mimeType' (API field name)"
|
|
68
|
+
assert "lastModified" in result, "Should use camelCase 'lastModified' (API field name)"
|
|
69
|
+
|
|
70
|
+
# Check that snake_case keys are NOT present
|
|
71
|
+
assert "data_source_id" not in result, "Should NOT use snake_case 'data_source_id' (Python field name)"
|
|
72
|
+
assert "mime_type" not in result, "Should NOT use snake_case 'mime_type' (Python field name)"
|
|
73
|
+
assert "last_modified" not in result, "Should NOT use snake_case 'last_modified' (Python field name)"
|
|
74
|
+
|
|
75
|
+
# Verify values are correct
|
|
76
|
+
assert result["dataSourceId"] == "source-123"
|
|
77
|
+
assert result["mimeType"] == "text/html"
|
|
78
|
+
assert result["lastModified"] == "2024-10-23T12:00:00Z"
|
|
79
|
+
|
|
80
|
+
# Cleanup
|
|
81
|
+
sys.path.remove(str(project_root))
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_field_mapping__roundtrip__preserves_data() -> None:
|
|
85
|
+
"""Test that field mapping works correctly in both directions.
|
|
86
|
+
|
|
87
|
+
Scenario:
|
|
88
|
+
- Generate client from business_swagger.json
|
|
89
|
+
- Create DocumentUpdate from dict with camelCase keys (API response)
|
|
90
|
+
- Serialize back to dict
|
|
91
|
+
- Verify field names are preserved
|
|
92
|
+
|
|
93
|
+
Expected Outcome:
|
|
94
|
+
- from_dict should accept camelCase and map to snake_case attributes
|
|
95
|
+
- DataclassSerializer should map snake_case attributes back to camelCase
|
|
96
|
+
- Data should be preserved through the roundtrip
|
|
97
|
+
"""
|
|
98
|
+
# Arrange - Generate client in temporary directory
|
|
99
|
+
spec_path = "input/business_swagger.json"
|
|
100
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
101
|
+
project_root = Path(tmpdir)
|
|
102
|
+
|
|
103
|
+
# Generate client
|
|
104
|
+
generate_client(
|
|
105
|
+
spec_path=spec_path,
|
|
106
|
+
project_root=str(project_root),
|
|
107
|
+
output_package="businessapi",
|
|
108
|
+
force=True,
|
|
109
|
+
no_postprocess=True, # Skip formatting for speed
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
# Import generated model dynamically
|
|
113
|
+
import sys
|
|
114
|
+
|
|
115
|
+
sys.path.insert(0, str(project_root))
|
|
116
|
+
from businessapi.core.utils import DataclassSerializer
|
|
117
|
+
from businessapi.models.document_update import DocumentUpdate
|
|
118
|
+
|
|
119
|
+
# Act - Create from API response (camelCase)
|
|
120
|
+
api_response = {
|
|
121
|
+
"url": "https://example.com/doc",
|
|
122
|
+
"dataSourceId": "source-456",
|
|
123
|
+
"mimeType": "application/json",
|
|
124
|
+
"lastModified": "2024-10-23T15:30:00Z",
|
|
125
|
+
}
|
|
126
|
+
doc_update = DocumentUpdate.from_dict(api_response)
|
|
127
|
+
|
|
128
|
+
# Verify attributes are accessible via snake_case
|
|
129
|
+
assert doc_update.data_source_id == "source-456"
|
|
130
|
+
assert doc_update.mime_type == "application/json"
|
|
131
|
+
assert doc_update.last_modified == "2024-10-23T15:30:00Z"
|
|
132
|
+
|
|
133
|
+
# Act - Serialize back (should produce camelCase)
|
|
134
|
+
result = DataclassSerializer.serialize(doc_update)
|
|
135
|
+
|
|
136
|
+
# Assert - Should match original API response format
|
|
137
|
+
assert result["dataSourceId"] == "source-456"
|
|
138
|
+
assert result["mimeType"] == "application/json"
|
|
139
|
+
assert result["lastModified"] == "2024-10-23T15:30:00Z"
|
|
140
|
+
|
|
141
|
+
# Cleanup
|
|
142
|
+
sys.path.remove(str(project_root))
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
if __name__ == "__main__":
|
|
146
|
+
pytest.main([__file__, "-v"])
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|