Merged
requested to merge github/fork/spacether/adds_python_experimental_dynamic_base_classes into master
Created by: spacether
Adds python-experimental which uses dynamic base classes
In this PR:
- when deserializing payloads or creating Schema/Model instances the new instance will dynamically inherit from the correct Schema classes (models) and immutable python primitive classes (frozendict, tuple, str, list, Decimal, bytes, NoneClass, BoolClass)
This is amore robust composition implementation than the standard python generator implementation because:
- any level of inline schema is supported (one won't need to extract composed schemas into components anymore)
- schema property names are used so there's no mutation of data when converting to python style variable names because python style variable names are not used
Separate endpoint parameters are made for for query, path, cookie, and header params because each of those types can contain parameters with colliding names. So having them separate allows us to preserve the original key naming in our inputs to the endpoint. One can see this working in the parameter_collisions endpoint that I added.
Examples
- Map payload w/ Object component Schema (Model): An object model Box will inherit from Box, DictSchema, and frozendict
- Map payload w/ untyped composed component Schema (Model): For a composed model Triangle oneOf includes ScaleneTriangle, the resulting instance will inherit from Triangle + ScaleneTriangle, DictSchema, and frozendict
Caviats:
- None, True, and False are singletons in python so if they were used, one would not be able to subclass them. I made a new Singleton class and used it to make base classes for those cases, NoneClass and BoolClass
- the Singleton subclasses NoneClass + BoolClass will allow me to generate type hints for XSchema + y where y is a base class like (frozendict, tuple, str, Decimal, NoneClass, BoolClass. XSchema is a class like Triangle or DictSchema.
- the Singleton class is used to generate Enum options, and enum classes can inherit from eachother. This is necessary when EnumA and EnumB are both validated for the same payload.
- when BinarySchemas are used, either files or bytes may be stored in them. When files are stored, the schema instance will be mutable. When bytes are stored, the schema instance is immutable.
Feature Additions:
- uniqueItems validations added
- model instances are now immutable. This was done to prevent users from adding/mutating model instance data after validations have already been run. For example if a composed discriminated model is made and a user was allowed to change the value of the discriminated variable className, that would invalidate the validation that was done on that instance.
- type inheritance is clear and deterministic, when multiple overlapping types are validated. Note: int/float/number are all stored as Decimal instances
- object type inherits from frozendict
- array type inherits from tuple
- null type inherits from NoneClass
- boolean type inherits from BoolClass
- number/int/float types inherit from Decimal
- string/date/datetime types inherit from str
- string Decimals/uuid etc can also inherit from str in the future
- openapi 3.0 parameter styles implemented (except for deepObject)
- api import time reduced; one endpoint or api can now be imported and used without loading others
- multiple content types supported in endpoint request bodies, see add_pet
- accept header content types added as an input, user overrides are also allowed for the accept header
- multiple response content types are allowed, see find_pets_by_status
Long Term Benefits of Using Dynamic Base Classes + This New structure
- the code is more maintainable: casting is done in one place, validation is much clearer
- any depth of inline models can be supported. Many filed issues + bugs occur because users are trying to use composed inline schemas.
- model definition is shorter
- This will save us from traversing composed schema relationship trees to figure out if NullableString/XSchema is allowed to contain None/True/False/some other primitive type
- This supports complicated use cases like
- allOf -OneOfComposedSchema. OneOfComposedSchema oneOf: -schemaA -schemaB
- enums containing multiple types
- a schema that combines two schemas where the type definition of a variable is different
str, number
vsnumber
- composed schemas with type constraints
- The number of times that validations and type conversion are done is reduced to only when the data is ingested and the schemas are validated
- once to make the dynamic class from a payload, then skip it when assigning object values at init time
- no need to run this code again when sending an instance of NullableString to the server
- when one has an instance of a composed schema, one will know what schemas passed validation for that payload
- simplicity of storing payload info once. Previously, dict properties were stored in multiple instances inside a composed schema, now property values are only stored in one place inside a single model dict
- make it easy to incorporate multiple types in openapi >= 3.1.0 by using StrBase, ListBase etc classes
- this is already implemented for nullable schemas, like str + nullable=True, see NullableString
PR checklist
-
Read the contribution guidelines. -
Pull Request title clearly describes the work in the pull request and Pull Request description provides details about how to validate the work. Missing information here may result in delayed response from the community. -
Run the following to build the project and update samples: ./mvnw clean package ./bin/generate-samples.sh ./bin/utils/export_docs_generators.sh
./bin/generate-samples.sh bin/configs/java*
. For Windows users, please run the script in Git BASH. -
File the PR against the correct branch: master
(5.3.0),6.0.x
-
If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request.