├── .github └── workflows │ └── pythonpackage.yml ├── .gitignore ├── .travis.yml ├── CHANGES.txt ├── LICENSE ├── Makefile ├── README.md ├── VERSION ├── examples ├── 00simple │ ├── Makefile │ ├── legacy_person.py │ ├── main.py │ ├── person.py │ └── person.yaml ├── 01ref │ ├── Makefile │ ├── main.py │ ├── person.py │ └── person.yaml ├── 02default │ ├── Makefile │ ├── definition.py │ ├── definition.yaml │ └── main.py ├── 03additionals │ ├── Makefile │ ├── legacy_score.py │ ├── main.py │ ├── score.py │ └── score.yaml ├── 04primitives │ ├── Makefile │ ├── legacy_schema.py │ ├── main.py │ ├── schema.py │ └── swagger.yaml ├── 05uber │ ├── Makefile │ ├── main.py │ ├── uber.yaml │ └── uberschema.py ├── 06github │ ├── Makefile │ ├── github.yaml │ ├── githubschema.py │ └── main.py ├── gen.mk ├── x00custom │ ├── Makefile │ ├── __init__.py │ ├── _custom.py │ ├── main.py │ ├── myschema.py │ ├── person.py │ └── person.yaml └── x01separated │ ├── Makefile │ ├── petstore-extend.json │ ├── petstore.json │ ├── petstore │ ├── Error.py │ ├── Pet.py │ ├── __init__.py │ └── _lazy.py │ ├── petstore_extend │ ├── Cat.py │ ├── Dog.py │ ├── Error.py │ ├── Owner.py │ ├── Pet.py │ ├── Shop.py │ ├── __init__.py │ └── _lazy.py │ ├── team-and-user.json │ └── team_and_user │ ├── __init__.py │ ├── _lazy.py │ ├── team.py │ └── user.py ├── setup.cfg ├── setup.py ├── shapes ├── Makefile ├── README.md ├── codegen_candidates.py ├── defs │ ├── 00simple-object.py │ ├── 01simple-primitive.py │ ├── 02nesting.py │ ├── 03self-nesting.py │ ├── 05additionalProperties-without-properties.py │ ├── 07additionalProperties-with-bool.py │ └── __init__.py ├── expected │ ├── 00simple-object.py │ ├── 01simple-primitive.py │ ├── 02nesting.py │ ├── 03self-nesting.py │ ├── 04inline-nesting.py │ ├── 05additionalProperties-without-properties.py │ ├── 06additionalProperties.py │ ├── 07additionalProperties-with-bool.py │ ├── 08additionalProperties-without-properties-object.json │ └── __init__.py ├── gen-candidates.py ├── gen.py ├── requirements.txt ├── test_schema.py ├── try.py ├── v2 │ ├── 00simple-object.json │ ├── 01simple-primitive.json │ ├── 02nesting.json │ ├── 03self-nesting.json │ ├── 04inline-nesting.json │ ├── 05additionalProperties-without-properties.json │ ├── 06additionalProperties.json │ ├── 07additionalProperties-with-bool.json │ └── 08additionalProperties-without-properties-object.json └── v3 │ ├── 00simple-object.json │ ├── 01simple-primitive.json │ ├── 02nesting.json │ ├── 03self-nesting.json │ ├── 04inline-nesting.json │ ├── 05additionalProperties-without-properties.json │ ├── 06additionalProperties.json │ ├── 07additionalProperties-with-bool.json │ └── 08additionalProperties-without-properties-object.json └── swagger_marshmallow_codegen ├── __init__.py ├── __main__.py ├── _version.py ├── cmd.py ├── codegen ├── __init__.py ├── config.py ├── context.py ├── v2 │ ├── __init__.py │ ├── accessor.py │ └── codegen.py └── v3 │ ├── __init__.py │ ├── accessor.py │ └── codegen.py ├── constants.py ├── dispatcher.py ├── driver.py ├── evil.py ├── fields.py ├── langhelpers.py ├── lifting.py ├── loading.py ├── resolver.py ├── schema ├── __init__.py ├── extra.py └── legacy.py ├── tests ├── __init__.py ├── codegen_candidates.py ├── legacy_dst │ ├── 00additional.py │ ├── 00allOf.py │ ├── 00allOf2.py │ ├── 00commit.py │ ├── 00default.py │ ├── 00emojis.py │ ├── 00empty.py │ ├── 00enum.py │ ├── 00items.py │ ├── 00length.py │ ├── 00list_with_options.py │ ├── 00maximum.py │ ├── 00nullable.py │ ├── 00paths.py │ ├── 00patternProperties.py │ ├── 00person.py │ ├── 00primitiveapi.py │ ├── 00readonly.py │ ├── 00regex.py │ ├── 00reserved.py │ ├── 00stat.py │ ├── 00typearray.py │ ├── 00x_marshmallow_name.py │ ├── 01additional.py │ ├── 01allOf2.py │ ├── 01commit.py │ ├── 01empty.py │ ├── 01paths.py │ ├── 01person.py │ ├── 01x_marshmallow_name.py │ ├── 02additional.py │ ├── 02allOf2.py │ ├── 02paths.py │ ├── 02person.py │ ├── 03additional.py │ ├── 03paths.py │ ├── 03person.py │ ├── 04person.py │ └── 05person.py ├── legacy_src │ ├── 00additional.yaml │ ├── 00allOf.yaml │ ├── 00allOf2.yaml │ ├── 00commit.yaml │ ├── 00default.yaml │ ├── 00emojis.yaml │ ├── 00empty.yaml │ ├── 00enum.yaml │ ├── 00items.yaml │ ├── 00length.yaml │ ├── 00list_with_options.yaml │ ├── 00maximum.yaml │ ├── 00nullable.yaml │ ├── 00paths.yaml │ ├── 00patternProperties.yaml │ ├── 00person.yaml │ ├── 00primitiveapi.yaml │ ├── 00readonly.yaml │ ├── 00regex.yaml │ ├── 00reserved.yaml │ ├── 00stat.yaml │ ├── 00typearray.yaml │ ├── 00x_marshmallow_name.yaml │ ├── 01additional.yaml │ ├── 01allOf2.yaml │ ├── 01commit.yaml │ ├── 01empty.yaml │ ├── 01paths.yaml │ ├── 01person.yaml │ ├── 01x_marshmallow_name.yaml │ ├── 02additional.yaml │ ├── 02allOf2.yaml │ ├── 02empty.yaml │ ├── 02paths.yaml │ ├── 02person.yaml │ ├── 03additional.yaml │ ├── 03paths.yaml │ ├── 03person.yaml │ ├── 04person.yaml │ └── 05person.yaml ├── test_codegen.py ├── test_codegen_legacy.py ├── test_legacy_schema.py ├── test_lifting.py ├── test_schema.py ├── test_validate.py ├── testing.py ├── v2dst ├── v2src ├── v3dst └── v3src └── validate.py /.github/workflows/pythonpackage.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | 4 | name: Python package 5 | 6 | on: 7 | push: [] 8 | pull_request: 9 | types: [opened, synchronize] 10 | schedule: 11 | - cron: '0 0 * * 6' 12 | 13 | jobs: 14 | build: 15 | 16 | runs-on: ubuntu-latest 17 | strategy: 18 | matrix: 19 | python-version: [3.7, 3.8, 3.9] 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | - name: Set up Python ${{ matrix.python-version }} 24 | uses: actions/setup-python@v1 25 | with: 26 | python-version: ${{ matrix.python-version }} 27 | - name: Install make 28 | run: sudo apt-get install make 29 | - name: Install dependencies 30 | run: | 31 | python -m pip install --upgrade pip 32 | pip install -e .[dev,testing] 33 | - name: Test with pytest 34 | run: | 35 | make test 36 | - name: Lint with 37 | run: | 38 | make lint 39 | - name: Type check with mypy 40 | run: | 41 | make typing 42 | - name: run examples 43 | run: | 44 | make examples 45 | git diff 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *.eggs 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | bin/ 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | 26 | # Installer logs 27 | pip-log.txt 28 | pip-delete-this-directory.txt 29 | 30 | # Unit test / coverage reports 31 | htmlcov/ 32 | .tox/ 33 | .coverage 34 | .cache 35 | nosetests.xml 36 | coverage.xml 37 | 38 | # Translations 39 | *.mo 40 | 41 | # Mr Developer 42 | .mr.developer.cfg 43 | .project 44 | .pydevproject 45 | 46 | # Rope 47 | .ropeproject 48 | 49 | # Django stuff: 50 | *.log 51 | *.pot 52 | 53 | # Sphinx documentation 54 | docs/_build/ 55 | 56 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | sudo: false 3 | python: 4 | - "3.6" 5 | - "3.7" 6 | - "3.8-dev" # 3.8 development branch 7 | - "nightly" 8 | install: 9 | - pip install -e .[testing] 10 | - pip install bson 11 | script: 12 | - python setup.py test 13 | - make ci 14 | -------------------------------------------------------------------------------- /CHANGES.txt: -------------------------------------------------------------------------------- 1 | 0.6.5 2 | 3 | - remove pyyaml dependency (use ruamel.yaml via dictknife.loading >= 0.14 ) 4 | 5 | 0.6.4 6 | 7 | - bug fix for minItems,maxItems #97 8 | 9 | 0.6.3 10 | 11 | - support separated output partially 12 | 13 | 0.6.2 14 | 15 | - support nested additionalProperties 16 | - fix guessing OAS version detection 17 | 18 | 0.6.1 19 | 20 | - fix bug at additionalProperties 21 | 22 | 0.6.0 23 | 24 | - add `--strict-additional-properties` options 25 | - partially support OpenAPI v3 26 | - use AwareDatetime Field, for tzaware datetime 27 | 28 | 0.5.1 29 | 30 | - catch up magicalimport 0.8.1 31 | 32 | 0.5.0 33 | 34 | - marshmallow 3.0.x support 35 | - drop python 3.5 support 36 | - support additionalProperties for array 37 | 38 | 0.4.0 39 | 40 | - tiny refactoring codegen 41 | - typo for fields.DateTime 42 | - change PrimitiveValueSchema implementation 43 | 44 | 0.3.9 45 | 46 | - see x-nullable 47 | 48 | 0.3.7 49 | 50 | - support additionalProperties with object 51 | 52 | 0.3.6 53 | 54 | - fix required option in parameters section are ignored 55 | 56 | 0.3.5 57 | 58 | - fix ordered output with list field keyeords 59 | - add evil functions (xxx_modify_field, xxx_add_processor) 60 | - support primitive value's ref 61 | - support type = ["string","null"] 62 | - more allOf support 63 | 64 | 0.3.4 65 | 66 | - list fields dispatch is also customizable 67 | 68 | 0.3.3 69 | 70 | - support python's resrved word field name #12 71 | - support path.parameters #11 72 | 73 | 0.3.2 74 | 75 | - fix bug validation for array type 76 | 77 | 0.3.1 78 | 79 | - fix bug for lifting schema 80 | 81 | 0.3 82 | 83 | - support generating schema from `paths` 84 | 85 | 0.2 86 | 87 | - support allOf 88 | - support readOnly 89 | - customization 90 | 91 | 0.1 92 | 93 | - first release 94 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 podhmo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PKG := swagger_marshmallow_codegen 2 | SHELL := bash 3 | 4 | test: 5 | pytest -vv $(PKG) 6 | 7 | lint: 8 | flake8 ${PKG} --ignore=E501,E303,E203,W391,W503 9 | 10 | format: 11 | black ${PKG} --exclude=dst 12 | 13 | # TODO: typing 14 | typing: 15 | : 16 | 17 | define runT 18 | $(1) 19 | 20 | endef 21 | 22 | define findCandidatesT 23 | $(shell find ${1} -mindepth 2 -name Makefile | xargs -n 1 -I{} dirname {} | sort) 24 | endef 25 | 26 | WHERE ?= ./examples 27 | 28 | examples: 29 | python -m pip install bson 30 | $(foreach x,$(call findCandidatesT,$(WHERE)),$(call runT,OPTS=--logging=WARNING make --silent -C $(x))) 31 | .PHONY: examples 32 | 33 | # for travis 34 | ci: 35 | $(foreach x,$(call findCandidatesT,$(WHERE)),$(call runT,OPTS=--logging=WARNING make --silent -C $(x))) 36 | test -z `git diff examples/` || (echo "*********DIFF*********" && git diff examples/ && exit 2) 37 | .PHONY: ci 38 | 39 | _find-candidates: 40 | echo $(call findCandidatesT,$(WHERE)) 41 | 42 | 43 | #### for pypi ######################## 44 | get-version: 45 | python -c 'import swagger_marshmallow_codegen as m; print(m.__version__)' 46 | 47 | build: 48 | # pip install wheel 49 | python setup.py sdist bdist_wheel 50 | 51 | upload: 52 | # pip install twine keyrings.alt 53 | twine check dist/swagger-marshmallow-codegen-$(shell make -s get-version)* 54 | twine upload dist/swagger*marshmallow*codegen-$(shell make -s get-version)*.gz 55 | twine upload dist/swagger*marshmallow*codegen*$(shell make -s get-version)*.whl 56 | 57 | .PHONY: build upload 58 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.6.5 -------------------------------------------------------------------------------- /examples/00simple/Makefile: -------------------------------------------------------------------------------- 1 | default: new legacy 2 | 3 | new: 4 | SRC=person.yaml DST=person.py $(MAKE) -f ../gen.mk -C . 5 | 6 | legacy: 7 | OPTS="--logging=DEBUG --legacy" SRC=person.yaml DST=legacy_person.py $(MAKE) -f ../gen.mk -C . 8 | -------------------------------------------------------------------------------- /examples/00simple/legacy_person.py: -------------------------------------------------------------------------------- 1 | # this is auto-generated by swagger-marshmallow-codegen 2 | from __future__ import annotations 3 | from swagger_marshmallow_codegen.schema.legacy import LegacySchema 4 | from marshmallow import fields 5 | 6 | 7 | class Person(LegacySchema): 8 | name = fields.String(required=True) 9 | age = fields.Integer() 10 | -------------------------------------------------------------------------------- /examples/00simple/main.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from person import Person 3 | 4 | 5 | if __name__ == "__main__": 6 | try: 7 | d = {"name": "foo", "age": "20"} 8 | data = Person().load(d) 9 | print("ok", data) 10 | except Exception as e: 11 | print("ng", e) 12 | sys.exit(-1) 13 | -------------------------------------------------------------------------------- /examples/00simple/person.py: -------------------------------------------------------------------------------- 1 | # this is auto-generated by swagger-marshmallow-codegen 2 | from __future__ import annotations 3 | from marshmallow import ( 4 | Schema, 5 | fields, 6 | ) 7 | 8 | 9 | class Person(Schema): 10 | name = fields.String(required=True) 11 | age = fields.Integer() 12 | -------------------------------------------------------------------------------- /examples/00simple/person.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | person: 3 | type: object 4 | properties: 5 | name: 6 | type: string 7 | age: 8 | type: integer 9 | required: 10 | - name 11 | -------------------------------------------------------------------------------- /examples/01ref/Makefile: -------------------------------------------------------------------------------- 1 | default: 2 | SRC=person.yaml DST=person.py $(MAKE) -f ../gen.mk -C . 3 | -------------------------------------------------------------------------------- /examples/01ref/main.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from person import Person 3 | 4 | 5 | if __name__ == "__main__": 6 | try: 7 | d = {"name": "foo", "age": "20"} 8 | data = Person().load(d) 9 | print("ok", data) 10 | except Exception as e: 11 | print("ng", e) 12 | sys.exit(-1) 13 | try: 14 | d = { 15 | "name": "foo", 16 | "age": "20", 17 | "father": {"name": "a", "age": "40"}, 18 | "mother": {"name": "b", "age": "40"}, 19 | } 20 | data = Person().load(d) 21 | print("ok", data) 22 | except Exception as e: 23 | print("ng", e) 24 | sys.exit(-1) 25 | -------------------------------------------------------------------------------- /examples/01ref/person.py: -------------------------------------------------------------------------------- 1 | # this is auto-generated by swagger-marshmallow-codegen 2 | from __future__ import annotations 3 | from marshmallow import ( 4 | Schema, 5 | fields, 6 | ) 7 | 8 | 9 | class Person(Schema): 10 | name = fields.String(required=True, description='name of something') 11 | age = fields.Integer(description='age') 12 | father = fields.Nested(lambda: Father()) 13 | mother = fields.Nested(lambda: Mother()) 14 | skills = fields.List(fields.Nested(lambda: Skill())) 15 | 16 | 17 | class Father(Person): 18 | pass 19 | 20 | 21 | class Mother(Person): 22 | pass 23 | 24 | 25 | class Skill(Schema): 26 | name = fields.String(required=True) 27 | -------------------------------------------------------------------------------- /examples/01ref/person.yaml: -------------------------------------------------------------------------------- 1 | # recursive $ref 2 | definitions: 3 | name: 4 | type: string 5 | description: "name of something" 6 | age: 7 | type: integer 8 | description: "age" 9 | father: 10 | $ref: "#/definitions/person" 11 | mother: 12 | $ref: "#/definitions/person" 13 | person: 14 | type: object 15 | properties: 16 | name: 17 | $ref: "#/definitions/name" 18 | age: 19 | $ref: "#/definitions/age" 20 | father: 21 | $ref: "#/definitions/father" 22 | mother: 23 | $ref: "#/definitions/mother" 24 | skills: 25 | type: array 26 | items: 27 | $ref: "#/definitions/skill" 28 | required: 29 | - name 30 | skill: 31 | type: object 32 | properties: 33 | name: 34 | type: string 35 | required: 36 | - name 37 | -------------------------------------------------------------------------------- /examples/02default/Makefile: -------------------------------------------------------------------------------- 1 | default: 2 | SRC=definition.yaml DST=definition.py $(MAKE) -f ../gen.mk -C . 3 | -------------------------------------------------------------------------------- /examples/02default/definition.py: -------------------------------------------------------------------------------- 1 | # this is auto-generated by swagger-marshmallow-codegen 2 | from __future__ import annotations 3 | from marshmallow import ( 4 | Schema, 5 | fields, 6 | ) 7 | import datetime 8 | from collections import OrderedDict 9 | from marshmallow.validate import ( 10 | Length, 11 | Regexp, 12 | OneOf, 13 | ) 14 | from swagger_marshmallow_codegen.validate import ( 15 | Range, 16 | ItemsRange, 17 | Unique, 18 | MultipleOf, 19 | ) 20 | import re 21 | 22 | 23 | class Default(Schema): 24 | string = fields.String(missing=lambda: 'default') 25 | integer = fields.Integer(missing=lambda: 10) 26 | boolean = fields.Boolean(missing=lambda: True) 27 | date = fields.Date(missing=lambda: datetime.date(2000, 1, 1)) 28 | datetime = fields.AwareDateTime(missing=lambda: datetime.datetime(2000, 1, 1, 1, 1, 1, tzinfo=datetime.timezone.utc)) 29 | object = fields.Nested(lambda: DefaultObject(), missing=lambda: OrderedDict([('name', 'foo'), ('age', 20)])) 30 | array = fields.List(fields.Integer(), missing=lambda: [1, 2, 3]) 31 | 32 | 33 | class DefaultObject(Schema): 34 | name = fields.String(missing=lambda: 'foo') 35 | age = fields.Integer(missing=lambda: 20) 36 | 37 | 38 | class Length_validation(Schema): 39 | s0 = fields.String() 40 | s1 = fields.String(validate=[Length(min=None, max=10, equal=None)]) 41 | s2 = fields.String(validate=[Length(min=5, max=None, equal=None)]) 42 | s3 = fields.String(validate=[Length(min=5, max=10, equal=None)]) 43 | 44 | 45 | class Maximum_validation(Schema): 46 | n0 = fields.Number(validate=[Range(min=None, max=100, min_inclusive=True, max_inclusive=True)]) 47 | n1 = fields.Number(validate=[Range(min=None, max=100, min_inclusive=True, max_inclusive=False)]) 48 | n2 = fields.Number(validate=[Range(min=None, max=100, min_inclusive=True, max_inclusive=True)]) 49 | m0 = fields.Number(validate=[Range(min=100, max=None, min_inclusive=True, max_inclusive=True)]) 50 | m1 = fields.Number(validate=[Range(min=100, max=None, min_inclusive=False, max_inclusive=True)]) 51 | m2 = fields.Number(validate=[Range(min=100, max=None, min_inclusive=True, max_inclusive=True)]) 52 | 53 | 54 | class Regex_validation(Schema): 55 | team = fields.String(validate=[Regexp(regex=re.compile('team[1-9][0-9]+'))]) 56 | team2 = fields.String(validate=[Length(min=None, max=10, equal=None), Regexp(regex=re.compile('team[1-9][0-9]+'))]) 57 | 58 | 59 | class Array_validation(Schema): 60 | nums = fields.List(fields.Integer(), validate=[ItemsRange(min=1, max=10, min_inclusive=True, max_inclusive=True), Unique()]) 61 | 62 | 63 | class Enum_validation(Schema): 64 | name = fields.String(required=True) 65 | money = fields.Integer(validate=[OneOf(choices=[1, 5, 10, 50, 100, 500, 1000, 5000, 10000], labels=[])]) 66 | deposit = fields.Integer(validate=[MultipleOf(n=10000)]) 67 | color = fields.String(required=True, validate=[OneOf(choices=['R', 'G', 'B'], labels=[])]) 68 | -------------------------------------------------------------------------------- /examples/02default/definition.yaml: -------------------------------------------------------------------------------- 1 | # todo: gentle example 2 | definitions: 3 | default: 4 | properties: 5 | string: 6 | type: string 7 | default: "default" 8 | integer: 9 | type: integer 10 | default: 10 11 | boolean: 12 | type: boolean 13 | default: true 14 | date: 15 | type: string 16 | format: date 17 | default: 2000-01-01 18 | datetime: 19 | type: string 20 | format: date-time 21 | default: 2000-01-01T01:01:01Z 22 | object: 23 | type: object 24 | properties: 25 | name: 26 | type: string 27 | default: foo 28 | age: 29 | type: integer 30 | default: 20 31 | default: 32 | name: foo 33 | age: 20 34 | array: 35 | type: array 36 | items: 37 | type: integer 38 | default: 39 | - 1 40 | - 2 41 | - 3 42 | 43 | length-validation: 44 | type: object 45 | properties: 46 | s0: 47 | type: string 48 | s1: 49 | type: string 50 | maxLength: 10 51 | s2: 52 | type: string 53 | minLength: 5 54 | s3: 55 | type: string 56 | maxLength: 10 57 | minLength: 5 58 | 59 | maximum-validation: 60 | type: object 61 | properties: 62 | n0: 63 | type: number 64 | maximum: 100 65 | n1: 66 | type: number 67 | maximum: 100 68 | exclusiveMaximum: true 69 | n2: 70 | type: number 71 | maximum: 100 72 | exclusiveMaximum: false 73 | m0: 74 | type: number 75 | minimum: 100 76 | m1: 77 | type: number 78 | minimum: 100 79 | exclusiveMinimum: true 80 | m2: 81 | type: number 82 | minimum: 100 83 | exclusiveMinimum: false 84 | 85 | regex-validation: 86 | type: object 87 | properties: 88 | team: 89 | type: string 90 | pattern: team[1-9][0-9]+ 91 | team2: 92 | type: string 93 | pattern: team[1-9][0-9]+ 94 | maxLength: 10 95 | 96 | array-validation: 97 | type: object 98 | properties: 99 | nums: 100 | type: array 101 | items: 102 | type: integer 103 | maxItems: 10 104 | minItems: 1 105 | uniqueItems: true 106 | 107 | color: 108 | type: string 109 | enum: 110 | - R 111 | - G 112 | - B 113 | yen: 114 | type: integer 115 | enum: 116 | - 1 117 | - 5 118 | - 10 119 | - 50 120 | - 100 121 | - 500 122 | - 1000 123 | - 5000 124 | - 10000 125 | huge-yen: 126 | type: integer 127 | multipleOf: 10000 128 | enum-validation: 129 | type: object 130 | required: 131 | - name 132 | - color 133 | properties: 134 | name: 135 | type: string 136 | money: 137 | $ref: "#/definitions/yen" 138 | deposit: 139 | $ref: "#/definitions/huge-yen" 140 | color: 141 | $ref: "#/definitions/color" 142 | -------------------------------------------------------------------------------- /examples/02default/main.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from definition import Default 3 | 4 | 5 | if __name__ == "__main__": 6 | try: 7 | d = {} 8 | data = Default().load(d) 9 | print("ok", data) 10 | print(Default().dump(data)) 11 | except Exception as e: 12 | print("ng", e) 13 | sys.exit(-1) 14 | -------------------------------------------------------------------------------- /examples/03additionals/Makefile: -------------------------------------------------------------------------------- 1 | default: new legacy 2 | 3 | new: 4 | SRC=score.yaml DST=score.py $(MAKE) -f ../gen.mk -C . 5 | 6 | legacy: 7 | OPTS="--logging=DEBUG --legacy" SRC=score.yaml DST=legacy_score.py $(MAKE) -f ../gen.mk -C . 8 | -------------------------------------------------------------------------------- /examples/03additionals/legacy_score.py: -------------------------------------------------------------------------------- 1 | # this is auto-generated by swagger-marshmallow-codegen 2 | from __future__ import annotations 3 | from swagger_marshmallow_codegen.schema.legacy import ( 4 | LegacySchema, 5 | AdditionalPropertiesSchema, 6 | ) 7 | from marshmallow import fields 8 | 9 | 10 | class Score(AdditionalPropertiesSchema): 11 | name = fields.String(required=True) 12 | 13 | class Meta: 14 | additional_field = fields.Integer() 15 | -------------------------------------------------------------------------------- /examples/03additionals/main.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from score import Score 3 | 4 | 5 | if __name__ == "__main__": 6 | try: 7 | d = {"name": "foo", "age": "20"} 8 | data = Score().load(d) 9 | print("ok", data) 10 | except Exception as e: 11 | print("ng", e) 12 | sys.exit(-1) 13 | -------------------------------------------------------------------------------- /examples/03additionals/score.py: -------------------------------------------------------------------------------- 1 | # this is auto-generated by swagger-marshmallow-codegen 2 | from __future__ import annotations 3 | from marshmallow import ( 4 | Schema, 5 | fields, 6 | ) 7 | from swagger_marshmallow_codegen.schema import AdditionalPropertiesSchema 8 | 9 | 10 | class Score(AdditionalPropertiesSchema): 11 | name = fields.String(required=True) 12 | 13 | class Meta: 14 | additional_field = fields.Integer() 15 | -------------------------------------------------------------------------------- /examples/03additionals/score.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | score: 3 | type: object 4 | properties: 5 | name: 6 | type: string 7 | required: 8 | - name 9 | additionalProperties: 10 | type: integer 11 | -------------------------------------------------------------------------------- /examples/04primitives/Makefile: -------------------------------------------------------------------------------- 1 | default: new legacy 2 | 3 | new: 4 | SRC=swagger.yaml DST=schema.py $(MAKE) -f ../gen.mk -C . 5 | 6 | legacy: 7 | OPTS="--legacy" SRC=swagger.yaml DST=legacy_schema.py $(MAKE) -f ../gen.mk -C . 8 | -------------------------------------------------------------------------------- /examples/04primitives/legacy_schema.py: -------------------------------------------------------------------------------- 1 | # this is auto-generated by swagger-marshmallow-codegen 2 | from __future__ import annotations 3 | from swagger_marshmallow_codegen.schema.legacy import ( 4 | LegacySchema, 5 | PrimitiveValueSchema, 6 | ) 7 | from marshmallow import fields 8 | 9 | 10 | class IdsInput: 11 | class Get: 12 | pass 13 | 14 | 15 | 16 | class IdsIdInput: 17 | class Get: 18 | pass 19 | 20 | 21 | 22 | class IdsOutput: 23 | class Get200(PrimitiveValueSchema): 24 | class schema_class(LegacySchema): 25 | value = fields.List(fields.Integer()) 26 | 27 | 28 | 29 | 30 | class IdsIdOutput: 31 | class Get200(PrimitiveValueSchema): 32 | class schema_class(LegacySchema): 33 | value = fields.Integer() 34 | -------------------------------------------------------------------------------- /examples/04primitives/main.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from schema import IdsIdOutput, IdsOutput 3 | 4 | 5 | if __name__ == "__main__": 6 | try: 7 | d = "1" 8 | data = IdsIdOutput.Get200().load(d) 9 | print("ok", data) 10 | except Exception as e: 11 | print("ng", e) 12 | sys.exit(-1) 13 | 14 | try: 15 | d = ["1", "2", "3"] 16 | data = IdsOutput.Get200().load(d) 17 | print("ok", data) 18 | except Exception as e: 19 | print("ng", e) 20 | sys.exit(-1) 21 | -------------------------------------------------------------------------------- /examples/04primitives/schema.py: -------------------------------------------------------------------------------- 1 | # this is auto-generated by swagger-marshmallow-codegen 2 | from __future__ import annotations 3 | from marshmallow import ( 4 | Schema, 5 | fields, 6 | ) 7 | from swagger_marshmallow_codegen.schema import PrimitiveValueSchema 8 | 9 | 10 | class IdsInput: 11 | class Get: 12 | pass 13 | 14 | 15 | 16 | class IdsIdInput: 17 | class Get: 18 | pass 19 | 20 | 21 | 22 | class IdsOutput: 23 | class Get200(PrimitiveValueSchema): 24 | class schema_class(Schema): 25 | value = fields.List(fields.Integer()) 26 | 27 | 28 | 29 | 30 | class IdsIdOutput: 31 | class Get200(PrimitiveValueSchema): 32 | class schema_class(Schema): 33 | value = fields.Integer() 34 | -------------------------------------------------------------------------------- /examples/04primitives/swagger.yaml: -------------------------------------------------------------------------------- 1 | paths: 2 | /ids: 3 | get: 4 | responses: 5 | 200: 6 | schema: 7 | type: array 8 | items: 9 | type: integer 10 | /ids/{id}: 11 | get: 12 | paramters: 13 | - in: path 14 | name: id 15 | required: true 16 | responses: 17 | 200: 18 | schema: 19 | type: integer 20 | -------------------------------------------------------------------------------- /examples/05uber/Makefile: -------------------------------------------------------------------------------- 1 | default: 2 | SRC=uber.yaml DST=uberschema.py $(MAKE) -f ../gen.mk -C . 3 | 4 | fetch: 5 | wget https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v2.0/yaml/uber.yaml -O uber.yaml 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/05uber/main.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from uberschema import ProductList 3 | from uberschema import Error 4 | 5 | if __name__ == "__main__": 6 | try: 7 | d = { 8 | "products": [ 9 | dict(product_id="x1", description="first", display_name="1st", capacity="4"), 10 | dict( 11 | product_id="x2", 12 | description="second", 13 | display_name="2nd", 14 | capacity="5", 15 | image="http://example.jp/img/notfound.jpg" 16 | ), 17 | ] 18 | } 19 | data = ProductList().load(d) 20 | print("ok", data) 21 | except Exception as e: 22 | print("ng", e) 23 | sys.exit(-1) 24 | 25 | print("----------------------------------------") 26 | print(Error().dump({"code": "400", "message": "bad request", "fields_": "*fields(dump to)*"})) 27 | -------------------------------------------------------------------------------- /examples/06github/Makefile: -------------------------------------------------------------------------------- 1 | default: 2 | SRC=github.yaml DST=githubschema.py $(MAKE) -f ../gen.mk -C . 3 | 4 | fetch: 5 | wget https://api.apis.guru/v2/specs/github.com/v3/swagger.yaml -O github.yaml 6 | gsed -i "s/type: *'null'/type: object/g;" github.yaml 7 | -------------------------------------------------------------------------------- /examples/06github/main.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import json 3 | from githubschema import Repo, Emojis 4 | 5 | 6 | d = json.loads("""{ 7 | "clone_url": "https://github.com/octocat/Hello-World.git", 8 | "created_at": "2011-01-26T19:01:12Z", 9 | "description": "This your first repo!", 10 | "fork": false, 11 | "forks_count": 9, 12 | "full_name": "octocat/Hello-World", 13 | "git_url": "git://github.com/octocat/Hello-World.git", 14 | "has_downloads": true, 15 | "has_issues": true, 16 | "has_wiki": true, 17 | "homepage": "https://github.com", 18 | "html_url": "https://github.com/octocat/Hello-World", 19 | "id": 1296269, 20 | "language": "", 21 | "mirror_url": "git://git.example.com/octocat/Hello-World", 22 | "name": "Hello-World", 23 | "open_issues_count": 0, 24 | "organization": { 25 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 26 | "gravatar_id": "somehexcode", 27 | "id": 1, 28 | "login": "octocat", 29 | "type": "Organization", 30 | "url": "https://api.github.com/users/octocat" 31 | }, 32 | "owner": { 33 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 34 | "gravatar_id": "somehexcode", 35 | "id": 1, 36 | "login": "octocat", 37 | "url": "https://api.github.com/users/octocat" 38 | }, 39 | "parent": { 40 | "clone_url": "https://github.com/octocat/Hello-World.git", 41 | "created_at": "2011-01-26T19:01:12Z", 42 | "description": "This your first repo!", 43 | "fork": true, 44 | "forks_count": 9, 45 | "full_name": "octocat/Hello-World", 46 | "git_url": "git://github.com/octocat/Hello-World.git", 47 | "homepage": "https://github.com", 48 | "html_url": "https://github.com/octocat/Hello-World", 49 | "id": 1296269, 50 | "language": "", 51 | "mirror_url": "git://git.example.com/octocat/Hello-World", 52 | "name": "Hello-World", 53 | "open_issues_count": 0, 54 | "owner": { 55 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 56 | "gravatar_id": "somehexcode", 57 | "id": 1, 58 | "login": "octocat", 59 | "url": "https://api.github.com/users/octocat" 60 | }, 61 | "private": false, 62 | "pushed_at": "2011-01-26T19:06:43Z", 63 | "size": 108, 64 | "ssh_url": "git@github.com:octocat/Hello-World.git", 65 | "svn_url": "https://svn.github.com/octocat/Hello-World", 66 | "updated_at": "2011-01-26T19:14:43Z", 67 | "url": "https://api.github.com/repos/octocat/Hello-World", 68 | "watchers_count": 80 69 | }, 70 | "private": false, 71 | "pushed_at": "2011-01-26T19:06:43Z", 72 | "size": 108, 73 | "source": { 74 | "clone_url": "https://github.com/octocat/Hello-World.git", 75 | "created_at": "2011-01-26T19:01:12Z", 76 | "description": "This your first repo!", 77 | "fork": true, 78 | "forks_count": 9, 79 | "full_name": "octocat/Hello-World", 80 | "git_url": "git://github.com/octocat/Hello-World.git", 81 | "homepage": "https://github.com", 82 | "html_url": "https://github.com/octocat/Hello-World", 83 | "id": 1296269, 84 | "language": "", 85 | "mirror_url": "git://git.example.com/octocat/Hello-World", 86 | "name": "Hello-World", 87 | "open_issues_count": 0, 88 | "owner": { 89 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 90 | "gravatar_id": "somehexcode", 91 | "id": 1, 92 | "login": "octocat", 93 | "url": "https://api.github.com/users/octocat" 94 | }, 95 | "private": false, 96 | "pushed_at": "2011-01-26T19:06:43Z", 97 | "size": 108, 98 | "ssh_url": "git@github.com:octocat/Hello-World.git", 99 | "svn_url": "https://svn.github.com/octocat/Hello-World", 100 | "updated_at": "2011-01-26T19:14:43Z", 101 | "url": "https://api.github.com/repos/octocat/Hello-World", 102 | "watchers_count": 80 103 | }, 104 | "ssh_url": "git@github.com:octocat/Hello-World.git", 105 | "svn_url": "https://svn.github.com/octocat/Hello-World", 106 | "updated_at": "2011-01-26T19:14:43Z", 107 | "url": "https://api.github.com/repos/octocat/Hello-World", 108 | "watchers_count": 80 109 | } 110 | """) 111 | 112 | 113 | if __name__ == "__main__": 114 | try: 115 | data = Repo().load(d) 116 | print("ok", json.dumps(data, indent=2)) 117 | except Exception as e: 118 | print("ng", e) 119 | sys.exit(-1) 120 | 121 | try: 122 | d = {"8ball": "hmm"} 123 | data = Emojis().load(d) 124 | print("ok", data, Emojis().dump(data)) 125 | except Exception as e: 126 | print("ng", e) 127 | sys.exit(-1) 128 | -------------------------------------------------------------------------------- /examples/gen.mk: -------------------------------------------------------------------------------- 1 | SRC ?= ./00simple/person.yaml 2 | DST ?= ./00simple/person.py 3 | OPTS ?= --logging=DEBUG 4 | default: 5 | swagger-marshmallow-codegen --strict-additional-properties --full ${OPTS} ${SRC} > ${DST} 6 | python `dirname ${DST}`/main.py 7 | -------------------------------------------------------------------------------- /examples/x00custom/Makefile: -------------------------------------------------------------------------------- 1 | SRC ?= ./person.yaml 2 | DST ?= ./person.py 3 | OPTS ?= "--logging=DEBUG" 4 | 5 | default: 6 | PYTHONPATH=. swagger-marshmallow-codegen ${OPTS} --driver=_custom.py:MyDriver ${SRC} > ${DST} 7 | PYTHONPATH=. python main.py 8 | -------------------------------------------------------------------------------- /examples/x00custom/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/podhmo/swagger-marshmallow-codegen/a21787d49f627c75241eb3a56a03b2d5107dfba4/examples/x00custom/__init__.py -------------------------------------------------------------------------------- /examples/x00custom/_custom.py: -------------------------------------------------------------------------------- 1 | import bson 2 | from swagger_marshmallow_codegen.driver import Driver 3 | from swagger_marshmallow_codegen.dispatcher import TYPE_MAP, Pair, FormatDispatcher, ReprWrapString 4 | 5 | 6 | class MyDispatcher(FormatDispatcher): 7 | type_map = { 8 | Pair(type="string", format="objectId"): "myschema:ObjectId", 9 | **TYPE_MAP, 10 | } 11 | 12 | def dispatch_default(self, c, value, field): 13 | if isinstance(value, bson.ObjectId) or field.get("format") == "objectId": 14 | c.import_("bson") 15 | return ReprWrapString("bson.{!r}".format(bson.ObjectId(value))) 16 | return super().dispatch_default(c, value, field) 17 | 18 | 19 | class MyDriver(Driver): 20 | codegen_factory = Driver.codegen_factory.override(schema_class_path="myschema:MySchema") 21 | dispatcher_factory = MyDispatcher 22 | -------------------------------------------------------------------------------- /examples/x00custom/main.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from person import Person 3 | 4 | 5 | if __name__ == "__main__": 6 | try: 7 | d = {"name": "foo", "age": "20"} 8 | data = Person().load(d) 9 | print("ok", data) 10 | except Exception as e: 11 | print("ng", e) 12 | sys.exit(-1) 13 | try: 14 | d = {"id": '5872c2e1c54d2d58294cbac6', "name": "bar", "age": "10"} 15 | data = Person().load(d) 16 | print("ok", data) 17 | except Exception as e: 18 | print("ng", e) 19 | sys.exit(-1) 20 | -------------------------------------------------------------------------------- /examples/x00custom/myschema.py: -------------------------------------------------------------------------------- 1 | import bson 2 | from marshmallow import Schema, fields 3 | 4 | 5 | class MySchema(Schema): 6 | class Meta: 7 | ordered = True 8 | strict = True 9 | 10 | 11 | class ObjectId(fields.String): 12 | default_error_messages = { 13 | 'invalid_object_id': 'Not a valid bson.ObjectId.', 14 | } 15 | 16 | def _validated(self, value): 17 | """Format the value or raise a :exc:`ValidationError` if an error occurs.""" 18 | if value is None: 19 | return None 20 | if isinstance(value, bson.ObjectId): 21 | return value 22 | try: 23 | return bson.ObjectId(value) 24 | except (ValueError, AttributeError): 25 | self.fail('invalid_object_id') 26 | 27 | def _deserialize(self, value, attr, data, **kwargs): 28 | return self._validated(value) 29 | 30 | def _serialize(self, value, attr, data, **kwargs): 31 | if not value: 32 | return value 33 | return str(value) 34 | -------------------------------------------------------------------------------- /examples/x00custom/person.py: -------------------------------------------------------------------------------- 1 | # this is auto-generated by swagger-marshmallow-codegen 2 | from __future__ import annotations 3 | from myschema import ( 4 | MySchema, 5 | ObjectId, 6 | ) 7 | from marshmallow import ( 8 | fields, 9 | INCLUDE, 10 | ) 11 | import bson 12 | 13 | 14 | class Person(MySchema): 15 | id = ObjectId(missing=lambda: bson.ObjectId('5872bad4c54d2d4e78b34c9d')) 16 | name = fields.String(required=True) 17 | age = fields.Integer() 18 | 19 | class Meta: 20 | unknown = INCLUDE 21 | -------------------------------------------------------------------------------- /examples/x00custom/person.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | person: 3 | type: object 4 | properties: 5 | id: 6 | type: string 7 | format: objectId 8 | default: 5872bad4c54d2d4e78b34c9d 9 | name: 10 | type: string 11 | age: 12 | type: integer 13 | required: 14 | - name 15 | -------------------------------------------------------------------------------- /examples/x01separated/Makefile: -------------------------------------------------------------------------------- 1 | GREP := $(shell which ggrep || which grep) 2 | 3 | default: clean 4 | $(MAKE) $(shell cat Makefile | $(GREP) -P '^[0-9]+:' | tr ':' ' ') 5 | 6 | clean: 7 | rm -rf petstore petstore_extend 8 | 9 | # simple 10 | 00: 11 | swagger-marshmallow-codegen petstore.json -d petstore 12 | 13 | # with simple relation 14 | 01: 15 | swagger-marshmallow-codegen petstore-extend.json -d petstore-extend 16 | 17 | # with mutual reference 18 | 02: 19 | swagger-marshmallow-codegen team-and-user.json -d team-and-user 20 | -------------------------------------------------------------------------------- /examples/x01separated/petstore-extend.json: -------------------------------------------------------------------------------- 1 | { 2 | "components": { 3 | "schemas": { 4 | "id": { 5 | "type": "string", 6 | "description": "ObjectId", 7 | "example": "541a9fd949b5ac0b00000000", 8 | "pattern": "[0-9a-f]{24}" 9 | }, 10 | "errorCategory": { 11 | "type": "string", 12 | "enum": ["API", "Internal", "Bug", "Human"] 13 | }, 14 | "errorCategories": { 15 | "type": "array", 16 | "items": { 17 | "$ref": "#/components/schemas/errorCategory" 18 | } 19 | }, 20 | "Error": { 21 | "properties": { 22 | "code": { 23 | "format": "int32", 24 | "type": "integer" 25 | }, 26 | "message": { 27 | "type": "string" 28 | }, 29 | "categoris": { 30 | "$ref": "#/components/schemas/errorCategories" 31 | } 32 | }, 33 | "required": [ 34 | "code", 35 | "message" 36 | ], 37 | "type": "object" 38 | }, 39 | "Pet": { 40 | "properties": { 41 | "father": { 42 | "$ref": "#/components/schemas/Pet" 43 | }, 44 | "id": { 45 | "$ref": "#/components/schemas/id" 46 | }, 47 | "mother": { 48 | "$ref": "#/components/schemas/Pet" 49 | }, 50 | "name": { 51 | "type": "string" 52 | }, 53 | "shop": { 54 | "$ref": "#/components/schemas/Shop" 55 | }, 56 | "owners": { 57 | "type": "array", 58 | "items": { 59 | "$ref": "#/components/schemas/Owner" 60 | } 61 | }, 62 | "tag": { 63 | "type": "string" 64 | } 65 | }, 66 | "required": [ 67 | "id", 68 | "name" 69 | ], 70 | "type": "object" 71 | }, 72 | "Pets": { 73 | "items": { 74 | "$ref": "#/components/schemas/Pet" 75 | }, 76 | "type": "array" 77 | }, 78 | "Cat": { 79 | "description": "A representation of a cat", 80 | "required": ["huntingSkill"], 81 | "allOf": [ 82 | {"$ref": "#/components/schemas/Pet"}, 83 | { 84 | "type": "object", 85 | "properties": { 86 | "huntingSkill": { 87 | "type": "string", 88 | "description": "The measured skill for hunting", 89 | "default": "lazy", 90 | "enum": ["clueless", "lazy", "adventurous", "aggressive"] 91 | } 92 | } 93 | } 94 | ] 95 | }, 96 | "Dog": { 97 | "description": "A representation of a dog", 98 | "required": ["packSize"], 99 | "allOf": [ 100 | {"$ref": "#/components/schemas/Pet"}, 101 | { 102 | "type": "object", 103 | "properties": { 104 | "packSize": { 105 | "type": "integer", 106 | "description": "the size of the pack the dog is from", 107 | "default": 0, 108 | "minimum": 0 109 | } 110 | } 111 | } 112 | ] 113 | }, 114 | "Shop": { 115 | "properties": { 116 | "id": { 117 | "$ref": "#/components/schemas/id" 118 | }, 119 | "name": { 120 | "type": "string" 121 | }, 122 | "description": { 123 | "properties": { 124 | "content": { 125 | "type": "string" 126 | }, 127 | "position": { 128 | "type": "string" 129 | } 130 | }, 131 | "type": "object" 132 | } 133 | }, 134 | "required": [ 135 | "id", 136 | "name" 137 | ], 138 | "type": "object" 139 | }, 140 | "Owner": { 141 | "properties": { 142 | "id": { 143 | "$ref": "#/components/schemas/id" 144 | }, 145 | "name": { 146 | "type": "string" 147 | }, 148 | "pets": { 149 | "$ref": "#/components/schemas/Pets" 150 | } 151 | }, 152 | "required": [ 153 | "id", 154 | "name" 155 | ], 156 | "type": "object" 157 | } 158 | } 159 | }, 160 | "openapi": "3.0.0" 161 | } 162 | -------------------------------------------------------------------------------- /examples/x01separated/petstore.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi": "3.0.0", 3 | "components": { 4 | "schemas": { 5 | "Pet": { 6 | "type": "object", 7 | "required": [ 8 | "id", 9 | "name" 10 | ], 11 | "properties": { 12 | "id": { 13 | "type": "integer", 14 | "format": "int64" 15 | }, 16 | "name": { 17 | "type": "string" 18 | }, 19 | "tag": { 20 | "type": "string" 21 | } 22 | } 23 | }, 24 | "Pets": { 25 | "type": "array", 26 | "items": { 27 | "$ref": "#/components/schemas/Pet" 28 | } 29 | }, 30 | "Error": { 31 | "type": "object", 32 | "required": [ 33 | "code", 34 | "message" 35 | ], 36 | "properties": { 37 | "code": { 38 | "type": "integer", 39 | "format": "int32" 40 | }, 41 | "message": { 42 | "type": "string" 43 | } 44 | } 45 | } 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /examples/x01separated/petstore/Error.py: -------------------------------------------------------------------------------- 1 | # this is auto-generated by swagger-marshmallow-codegen 2 | from __future__ import annotations 3 | from marshmallow import ( 4 | Schema, 5 | fields, 6 | INCLUDE, 7 | ) 8 | 9 | 10 | class Error(Schema): 11 | code = fields.Integer(required=True) 12 | message = fields.String(required=True) 13 | 14 | class Meta: 15 | unknown = INCLUDE 16 | -------------------------------------------------------------------------------- /examples/x01separated/petstore/Pet.py: -------------------------------------------------------------------------------- 1 | # this is auto-generated by swagger-marshmallow-codegen 2 | from __future__ import annotations 3 | from marshmallow import ( 4 | Schema, 5 | fields, 6 | INCLUDE, 7 | ) 8 | 9 | 10 | class Pet(Schema): 11 | id = fields.Integer(required=True) 12 | name = fields.String(required=True) 13 | tag = fields.String() 14 | 15 | class Meta: 16 | unknown = INCLUDE 17 | -------------------------------------------------------------------------------- /examples/x01separated/petstore/__init__.py: -------------------------------------------------------------------------------- 1 | from .Error import Error 2 | from .Pet import Pet 3 | -------------------------------------------------------------------------------- /examples/x01separated/petstore/_lazy.py: -------------------------------------------------------------------------------- 1 | def _useError(): 2 | from .Error import Error 3 | return Error 4 | 5 | 6 | def _usePet(): 7 | from .Pet import Pet 8 | return Pet 9 | -------------------------------------------------------------------------------- /examples/x01separated/petstore_extend/Cat.py: -------------------------------------------------------------------------------- 1 | # this is auto-generated by swagger-marshmallow-codegen 2 | from __future__ import annotations 3 | from marshmallow import ( 4 | Schema, 5 | fields, 6 | INCLUDE, 7 | ) 8 | from marshmallow.validate import OneOf 9 | from ._lazy import _usePet 10 | from .Pet import Pet 11 | 12 | 13 | class Cat(Pet): 14 | """A representation of a cat""" 15 | huntingSkill = fields.String(required=True, description='The measured skill for hunting', validate=[OneOf(choices=['clueless', 'lazy', 'adventurous', 'aggressive'], labels=[])]) 16 | 17 | class Meta: 18 | unknown = INCLUDE 19 | -------------------------------------------------------------------------------- /examples/x01separated/petstore_extend/Dog.py: -------------------------------------------------------------------------------- 1 | # this is auto-generated by swagger-marshmallow-codegen 2 | from __future__ import annotations 3 | from marshmallow import ( 4 | Schema, 5 | fields, 6 | INCLUDE, 7 | ) 8 | from swagger_marshmallow_codegen.validate import Range 9 | from ._lazy import _usePet 10 | from .Pet import Pet 11 | 12 | 13 | class Dog(Pet): 14 | """A representation of a dog""" 15 | packSize = fields.Integer(required=True, description='the size of the pack the dog is from', validate=[Range(min=0, max=None, min_inclusive=True, max_inclusive=True)]) 16 | 17 | class Meta: 18 | unknown = INCLUDE 19 | -------------------------------------------------------------------------------- /examples/x01separated/petstore_extend/Error.py: -------------------------------------------------------------------------------- 1 | # this is auto-generated by swagger-marshmallow-codegen 2 | from __future__ import annotations 3 | from marshmallow import ( 4 | Schema, 5 | fields, 6 | INCLUDE, 7 | ) 8 | from marshmallow.validate import OneOf 9 | 10 | 11 | class Error(Schema): 12 | code = fields.Integer(required=True) 13 | message = fields.String(required=True) 14 | categoris = fields.List(fields.String(validate=[OneOf(choices=['API', 'Internal', 'Bug', 'Human'], labels=[])])) 15 | 16 | class Meta: 17 | unknown = INCLUDE 18 | -------------------------------------------------------------------------------- /examples/x01separated/petstore_extend/Owner.py: -------------------------------------------------------------------------------- 1 | # this is auto-generated by swagger-marshmallow-codegen 2 | from __future__ import annotations 3 | from marshmallow import ( 4 | Schema, 5 | fields, 6 | INCLUDE, 7 | ) 8 | import re 9 | from marshmallow.validate import Regexp 10 | from ._lazy import _usePet 11 | 12 | 13 | class Owner(Schema): 14 | id = fields.String(required=True, description='ObjectId', validate=[Regexp(regex=re.compile('[0-9a-f]{24}'))]) 15 | name = fields.String(required=True) 16 | pets = fields.List(fields.Nested(_usePet)) 17 | 18 | class Meta: 19 | unknown = INCLUDE 20 | -------------------------------------------------------------------------------- /examples/x01separated/petstore_extend/Pet.py: -------------------------------------------------------------------------------- 1 | # this is auto-generated by swagger-marshmallow-codegen 2 | from __future__ import annotations 3 | from marshmallow import ( 4 | Schema, 5 | fields, 6 | INCLUDE, 7 | ) 8 | import re 9 | from marshmallow.validate import Regexp 10 | from ._lazy import ( 11 | _useShop, 12 | _useOwner, 13 | ) 14 | 15 | 16 | class Pet(Schema): 17 | father = fields.Nested(lambda: Pet()) 18 | id = fields.String(required=True, description='ObjectId', validate=[Regexp(regex=re.compile('[0-9a-f]{24}'))]) 19 | mother = fields.Nested(lambda: Pet()) 20 | name = fields.String(required=True) 21 | shop = fields.Nested(_useShop) 22 | owners = fields.List(fields.Nested(_useOwner)) 23 | tag = fields.String() 24 | 25 | class Meta: 26 | unknown = INCLUDE 27 | -------------------------------------------------------------------------------- /examples/x01separated/petstore_extend/Shop.py: -------------------------------------------------------------------------------- 1 | # this is auto-generated by swagger-marshmallow-codegen 2 | from __future__ import annotations 3 | from marshmallow import ( 4 | Schema, 5 | fields, 6 | INCLUDE, 7 | ) 8 | import re 9 | from marshmallow.validate import Regexp 10 | 11 | 12 | class Shop(Schema): 13 | id = fields.String(required=True, description='ObjectId', validate=[Regexp(regex=re.compile('[0-9a-f]{24}'))]) 14 | name = fields.String(required=True) 15 | description = fields.Nested(lambda: ShopDescription()) 16 | 17 | class Meta: 18 | unknown = INCLUDE 19 | 20 | 21 | 22 | class ShopDescription(Schema): 23 | content = fields.String() 24 | position = fields.String() 25 | 26 | class Meta: 27 | unknown = INCLUDE 28 | -------------------------------------------------------------------------------- /examples/x01separated/petstore_extend/__init__.py: -------------------------------------------------------------------------------- 1 | from .Cat import Cat 2 | from .Dog import Dog 3 | from .Error import Error 4 | from .Owner import Owner 5 | from .Pet import Pet 6 | from .Shop import Shop 7 | -------------------------------------------------------------------------------- /examples/x01separated/petstore_extend/_lazy.py: -------------------------------------------------------------------------------- 1 | def _useCat(): 2 | from .Cat import Cat 3 | return Cat 4 | 5 | 6 | def _useDog(): 7 | from .Dog import Dog 8 | return Dog 9 | 10 | 11 | def _useError(): 12 | from .Error import Error 13 | return Error 14 | 15 | 16 | def _useOwner(): 17 | from .Owner import Owner 18 | return Owner 19 | 20 | 21 | def _usePet(): 22 | from .Pet import Pet 23 | return Pet 24 | 25 | 26 | def _useShop(): 27 | from .Shop import Shop 28 | return Shop 29 | -------------------------------------------------------------------------------- /examples/x01separated/team-and-user.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "team": { 4 | "properties": { 5 | "members": { 6 | "items": { 7 | "$ref": "#/definitions/user" 8 | }, 9 | "type": "array" 10 | }, 11 | "name": { 12 | "type": "string" 13 | } 14 | } 15 | }, 16 | "user": { 17 | "properties": { 18 | "name": { 19 | "type": "string" 20 | }, 21 | "team": { 22 | "$ref": "#/definitions/team" 23 | } 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/x01separated/team_and_user/__init__.py: -------------------------------------------------------------------------------- 1 | from .team import Team 2 | from .user import User 3 | -------------------------------------------------------------------------------- /examples/x01separated/team_and_user/_lazy.py: -------------------------------------------------------------------------------- 1 | def _useTeam(): 2 | from .team import Team 3 | return Team 4 | 5 | 6 | def _useUser(): 7 | from .user import User 8 | return User 9 | -------------------------------------------------------------------------------- /examples/x01separated/team_and_user/team.py: -------------------------------------------------------------------------------- 1 | # this is auto-generated by swagger-marshmallow-codegen 2 | from __future__ import annotations 3 | from marshmallow import ( 4 | Schema, 5 | fields, 6 | INCLUDE, 7 | ) 8 | from ._lazy import _useUser 9 | 10 | 11 | class Team(Schema): 12 | members = fields.List(fields.Nested(_useUser)) 13 | name = fields.String() 14 | 15 | class Meta: 16 | unknown = INCLUDE 17 | -------------------------------------------------------------------------------- /examples/x01separated/team_and_user/user.py: -------------------------------------------------------------------------------- 1 | # this is auto-generated by swagger-marshmallow-codegen 2 | from __future__ import annotations 3 | from marshmallow import ( 4 | Schema, 5 | fields, 6 | INCLUDE, 7 | ) 8 | from ._lazy import _useTeam 9 | 10 | 11 | class User(Schema): 12 | name = fields.String() 13 | team = fields.Nested(_useTeam) 14 | 15 | class Meta: 16 | unknown = INCLUDE 17 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = swagger-marshmallow-codegen 3 | version = attr: swagger_marshmallow_codegen._version.__version__ 4 | 5 | summary = "generating marshmallow's schema from swagger definition file" 6 | long_description = file:README.md 7 | long_description_content_type = text/markdown 8 | 9 | url = https://github.com/podhmo/swagger-marshmallow-codegen 10 | author = podhmo 11 | author_email = ababjam61+github@gmail.com 12 | 13 | [options] 14 | include_package_data = True 15 | zip_safe = False 16 | 17 | [options.entry_points] 18 | console_scripts = 19 | swagger-marshmallow-codegen = swagger_marshmallow_codegen.cmd:main 20 | 21 | [bdist_wheel] 22 | universal=0 23 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | install_requires = [ 4 | "dictknife[load]>=0.14.0", # for ruamel.yaml 5 | "prestring", 6 | "marshmallow>=3.0.0", 7 | "magicalimport", 8 | "typing_extensions", 9 | ] 10 | 11 | docs_extras = [] 12 | 13 | tests_require = ["pytest"] 14 | 15 | testing_extras = tests_require + [] 16 | 17 | setup( 18 | classifiers=[ 19 | "License :: OSI Approved :: MIT License", 20 | "Programming Language :: Python :: 3", 21 | # How mature is this project? Common values are 22 | # 3 - Alpha 23 | # 4 - Beta 24 | # 5 - Production/Stable 25 | "Development Status :: 4 - Beta", 26 | ], 27 | keywords="swagger,codegen,marshmallow,code-generation,schema", 28 | packages=find_packages(exclude=["swagger_marshmallow_codegen.tests"]), 29 | install_requires=install_requires, 30 | extras_require={ 31 | "dev": ["black", "flake8"], 32 | "testing": testing_extras, 33 | "docs": docs_extras, 34 | }, 35 | tests_require=tests_require, 36 | test_suite="swagger_marshmallow_codegen.tests", 37 | ) 38 | -------------------------------------------------------------------------------- /shapes/Makefile: -------------------------------------------------------------------------------- 1 | gen: 2 | python gen.py --logging=INFO \ 3 | defs/00simple-object.py \ 4 | defs/02nesting.py \ 5 | defs/03self-nesting.py \ 6 | defs/05additionalProperties-without-properties.py \ 7 | 8 | python gen.py --verbose --logging=INFO \ 9 | defs/07additionalProperties-with-bool.py \ 10 | 11 | 12 | # 01,04,06 are added by hand. 13 | 14 | gen-candidates: 15 | python gen-candidates.py expected > codegen_candidates.py 16 | cp codegen_candidates.py ../swagger_marshmallow_codegen/tests 17 | 18 | .PHONY: gen 19 | 20 | test: gen-candidates 21 | pytest -vv ../swagger_marshmallow_codegen/tests/test_codegen.py 22 | pytest . 23 | 24 | .PHONY: test 25 | -------------------------------------------------------------------------------- /shapes/README.md: -------------------------------------------------------------------------------- 1 | # memo 2 | 3 | - try.py generates marshmallow schema's from v2 openapi.json 4 | - gen.py generates v2,v3 openapi.json from python class (POPO) 5 | 6 | 7 | ## before tests 8 | 9 | Need `make sync`. 10 | 11 | ```console 12 | # copy files to swagger_marshmallow_codegen.tests 13 | $ make sync 14 | ``` 15 | 16 | ## examples 17 | 18 | ```console 19 | $ tree 20 | . 21 | ├── defs # POPO (plain old python object) 22 | │   ├── 00simple-object.py 23 | │   └── 01simple-primitive.py 24 | ├── expected # marshmallow schema 25 | │   └── 00simple-object.py 26 | ├── v2 # v2 openapi.json 27 | │   ├── 00simple-object.json 28 | │   └── 01simple-primitive.json 29 | └── v3 # v3 openapi.json 30 | ├── 00simple-object.json 31 | └── 01simple-primitive.json 32 | ``` 33 | 34 | ## try.py 35 | 36 | ```console 37 | $ python try.py v2/01simple-primitive.json 38 | ``` 39 | 40 | ## gen.py 41 | 42 | ```console 43 | $ python gen.py defs/00simple-object.py 44 | ``` 45 | -------------------------------------------------------------------------------- /shapes/codegen_candidates.py: -------------------------------------------------------------------------------- 1 | CANDIDATES = [ 2 | ('simple-object', '00simple-object.json', '00simple-object.py'), 3 | ('simple-primitive', '01simple-primitive.json', '01simple-primitive.py'), 4 | ('nesting', '02nesting.json', '02nesting.py'), 5 | ('self-nesting', '03self-nesting.json', '03self-nesting.py'), 6 | ('inline-nesting', '04inline-nesting.json', '04inline-nesting.py'), 7 | ('additionalProperties-without-properties', '05additionalProperties-without-properties.json', '05additionalProperties-without-properties.py'), 8 | ('additionalProperties', '06additionalProperties.json', '06additionalProperties.py'), 9 | ('additionalProperties-with-bool', '07additionalProperties-with-bool.json', '07additionalProperties-with-bool.py'), 10 | ] 11 | -------------------------------------------------------------------------------- /shapes/defs/00simple-object.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | import typing as t 3 | 4 | 5 | class Person: 6 | name: str 7 | age: t.Optional[int] 8 | -------------------------------------------------------------------------------- /shapes/defs/01simple-primitive.py: -------------------------------------------------------------------------------- 1 | import typing as t 2 | 3 | MyInt = t.NewType("MyInt", int) 4 | -------------------------------------------------------------------------------- /shapes/defs/02nesting.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | import typing as t 3 | 4 | 5 | class Person: 6 | name: str 7 | age: t.Optional[int] 8 | memo: Memo 9 | 10 | 11 | class Memo: 12 | title: str 13 | content: str 14 | -------------------------------------------------------------------------------- /shapes/defs/03self-nesting.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | import typing as t 3 | 4 | 5 | class Person: 6 | name: str 7 | age: t.Optional[int] 8 | father: t.Optional[Person] 9 | mother: t.Optional[Person] 10 | -------------------------------------------------------------------------------- /shapes/defs/05additionalProperties-without-properties.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | import typing as t 3 | 4 | 5 | class Person: 6 | name: str 7 | age: int 8 | data: t.Optional[t.Dict[str, str]] 9 | -------------------------------------------------------------------------------- /shapes/defs/07additionalProperties-with-bool.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | import typing as t 3 | 4 | 5 | class Person: 6 | name: str 7 | age: t.Optional[int] 8 | 9 | 10 | class Person_AdditionalProperties_True: 11 | name: str 12 | age: t.Optional[int] 13 | 14 | additionalProperties = True 15 | 16 | 17 | class Person_AdditionalProperties_False: 18 | name: str 19 | age: t.Optional[int] 20 | 21 | additionalProperties = False 22 | -------------------------------------------------------------------------------- /shapes/defs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/podhmo/swagger-marshmallow-codegen/a21787d49f627c75241eb3a56a03b2d5107dfba4/shapes/defs/__init__.py -------------------------------------------------------------------------------- /shapes/expected/00simple-object.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | 6 | 7 | class Person(Schema): 8 | name = fields.String(required=True) 9 | age = fields.Integer() 10 | -------------------------------------------------------------------------------- /shapes/expected/01simple-primitive.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | from swagger_marshmallow_codegen.schema import PrimitiveValueSchema 6 | 7 | 8 | class MyInt(PrimitiveValueSchema): 9 | class schema_class(Schema): 10 | value = fields.Integer() 11 | -------------------------------------------------------------------------------- /shapes/expected/02nesting.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | 6 | 7 | class Person(Schema): 8 | name = fields.String(required=True) 9 | age = fields.Integer() 10 | memo = fields.Nested(lambda: Memo(), required=True) 11 | 12 | 13 | class Memo(Schema): 14 | title = fields.String(required=True) 15 | content = fields.String(required=True) 16 | -------------------------------------------------------------------------------- /shapes/expected/03self-nesting.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | 6 | 7 | class Person(Schema): 8 | name = fields.String(required=True) 9 | age = fields.Integer() 10 | father = fields.Nested(lambda: Person()) 11 | mother = fields.Nested(lambda: Person()) 12 | -------------------------------------------------------------------------------- /shapes/expected/04inline-nesting.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | 6 | 7 | class Person(Schema): 8 | age = fields.Integer() 9 | memo = fields.Nested(lambda: PersonMemo(), required=True) 10 | name = fields.String(required=True) 11 | 12 | 13 | class PersonMemo(Schema): 14 | content = fields.String(required=True) 15 | title = fields.String(required=True) 16 | -------------------------------------------------------------------------------- /shapes/expected/05additionalProperties-without-properties.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | 6 | 7 | class Person(Schema): 8 | name = fields.String(required=True) 9 | age = fields.Integer(required=True) 10 | data = fields.Dict(keys=fields.String(), values=fields.String()) 11 | -------------------------------------------------------------------------------- /shapes/expected/06additionalProperties.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | from swagger_marshmallow_codegen.schema import ( 6 | AdditionalPropertiesSchema, 7 | PrimitiveValueSchema, 8 | ) 9 | 10 | 11 | class Box(AdditionalPropertiesSchema): 12 | name = fields.String(required=True) 13 | 14 | class Meta: 15 | additional_field = fields.Integer() 16 | 17 | 18 | 19 | class Value(PrimitiveValueSchema): 20 | class schema_class(Schema): 21 | value = fields.Integer() 22 | -------------------------------------------------------------------------------- /shapes/expected/07additionalProperties-with-bool.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | INCLUDE, 5 | RAISE, 6 | ) 7 | 8 | 9 | class Person(Schema): 10 | name = fields.String(required=True) 11 | age = fields.Integer() 12 | 13 | 14 | class Person_AdditionalProperties_True(Schema): 15 | name = fields.String(required=True) 16 | age = fields.Integer() 17 | 18 | class Meta: 19 | unknown = INCLUDE 20 | 21 | 22 | 23 | class Person_AdditionalProperties_False(Schema): 24 | name = fields.String(required=True) 25 | age = fields.Integer() 26 | 27 | class Meta: 28 | unknown = RAISE 29 | -------------------------------------------------------------------------------- /shapes/expected/08additionalProperties-without-properties-object.json: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | from swagger_marshmallow_codegen.schema import AdditionalPropertiesSchema 6 | 7 | 8 | class D(AdditionalPropertiesSchema): 9 | 10 | class Meta: 11 | additional_field = fields.Field() 12 | 13 | 14 | 15 | class O(Schema): 16 | name = fields.String() 17 | 18 | 19 | class S(Schema): 20 | x = fields.Nested(lambda: SX()) 21 | y = fields.Dict(keys=fields.String(), values=fields.Nested(lambda: O())) 22 | z = fields.Dict(keys=fields.String(), values=fields.Nested(lambda: None())) 23 | 24 | 25 | class SX(Schema): 26 | name = fields.String() 27 | -------------------------------------------------------------------------------- /shapes/expected/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/podhmo/swagger-marshmallow-codegen/a21787d49f627c75241eb3a56a03b2d5107dfba4/shapes/expected/__init__.py -------------------------------------------------------------------------------- /shapes/gen-candidates.py: -------------------------------------------------------------------------------- 1 | from handofcats import as_command 2 | import pathlib 3 | 4 | 5 | @as_command 6 | def run(dirpath: str) -> None: 7 | import re 8 | 9 | rx = re.compile("^[0-9]+") 10 | print("CANDIDATES = [") 11 | for p in pathlib.Path(dirpath).glob("*.py"): 12 | m = rx.search(p.name) 13 | if m is None: 14 | continue 15 | print( 16 | f' ({rx.sub("", p.with_suffix("").name)!r}, {p.with_suffix(".json").name!r}, {p.name!r}),' 17 | ) 18 | print("]") 19 | -------------------------------------------------------------------------------- /shapes/gen.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | import typing as t 3 | import pathlib 4 | import logging 5 | from handofcats import as_command 6 | from metashape.outputs.openapi import emit, scan 7 | from metashape.runtime import get_walker 8 | from magicalimport import import_module 9 | 10 | logger = logging.getLogger(__name__) 11 | logging.getLogger("metashape.analyze.walker").setLevel(logging.WARNING) 12 | here = pathlib.Path(__file__).parent 13 | 14 | 15 | @as_command 16 | def run( 17 | filenames: t.List[str], *, aggressive: bool = True, verbose: bool = False 18 | ) -> None: 19 | for filename in filenames: 20 | m = import_module(filename, cwd=True) 21 | walker = get_walker(m, aggressive=aggressive) 22 | walker.config.option.strict = False 23 | ctx = scan(walker) 24 | 25 | name = pathlib.Path(filename).with_suffix(".json").name 26 | 27 | # v3 28 | with (here / "v3" / name).open("w") as wf: 29 | logger.info("write %s", wf.name) 30 | ctx.result["openapi"] = "3.1.0" 31 | if verbose: 32 | for clsname, s in ctx.result["components"]["schemas"].items(): 33 | cls = getattr(m, clsname) 34 | v = getattr(cls, "additionalProperties", None) 35 | if v is not None: 36 | s["additionalProperties"] = v 37 | emit(ctx, output=wf) 38 | 39 | # v2 40 | with (here / "v2" / name).open("w") as wf: 41 | logger.info("write %s", wf.name) 42 | ctx.result["openapi"] = "2.0.0" 43 | ctx.result["definitions"] = ctx.result["components"].pop("schemas") 44 | ctx.result.pop("components") 45 | from io import StringIO 46 | 47 | o = StringIO() 48 | emit(ctx, output=o) 49 | print(o.getvalue().replace("/components/schemas", "/definitions"), file=wf) 50 | -------------------------------------------------------------------------------- /shapes/requirements.txt: -------------------------------------------------------------------------------- 1 | metashape 2 | magicalimport 3 | handofcats 4 | -------------------------------------------------------------------------------- /shapes/test_schema.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from magicalimport import import_module 3 | from marshmallow import ValidationError 4 | 5 | 6 | def test_00(): 7 | m = import_module("./expected/00simple-object.py", cwd=False) 8 | data = {"name": "foo", "age": 20} 9 | m.Person().load(data) 10 | 11 | with pytest.raises(ValidationError): 12 | data = {"name": "foo", "age": "foo"} 13 | m.Person().load(data) 14 | 15 | with pytest.raises(ValidationError): 16 | data = {"age": 20} 17 | m.Person().load(data) 18 | 19 | 20 | def test_01(): 21 | m = import_module("./expected/01simple-primitive.py", cwd=False) 22 | s = m.MyInt() 23 | data = 10 24 | s.load(data) 25 | 26 | 27 | def test_02(): 28 | m = import_module("./expected/02nesting.py", cwd=False) 29 | s = m.Person() 30 | data = { 31 | "name": "foo", 32 | "age": 20, 33 | "memo": {"title": "hello", "content": "this is first greeting"}, 34 | } 35 | s.load(data) 36 | 37 | 38 | def test_03(): 39 | m = import_module("./expected/03self-nesting.py", cwd=False) 40 | s = m.Person() 41 | data = { 42 | "name": "foo", 43 | "age": 20, 44 | "father": {"name": "bar", "age": 40}, 45 | "mother": {"name": "boo"}, 46 | } 47 | s.load(data) 48 | 49 | 50 | def test_04(): 51 | m = import_module("./expected/04inline-nesting.py", cwd=False) 52 | s = m.Person() 53 | data = { 54 | "name": "foo", 55 | "age": 20, 56 | "memo": {"title": "hello", "content": "hello world"}, 57 | } 58 | s.load(data) 59 | 60 | 61 | def test_05(): 62 | m = import_module( 63 | "./expected/05additionalProperties-without-properties.py", cwd=False 64 | ) 65 | s = m.Person() 66 | data = { 67 | "name": "foo", 68 | "age": 20, 69 | "data": {"title": "hello", "content": "hello world"}, 70 | } 71 | s.load(data) 72 | 73 | 74 | def test_06(): 75 | m = import_module("./expected/06additionalProperties.py", cwd=False) 76 | s = m.Box() 77 | data = { 78 | "name": "foo", 79 | "x": 1, 80 | "y": 20, 81 | "z": 300, 82 | } 83 | s.load(data) 84 | 85 | with pytest.raises(ValidationError): 86 | data = { 87 | "name": "foo", 88 | "x": "xxx", 89 | } 90 | s.load(data) 91 | 92 | 93 | def test_07(): 94 | m = import_module("./expected/07additionalProperties-with-bool.py", cwd=False) 95 | data = { 96 | "name": "foo", 97 | "xxxx": "xxxx", 98 | } 99 | 100 | # default 101 | with pytest.raises(ValidationError): 102 | m.Person().load(data) 103 | 104 | # true 105 | m.Person_AdditionalProperties_True().load(data) 106 | 107 | # false 108 | with pytest.raises(ValidationError): 109 | m.Person_AdditionalProperties_False().load(data) 110 | -------------------------------------------------------------------------------- /shapes/try.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | import typing as t 3 | 4 | if t.TYPE_CHECKING: 5 | from swagger_marshmallow_codegen.driver import Driver 6 | 7 | 8 | def setup(driver: Driver) -> None: 9 | driver.config["additional_properties_default"] = False 10 | driver.config["emit_schema_even_primitive_type"] = True 11 | driver.config["header_comment"] = "" 12 | 13 | 14 | if __name__ == "__main__": 15 | from swagger_marshmallow_codegen.cmd import main 16 | 17 | main(setup) 18 | -------------------------------------------------------------------------------- /shapes/v2/00simple-object.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi": "2.0.0", 3 | "definitions": { 4 | "Person": { 5 | "type": "object", 6 | "properties": { 7 | "name": { 8 | "type": "string" 9 | }, 10 | "age": { 11 | "type": "integer" 12 | } 13 | }, 14 | "required": [ 15 | "name" 16 | ] 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /shapes/v2/01simple-primitive.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "MyInt": { 4 | "type": "integer" 5 | } 6 | }, 7 | "openapi": "2.0.0" 8 | } 9 | -------------------------------------------------------------------------------- /shapes/v2/02nesting.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi": "2.0.0", 3 | "definitions": { 4 | "Person": { 5 | "type": "object", 6 | "properties": { 7 | "name": { 8 | "type": "string" 9 | }, 10 | "age": { 11 | "type": "integer" 12 | }, 13 | "memo": { 14 | "$ref": "#/definitions/Memo" 15 | } 16 | }, 17 | "required": [ 18 | "name", 19 | "memo" 20 | ] 21 | }, 22 | "Memo": { 23 | "type": "object", 24 | "properties": { 25 | "title": { 26 | "type": "string" 27 | }, 28 | "content": { 29 | "type": "string" 30 | } 31 | }, 32 | "required": [ 33 | "title", 34 | "content" 35 | ] 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /shapes/v2/03self-nesting.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi": "2.0.0", 3 | "definitions": { 4 | "Person": { 5 | "type": "object", 6 | "properties": { 7 | "name": { 8 | "type": "string" 9 | }, 10 | "age": { 11 | "type": "integer" 12 | }, 13 | "father": { 14 | "$ref": "#/definitions/Person" 15 | }, 16 | "mother": { 17 | "$ref": "#/definitions/Person" 18 | } 19 | }, 20 | "required": [ 21 | "name" 22 | ] 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /shapes/v2/04inline-nesting.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "Person": { 4 | "properties": { 5 | "age": { 6 | "type": "integer" 7 | }, 8 | "memo": { 9 | "properties": { 10 | "content": { 11 | "type": "string" 12 | }, 13 | "title": { 14 | "type": "string" 15 | } 16 | }, 17 | "required": [ 18 | "title", 19 | "content" 20 | ], 21 | "type": "object" 22 | }, 23 | "name": { 24 | "type": "string" 25 | } 26 | }, 27 | "required": [ 28 | "name", 29 | "memo" 30 | ], 31 | "type": "object" 32 | } 33 | }, 34 | "openapi": "2.0.0" 35 | } 36 | -------------------------------------------------------------------------------- /shapes/v2/05additionalProperties-without-properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi": "2.0.0", 3 | "definitions": { 4 | "Person": { 5 | "type": "object", 6 | "properties": { 7 | "name": { 8 | "type": "string" 9 | }, 10 | "age": { 11 | "type": "integer" 12 | }, 13 | "data": { 14 | "type": "object", 15 | "additionalProperties": { 16 | "type": "string" 17 | } 18 | } 19 | }, 20 | "required": [ 21 | "name", 22 | "age" 23 | ] 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /shapes/v2/06additionalProperties.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "Box": { 4 | "additionalProperties": { 5 | "$ref": "#/definitions/value" 6 | }, 7 | "properties": { 8 | "name": { 9 | "type": "string" 10 | } 11 | }, 12 | "required": [ 13 | "name" 14 | ] 15 | }, 16 | "value": { 17 | "type": "integer" 18 | } 19 | }, 20 | "openapi": "2.0.0" 21 | } 22 | -------------------------------------------------------------------------------- /shapes/v2/07additionalProperties-with-bool.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi": "2.0.0", 3 | "definitions": { 4 | "Person": { 5 | "type": "object", 6 | "properties": { 7 | "name": { 8 | "type": "string" 9 | }, 10 | "age": { 11 | "type": "integer" 12 | } 13 | }, 14 | "required": [ 15 | "name" 16 | ] 17 | }, 18 | "Person_AdditionalProperties_True": { 19 | "type": "object", 20 | "properties": { 21 | "name": { 22 | "type": "string" 23 | }, 24 | "age": { 25 | "type": "integer" 26 | } 27 | }, 28 | "required": [ 29 | "name" 30 | ], 31 | "additionalProperties": true 32 | }, 33 | "Person_AdditionalProperties_False": { 34 | "type": "object", 35 | "properties": { 36 | "name": { 37 | "type": "string" 38 | }, 39 | "age": { 40 | "type": "integer" 41 | } 42 | }, 43 | "required": [ 44 | "name" 45 | ], 46 | "additionalProperties": false 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /shapes/v2/08additionalProperties-without-properties-object.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "D": { 4 | "additionalProperties": { 5 | "properties": { 6 | "name": { 7 | "type": "string" 8 | } 9 | }, 10 | "type": "object" 11 | } 12 | }, 13 | "O": { 14 | "properties": { 15 | "name": { 16 | "type": "string" 17 | } 18 | }, 19 | "type": "object" 20 | }, 21 | "S": { 22 | "properties": { 23 | "x": { 24 | "additionalProperties": { 25 | "properties": { 26 | "name": { 27 | "type": "string" 28 | } 29 | }, 30 | "type": "object" 31 | } 32 | }, 33 | "y": { 34 | "additionalProperties": { 35 | "$ref": "#/definitions/O" 36 | } 37 | }, 38 | "z": { 39 | "$ref": "#/definitions/D" 40 | } 41 | }, 42 | "type": "object" 43 | } 44 | }, 45 | "verssion": "2.0.0" 46 | } 47 | -------------------------------------------------------------------------------- /shapes/v3/00simple-object.json: -------------------------------------------------------------------------------- 1 | { 2 | "components": { 3 | "schemas": { 4 | "Person": { 5 | "type": "object", 6 | "properties": { 7 | "name": { 8 | "type": "string" 9 | }, 10 | "age": { 11 | "type": "integer" 12 | } 13 | }, 14 | "required": [ 15 | "name" 16 | ] 17 | } 18 | } 19 | }, 20 | "openapi": "3.1.0" 21 | } -------------------------------------------------------------------------------- /shapes/v3/01simple-primitive.json: -------------------------------------------------------------------------------- 1 | { 2 | "components": { 3 | "schemas": { 4 | "MyInt": { 5 | "type": "integer" 6 | } 7 | } 8 | }, 9 | "openapi": "3.1.0" 10 | } 11 | -------------------------------------------------------------------------------- /shapes/v3/02nesting.json: -------------------------------------------------------------------------------- 1 | { 2 | "components": { 3 | "schemas": { 4 | "Person": { 5 | "type": "object", 6 | "properties": { 7 | "name": { 8 | "type": "string" 9 | }, 10 | "age": { 11 | "type": "integer" 12 | }, 13 | "memo": { 14 | "$ref": "#/components/schemas/Memo" 15 | } 16 | }, 17 | "required": [ 18 | "name", 19 | "memo" 20 | ] 21 | }, 22 | "Memo": { 23 | "type": "object", 24 | "properties": { 25 | "title": { 26 | "type": "string" 27 | }, 28 | "content": { 29 | "type": "string" 30 | } 31 | }, 32 | "required": [ 33 | "title", 34 | "content" 35 | ] 36 | } 37 | } 38 | }, 39 | "openapi": "3.1.0" 40 | } -------------------------------------------------------------------------------- /shapes/v3/03self-nesting.json: -------------------------------------------------------------------------------- 1 | { 2 | "components": { 3 | "schemas": { 4 | "Person": { 5 | "type": "object", 6 | "properties": { 7 | "name": { 8 | "type": "string" 9 | }, 10 | "age": { 11 | "type": "integer" 12 | }, 13 | "father": { 14 | "$ref": "#/components/schemas/Person" 15 | }, 16 | "mother": { 17 | "$ref": "#/components/schemas/Person" 18 | } 19 | }, 20 | "required": [ 21 | "name" 22 | ] 23 | } 24 | } 25 | }, 26 | "openapi": "3.1.0" 27 | } -------------------------------------------------------------------------------- /shapes/v3/04inline-nesting.json: -------------------------------------------------------------------------------- 1 | { 2 | "components": { 3 | "schemas": { 4 | "Person": { 5 | "properties": { 6 | "age": { 7 | "type": "integer" 8 | }, 9 | "memo": { 10 | "properties": { 11 | "content": { 12 | "type": "string" 13 | }, 14 | "title": { 15 | "type": "string" 16 | } 17 | }, 18 | "required": [ 19 | "title", 20 | "content" 21 | ], 22 | "type": "object" 23 | }, 24 | "name": { 25 | "type": "string" 26 | } 27 | }, 28 | "required": [ 29 | "name", 30 | "memo" 31 | ], 32 | "type": "object" 33 | } 34 | } 35 | }, 36 | "openapi": "3.1.0" 37 | } 38 | -------------------------------------------------------------------------------- /shapes/v3/05additionalProperties-without-properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "components": { 3 | "schemas": { 4 | "Person": { 5 | "type": "object", 6 | "properties": { 7 | "name": { 8 | "type": "string" 9 | }, 10 | "age": { 11 | "type": "integer" 12 | }, 13 | "data": { 14 | "type": "object", 15 | "additionalProperties": { 16 | "type": "string" 17 | } 18 | } 19 | }, 20 | "required": [ 21 | "name", 22 | "age" 23 | ] 24 | } 25 | } 26 | }, 27 | "openapi": "3.1.0" 28 | } -------------------------------------------------------------------------------- /shapes/v3/06additionalProperties.json: -------------------------------------------------------------------------------- 1 | { 2 | "components": { 3 | "schemas": { 4 | "Box": { 5 | "additionalProperties": { 6 | "$ref": "#/components/schemas/value" 7 | }, 8 | "properties": { 9 | "name": { 10 | "type": "string" 11 | } 12 | }, 13 | "required": [ 14 | "name" 15 | ] 16 | }, 17 | "value": { 18 | "type": "integer" 19 | } 20 | } 21 | }, 22 | "openapi": "3.1.0" 23 | } 24 | -------------------------------------------------------------------------------- /shapes/v3/07additionalProperties-with-bool.json: -------------------------------------------------------------------------------- 1 | { 2 | "components": { 3 | "schemas": { 4 | "Person": { 5 | "type": "object", 6 | "properties": { 7 | "name": { 8 | "type": "string" 9 | }, 10 | "age": { 11 | "type": "integer" 12 | } 13 | }, 14 | "required": [ 15 | "name" 16 | ] 17 | }, 18 | "Person_AdditionalProperties_True": { 19 | "type": "object", 20 | "properties": { 21 | "name": { 22 | "type": "string" 23 | }, 24 | "age": { 25 | "type": "integer" 26 | } 27 | }, 28 | "required": [ 29 | "name" 30 | ], 31 | "additionalProperties": true 32 | }, 33 | "Person_AdditionalProperties_False": { 34 | "type": "object", 35 | "properties": { 36 | "name": { 37 | "type": "string" 38 | }, 39 | "age": { 40 | "type": "integer" 41 | } 42 | }, 43 | "required": [ 44 | "name" 45 | ], 46 | "additionalProperties": false 47 | } 48 | } 49 | }, 50 | "openapi": "3.1.0" 51 | } -------------------------------------------------------------------------------- /shapes/v3/08additionalProperties-without-properties-object.json: -------------------------------------------------------------------------------- 1 | { 2 | "components": { 3 | "schemas": { 4 | "D": { 5 | "additionalProperties": { 6 | "properties": { 7 | "name": { 8 | "type": "string" 9 | } 10 | }, 11 | "type": "object" 12 | } 13 | }, 14 | "O": { 15 | "properties": { 16 | "name": { 17 | "type": "string" 18 | } 19 | }, 20 | "type": "object" 21 | }, 22 | "S": { 23 | "properties": { 24 | "x": { 25 | "additionalProperties": { 26 | "properties": { 27 | "name": { 28 | "type": "string" 29 | } 30 | }, 31 | "type": "object" 32 | } 33 | }, 34 | "y": { 35 | "additionalProperties": { 36 | "$ref": "#/components/schemas/O" 37 | } 38 | }, 39 | "z": { 40 | "$ref": "#/components/schemas/D" 41 | } 42 | }, 43 | "type": "object" 44 | } 45 | } 46 | }, 47 | "openapi": "3.1.0" 48 | } 49 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/__init__.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from ._version import __version__ # noqa:F401 3 | 4 | logger = logging.getLogger(__name__) 5 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/__main__.py: -------------------------------------------------------------------------------- 1 | if __name__ == "__main__": 2 | from swagger_marshmallow_codegen.cmd import main 3 | 4 | main() 5 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/_version.py: -------------------------------------------------------------------------------- 1 | __version__ = "0.6.5" 2 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/cmd.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | import typing as t 3 | import sys 4 | import logging 5 | import argparse 6 | from magicalimport import import_symbol 7 | 8 | 9 | if t.TYPE_CHECKING: 10 | from swagger_marshmallow_codegen.driver import Driver, ConfigDict 11 | 12 | logger = logging.getLogger(__name__) 13 | 14 | 15 | def main(setup: t.Optional[t.Callable[[Driver], None]] = None): 16 | parser = argparse.ArgumentParser() 17 | parser.add_argument("--driver", default="Driver") 18 | parser.add_argument( 19 | "--logging", 20 | default="INFO", 21 | choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], 22 | ) 23 | parser.add_argument("--full", action="store_true") 24 | parser.add_argument("--legacy", action="store_true") 25 | parser.add_argument("--strict-additional-properties", action="store_true") 26 | parser.add_argument("--explicit", action="store_true") 27 | 28 | parser.add_argument("-d", "--separated-output", default=None) 29 | 30 | parser.add_argument("file", default=None) 31 | args = parser.parse_args() 32 | 33 | logging.basicConfig( 34 | format="%(levelname)5s:%(name)36s:%(message)s", 35 | level=logging._nameToLevel[args.logging], 36 | ) 37 | 38 | driver_cls = args.driver 39 | if args.legacy and driver_cls == "Driver": # xxx 40 | logger.info("legacy option is added. output Schema is legacy flavor") 41 | driver_cls = "LegacyDriver" 42 | if ":" not in driver_cls: 43 | driver_cls = "swagger_marshmallow_codegen.driver:{}".format(driver_cls) 44 | 45 | config: ConfigDict = { 46 | "emit_schema": True, 47 | "emit_input": False, 48 | "emit_output": False, 49 | "additional_properties_default": not args.strict_additional_properties, 50 | "separated_output": bool(args.separated_output), 51 | } 52 | if args.full: 53 | config["emit_input"] = True 54 | config["emit_output"] = True 55 | 56 | logger.debug("config is %r", config) 57 | 58 | driver = import_symbol(driver_cls, cwd=True)(config) 59 | if setup is not None: 60 | setup(driver) 61 | 62 | if args.file is None: 63 | driver.run(sys.stdin, output=args.separated_output) 64 | else: 65 | with open(args.file) as rf: 66 | driver.run(rf, output=args.separated_output) 67 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/codegen/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | import typing as t 3 | import logging 4 | from functools import partial 5 | from .config import ConfigDict 6 | from .context import Context 7 | from .context import OneFileContextFactory, SeparatedFilesContextFactory 8 | from .v2.codegen import SchemaWriter # noqa:F401 9 | 10 | if t.TYPE_CHECKING: 11 | from swagger_marshmallow_codegen.resolver import Resolver 12 | from .context import InputData 13 | from .context import OutputData 14 | 15 | logger = logging.getLogger(__name__) 16 | 17 | 18 | class Codegen: 19 | version_path: str = "openapi" 20 | 21 | @classmethod 22 | def override( 23 | cls, 24 | *, 25 | schema_class_path: t.Optional[str] = None, 26 | schema_writer_factory: t.Callable[[...], SchemaWriter] = None, 27 | ): 28 | return partial( 29 | cls, 30 | schema_class_path=schema_class_path, 31 | schema_writer_factory=schema_writer_factory, 32 | ) 33 | 34 | def __init__( 35 | self, 36 | resolver: Resolver, 37 | *, 38 | schema_class_path: t.Optional[str] = None, 39 | schema_writer_factory: t.Optional[t.Any] = None, # todo: type 40 | ): 41 | self.resolver = resolver 42 | self.schema_class_path = schema_class_path 43 | self.schema_writer_factory = schema_writer_factory 44 | 45 | def codegen( 46 | self, d: InputData, config: ConfigDict, *, ctx: t.Optional[Context] = None, 47 | ) -> OutputData: 48 | codegen_cls = self.guess_factory(d, config=config, path=self.version_path) 49 | codegen = codegen_cls( 50 | schema_class_path=self.schema_class_path, 51 | schema_writer_factory=self.schema_writer_factory, 52 | ) 53 | if config.get("separated_output", False): 54 | context_factory_cls = SeparatedFilesContextFactory 55 | else: 56 | context_factory_cls = OneFileContextFactory 57 | 58 | context_factory = context_factory_cls( 59 | ctx or Context(), setup=codegen.setup_context 60 | ) 61 | try: 62 | return codegen.codegen(d, context_factory=context_factory) 63 | finally: 64 | if context_factory.teardown is not None: 65 | context_factory.teardown(codegen.accessor.resolver) 66 | 67 | def guess_factory( 68 | self, d: t.Dict, *, config: ConfigDict, path: str 69 | ) -> t.Type[t.Any]: 70 | version = d.get(path) 71 | if version is None: 72 | logger.info("version is not found, guessing... v2") 73 | version = "2.0" 74 | 75 | if version.strip()[0] == "2": 76 | from .v2.codegen import Codegen 77 | from .v2.accessor import Accessor 78 | 79 | logger.info("openapi version is v2") 80 | return partial(Codegen, Accessor(self.resolver, config=config)) 81 | elif version.strip()[0] >= "3": 82 | from .v3.codegen import Codegen 83 | from .v3.accessor import Accessor 84 | 85 | logger.info("openapi version is v3") 86 | return partial(Codegen, Accessor(self.resolver, config=config)) 87 | else: 88 | raise RuntimeError(f"unexpected openapi version {version}") 89 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/codegen/config.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | import typing_extensions as tx 3 | 4 | 5 | class ConfigDict(tx.TypedDict, total=False): 6 | emit_schema: bool # emit definitions schemas 7 | emit_input: bool # emit input schema 8 | emit_output: bool # emit output schema 9 | 10 | separated_output: bool # activate separated output 11 | 12 | explicit: bool # emit Meta.unknown=RAISE, explicitly 13 | additional_properties_default: bool # if true meta.unknown=INCLUDE 14 | 15 | emit_schema_even_primitive_type: bool # emit not used toplevel definitions 16 | header_comment: str # header comment 17 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/codegen/v2/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/podhmo/swagger-marshmallow-codegen/a21787d49f627c75241eb3a56a03b2d5107dfba4/swagger_marshmallow_codegen/codegen/v2/__init__.py -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/codegen/v2/accessor.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | import typing as t 3 | import logging 4 | 5 | if t.TYPE_CHECKING: 6 | from swagger_marshmallow_codegen.resolver import Resolver 7 | from swagger_marshmallow_codegen.dispatcher import FormatDispatcher 8 | from ..config import ConfigDict 9 | from ..context import Context 10 | logger = logging.getLogger(__name__) 11 | 12 | 13 | class Accessor: 14 | def __init__(self, resolver: Resolver, *, config: ConfigDict) -> None: 15 | self.resolver = resolver 16 | self.config = config 17 | 18 | @property 19 | def dispatcher(self) -> FormatDispatcher: 20 | return self.resolver.dispatcher 21 | 22 | def paths(self, d: t.Dict[str, t.Any]) -> t.List[t.Dict[str, t.Any]]: 23 | return (d.get("paths") or {}).items() 24 | 25 | def methods( 26 | self, 27 | d: t.Dict[str, t.Any], 28 | candidates: t.Set[str] = set( 29 | ("get", "post", "put", "head", "delete", "options", "patch") 30 | ), 31 | ) -> t.Iterator[t.Tuple[str, t.Dict[str, t.Any]]]: 32 | for method, definition in d.items(): 33 | if method in candidates: 34 | yield method, definition 35 | 36 | def parameters(self, d: t.Dict[str, t.Any]) -> t.List[t.Dict[str, t.Any]]: 37 | return d.get("parameters") or [] 38 | 39 | def responses( 40 | self, d: t.Dict[str, t.Any] 41 | ) -> t.Iterator[t.Tuple[str, t.Dict[str, t.Any]]]: 42 | for name, definition in (d.get("responses") or {}).items(): 43 | name = str(name) 44 | if not name.startswith("x-"): 45 | yield name, definition 46 | 47 | def definitions(self, d: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]: 48 | return (d.get("definitions") or {}).items() 49 | 50 | def properties(self, d: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]: 51 | return d.get("properties") or {} 52 | 53 | def pattern_properties(self, d: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]: 54 | return d.get("patternProperties") or {} 55 | 56 | def update_options_pre_properties( 57 | self, d: t.Dict[str, t.Any], opts: t.Dict[str, t.Any] 58 | ) -> t.Dict[str, t.Any]: 59 | for name in d.get("required") or []: 60 | opts[name]["required"] = True 61 | return opts 62 | 63 | def update_option_on_property( 64 | self, c: Context, field: t.Dict[str, t.Any], opts: t.Dict[str, t.Any] 65 | ) -> t.Dict[str, t.Any]: 66 | if "description" in field: 67 | opts["description"] = field["description"] 68 | if self.resolver.has_many(field): 69 | logger.debug(" resolve: many=True") 70 | opts["many"] = True 71 | if "default" in field and not opts.get("required", False): 72 | logger.debug(" resolve: default=%r", field["default"]) 73 | opts["missing"] = self.dispatcher.handle_default( 74 | c, field["default"], field 75 | ) # xxx 76 | if field.get("readOnly", False): 77 | logger.debug(" resolve: dump_only=True") 78 | opts["dump_only"] = True 79 | typ = field.get("type") 80 | 81 | if isinstance(typ, (list, tuple)): 82 | for typ in typ: 83 | if typ is None: 84 | opts["allow_none"] = True 85 | if field.get("x-nullable") or field.get("nullable"): 86 | opts["allow_none"] = True 87 | 88 | validators = self.resolver.resolve_validators_on_property(c, field) 89 | if validators: 90 | opts["validate"] = validators 91 | return opts 92 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/codegen/v3/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/podhmo/swagger-marshmallow-codegen/a21787d49f627c75241eb3a56a03b2d5107dfba4/swagger_marshmallow_codegen/codegen/v3/__init__.py -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/codegen/v3/accessor.py: -------------------------------------------------------------------------------- 1 | from ..v2.accessor import Accessor as V2Accessor 2 | 3 | 4 | class Accessor(V2Accessor): 5 | def definitions(self, d): 6 | components = d.get("components") 7 | if components is None: 8 | return {} 9 | return (components.get("schemas") or {}).items() 10 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/codegen/v3/codegen.py: -------------------------------------------------------------------------------- 1 | from ..v2.codegen import Codegen 2 | 3 | __all__ = ["Codegen"] 4 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/constants.py: -------------------------------------------------------------------------------- 1 | X_MARSHMALLOW_NAME = "x-marshmallow-name" 2 | X_MARSHMALLOW_INLINE = "x-marshmallow-inline" 3 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/dispatcher.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | import logging 3 | from functools import partial 4 | from collections import namedtuple 5 | from magicalimport import import_symbol 6 | 7 | logger = logging.getLogger(__name__) 8 | 9 | 10 | Pair = namedtuple("Pair", "type,format") 11 | 12 | # TODO: correct mapping 13 | 14 | # http://apispec.readthedocs.io/en/latest/_modules/apispec/ext/marshmallow/swagger.html 15 | TYPE_MAP = { 16 | Pair(type="integer", format="int32"): "marshmallow.fields:Integer", 17 | Pair(type="number", format=None): "marshmallow.fields:Number", 18 | Pair(type="number", format="float"): "marshmallow.fields:Float", 19 | Pair(type="number", format="decimal"): "marshmallow.fields:Decimal", # not matched 20 | Pair(type="number", format="integer"): "marshmallow.fields:Integer", 21 | Pair(type="integer", format=None): "marshmallow.fields:Integer", # swagger 22 | Pair(type="string", format=None): "marshmallow.fields:String", 23 | Pair(type="boolean", format=None): "marshmallow.fields:Boolean", 24 | Pair(type="string", format="uuid"): "marshmallow.fields:UUID", 25 | Pair(type="string", format="date-time"): "marshmallow.fields:AwareDateTime", 26 | Pair(type="string", format="date"): "marshmallow.fields:Date", 27 | Pair(type="string", format="time"): "marshmallow.fields:Time", 28 | Pair(type="string", format="email"): "marshmallow.fields:Email", 29 | Pair(type="string", format="url"): "marshmallow.fields:URL", 30 | Pair(type="array", format=None): "marshmallow.fields:List", 31 | Pair(type="object", format=None): "marshmallow.fields:Nested", 32 | Pair(type="any", format=None): "marshmallow.fields:Field", 33 | Pair(type="file", format=None): "marshmallow.fields:Field", 34 | } 35 | 36 | 37 | class FormatDispatcher: 38 | type_map = TYPE_MAP 39 | 40 | @classmethod 41 | def override(cls, type_map): 42 | return partial(cls, type_map=type_map) 43 | 44 | @classmethod 45 | def load_def_map(cls, type_map): 46 | return {pair: import_symbol(path, cwd=True) for pair, path in type_map.items()} 47 | 48 | def __init__(self, type_map=None, use_def_map=True): 49 | self.type_map = type_map or self.__class__.type_map 50 | self.def_map = self.load_def_map(self.type_map) if use_def_map else {} 51 | 52 | def dispatch(self, pair, field): 53 | return self.type_map.get(pair) or self.type_map.get((pair[0], None)) 54 | 55 | def handle_validator(self, c, value): 56 | return ReprWrapValidator(self.dispatch_validator(c, value)) 57 | 58 | def dispatch_validator(self, c, value): 59 | from marshmallow.validate import Length, Regexp, OneOf 60 | from .validate import Range, MultipleOf, Unique, ItemsRange 61 | 62 | if isinstance(value, (Regexp)): 63 | c.import_("re") # xxx 64 | c.from_("marshmallow.validate", value.__class__.__name__) 65 | elif isinstance(value, (Length, OneOf)): 66 | c.from_("marshmallow.validate", value.__class__.__name__) 67 | elif isinstance(value, (Range, MultipleOf, Unique, ItemsRange)): 68 | c.from_("swagger_marshmallow_codegen.validate", value.__class__.__name__) 69 | return value 70 | 71 | def handle_default(self, c, value, field): 72 | return ReprWrapDefault(self.dispatch_default(c, value, field)) 73 | 74 | def dispatch_default(self, c, value, field): 75 | from datetime import datetime, time, date # xxx 76 | from collections import OrderedDict # xxx 77 | 78 | if isinstance(value, (time, date, datetime)): 79 | c.import_("datetime") 80 | elif isinstance(value, OrderedDict): 81 | c.from_("collections", "OrderedDict") 82 | return value 83 | 84 | 85 | class ReprWrap: 86 | def __init__(self, value): 87 | self.value = value 88 | 89 | def __getattr__(self, name): 90 | return getattr(self.value, name) 91 | 92 | @property 93 | def __class__(self): 94 | return self.value.__class__ 95 | 96 | 97 | class ReprWrapValidator(ReprWrap): 98 | def __repr__(self): 99 | return "{self.__class__.__name__}({args})".format( 100 | self=self, args=self.value._repr_args() 101 | ) 102 | 103 | 104 | class ReprWrapDefault(ReprWrap): 105 | def __repr__(self): 106 | return "lambda: {self.value!r}".format(self=self) 107 | 108 | 109 | class ReprWrapString(ReprWrap): 110 | def __repr__(self): 111 | return str(self.value) 112 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/driver.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | import logging 3 | import typing as t 4 | from prestring.output import output as create_separated_output 5 | from . import loading 6 | from .resolver import Resolver 7 | from .codegen import Codegen, SchemaWriter 8 | from .dispatcher import FormatDispatcher 9 | from .lifting import lifting_definition 10 | from .langhelpers import normalize 11 | 12 | if t.TYPE_CHECKING: 13 | from .codegen.config import ConfigDict 14 | from .codegen.context import InputData, OutputData 15 | 16 | logger = logging.getLogger(__name__) 17 | 18 | 19 | class Driver: 20 | dispatcher_factory = FormatDispatcher 21 | resolver_factory = Resolver 22 | codegen_factory = Codegen 23 | 24 | def __init__(self, config: ConfigDict): 25 | self.config: ConfigDict = config 26 | 27 | def load(self, fp: t.Any) -> InputData: 28 | # hack: 29 | # import dictknife.loading._yaml as xxx 30 | # xxx.make_dict = dict 31 | return loading.load(fp) 32 | 33 | def dump(self, d: OutputData, *, output: t.Optional[str] = None) -> None: 34 | if output is None: 35 | for name, m in d.files: 36 | print(m) 37 | return 38 | 39 | with create_separated_output(normalize(output), suffix=".py") as fs: 40 | seen = set() 41 | for name, m in d.files: 42 | seen.add(name) 43 | with fs.open(name, "w") as wf: 44 | print(m, file=wf) 45 | 46 | def transform(self, d: InputData) -> OutputData: 47 | d = lifting_definition(d) 48 | return self.create_codegen().codegen(d, config=self.config) 49 | 50 | def run(self, inp, *, output: t.Optional[str] = None): 51 | data = self.load(inp) 52 | result = self.transform(data) 53 | self.dump(result, output=output) 54 | 55 | def create_codegen(self): 56 | dispatcher = self.dispatcher_factory() 57 | resolver = self.resolver_factory(dispatcher) 58 | return self.codegen_factory(resolver) 59 | 60 | 61 | class LegacyDriver(Driver): 62 | def codegen_factory(self, accessor): 63 | factory = Codegen.override( 64 | schema_class_path="swagger_marshmallow_codegen.schema.legacy:LegacySchema", 65 | schema_writer_factory=SchemaWriter.override( 66 | extra_schema_module="swagger_marshmallow_codegen.schema.legacy" 67 | ), 68 | ) 69 | return factory(accessor) 70 | 71 | 72 | class Flatten: 73 | def __init__(self, config: ConfigDict) -> None: 74 | self.config: ConfigDict = config 75 | 76 | def load(self, fp: t.Any) -> InputData: 77 | return loading.load(fp) 78 | 79 | def dump(self, result: OutputData, *, output: t.Optional[str] = None) -> None: 80 | for _, d in result.files: 81 | return loading.dump(d, output) 82 | 83 | def transform(self, d: InputData) -> OutputData: 84 | lifted = lifting_definition(d) 85 | 86 | class _Data: 87 | @property 88 | def files(self) -> t.Iterator[t.Tuple[str, t.Any]]: 89 | yield lifted 90 | 91 | return _Data() 92 | 93 | def run(self, inp, *, output: t.Optional[str] = None): 94 | data = self.load(inp) 95 | result = self.transform(data) 96 | self.dump(result, output=output) 97 | 98 | 99 | class ProfileDriver(Driver): 100 | def run(self, inp, *, output: t.Optional[str] = None): 101 | import cProfile 102 | import pstats 103 | 104 | profile = cProfile.Profile() 105 | profile.enable() 106 | data = self.load(inp) 107 | result = self.transform(data) 108 | profile.disable() 109 | s = pstats.Stats(profile) 110 | s.dump_stats("swagger-marshmallow-codegen.prof") 111 | self.dump(result, output=output) 112 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/evil.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import logging 3 | from marshmallow.decorators import tag_processor 4 | 5 | 6 | logger = logging.getLogger(__name__) 7 | 8 | 9 | def xxx_modify_field(schema_cls, name): 10 | def action(modify): 11 | logger.debug("\t xxx: modify field=%r schema=%r, ", name, schema_cls) 12 | fields = schema_cls._declared_fields 13 | modify(fields[name]) 14 | return modify 15 | 16 | return action 17 | 18 | 19 | def xxx_add_processor(cls, tag, pass_many=False, pass_original=False): 20 | logger.debug("\t xxx: add tag=%r, pass_many=%r", tag, pass_many) 21 | 22 | def wrapped(fn): 23 | name = fn.__name__ 24 | fn = tag_processor(tag, fn, pass_many, pass_original=pass_original) 25 | setattr(cls, name, fn) 26 | cls.__processors__[(tag, pass_many)].append(name) 27 | return fn 28 | 29 | return wrapped 30 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/fields.py: -------------------------------------------------------------------------------- 1 | from marshmallow import fields, validate 2 | 3 | # for backward compatibility 4 | Date = fields.Date 5 | DateTime = fields.DateTime 6 | Time = fields.Time 7 | 8 | 9 | class PatternProperties(fields.Field): # not supported yet 10 | """ 11 | for jsonschma's patternProperties option. 12 | 13 | .. code-block:: yaml 14 | 15 | scoreData: 16 | type: object 17 | patternProperties: 18 | ".+Score$": 19 | type: integer 20 | additionalProperties: false 21 | """ 22 | 23 | def __init__(self, pattern, nested_field, *args, **kwargs): 24 | fields.Field.__init__(self, *args, **kwargs) 25 | self.key_field = fields.Str(validate=validate.Regexp(pattern)) 26 | self.nested_field = nested_field 27 | 28 | def _deserialize(self, value): 29 | return { 30 | self.key_field.deserialize(k): self.nested_field.deserialize(v) 31 | for k, v in value.items() 32 | } 33 | 34 | def _serialize(self, value, attr, obj): 35 | return { 36 | self.key_field._serialize(k, attr, obj): self.nested_field.serialize( 37 | k, self.get_value(attr, obj) 38 | ) 39 | for k, v in value.items() 40 | } 41 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/langhelpers.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import re 3 | 4 | 5 | def normalize(name, ignore_rx=re.compile("[^0-9a-zA-Z_]+")): 6 | c = name[0] 7 | if c.isdigit(): 8 | name = "n" + name 9 | elif not (c.isalpha() or c == "_"): 10 | name = "x" + name 11 | return ignore_rx.sub("", name.replace("-", "_")) 12 | 13 | 14 | def titleize(name): 15 | if not name: 16 | return name 17 | name = str(name) 18 | return normalize("{}{}".format(name[0].upper(), name[1:])) 19 | 20 | 21 | def untitleize(name): 22 | if not name: 23 | return name 24 | return "{}{}".format(name[0].lower(), name[1:]) 25 | 26 | 27 | def clsname_from_path( 28 | path, ignore_rx=re.compile("[^0-9a-zA-Z_]+"), separate_rx=re.compile("[/_]") 29 | ): 30 | path_separated = separate_rx.split(path.lstrip("/")) # xxx: 31 | return "".join(titleize(ignore_rx.sub("", name)) for name in path_separated) 32 | 33 | 34 | class LazyCallString: 35 | def __init__(self, call, *args, **kwargs): 36 | self.call = call 37 | self.args = args 38 | self.kwargs = kwargs 39 | 40 | def __str__(self): 41 | return self.call(*self.args, **self.kwargs) 42 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/lifting.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | import copy 3 | from dictknife.jsonknife.lifting import Handler 4 | from dictknife.accessing import Accessor 5 | from swagger_marshmallow_codegen.constants import X_MARSHMALLOW_INLINE 6 | from .langhelpers import titleize 7 | 8 | 9 | class Flattener: 10 | def __init__(self, replace=True, *, prefix="#/definitions/"): 11 | self.replace = replace 12 | self.prefix = prefix 13 | 14 | def extract(self, data, ctx): 15 | self._extract(data, ctx) 16 | for k in list(ctx.r.keys()): 17 | ctx.r[k] = copy.deepcopy(ctx.r[k]) 18 | return ctx.r 19 | 20 | def _extract(self, data, ctx, from_array=False): 21 | typ = data.get("type") 22 | if typ == "array" and "items" in data: 23 | return self.on_array_has_items(data, ctx) 24 | elif typ is None or typ == "object": 25 | if from_array or "properties" in data: 26 | return self.on_object_has_properties(data, ctx) 27 | elif ( 28 | hasattr(data.get("additionalProperties"), "keys") 29 | and "properties" in data["additionalProperties"] 30 | ): 31 | ctx.add_name("__additionalProperties") 32 | r = self.on_object_has_properties(data["additionalProperties"], ctx) 33 | ctx.pop_name() 34 | data["additionalProperties"] = r 35 | return data 36 | else: 37 | return data 38 | else: 39 | return data 40 | 41 | def return_definition(self, definition, fullname, typ="object"): 42 | if self.replace: 43 | return {"$ref": self.prefix + fullname} 44 | else: 45 | return definition 46 | 47 | def on_object_has_properties(self, data, ctx): 48 | for name in data.get("properties") or {}: 49 | ctx.add_name(name) 50 | data["properties"][name] = self._extract(data["properties"][name], ctx) 51 | ctx.pop_name() 52 | 53 | if "$ref" in data: 54 | return data 55 | fullname = ctx.full_name() 56 | 57 | if ctx.path[0] != fullname: 58 | data[X_MARSHMALLOW_INLINE] = ctx.path[0] 59 | ctx.save_object(fullname, data) 60 | 61 | return self.return_definition(data, fullname, typ="object") 62 | 63 | def on_array_has_items(self, data, ctx): 64 | if "$ref" in data["items"]: 65 | return data 66 | fullname = ctx.full_name() 67 | ctx.add_array_item() 68 | data["items"] = self._extract(data["items"], ctx, from_array=True) 69 | 70 | if ctx.path[0] != fullname: 71 | data[X_MARSHMALLOW_INLINE] = ctx.path[0] 72 | ctx.save_array(fullname, data) 73 | 74 | ctx.pop_name() 75 | return self.return_definition(data, fullname, typ="array") 76 | 77 | 78 | class MyHandler(Handler): 79 | def add_name(self, name): 80 | self.path.append(titleize(name)) 81 | 82 | 83 | # TODO: separate implementation 84 | # TODO: handling, paths,response,params 85 | def lifting_definition(data, replace=True, *, a=Accessor()): 86 | if "definitions" in data: 87 | definitions = data["definitions"] 88 | w = Flattener(replace=replace, prefix="#/definitions/") 89 | elif "components" in data: 90 | definitions = data["components"] 91 | if "schemas" not in definitions: 92 | return data 93 | definitions = definitions["schemas"] 94 | w = Flattener(replace=replace, prefix="#/components/schemas/") 95 | else: 96 | return data 97 | 98 | for name in list(definitions.keys()): 99 | prop = definitions.pop(name) 100 | extracted = w.extract(prop, MyHandler([name])) 101 | extracted[name] = prop 102 | definitions.update(reversed(list(extracted.items()))) 103 | return data 104 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/loading.py: -------------------------------------------------------------------------------- 1 | from dictknife import loading # NOQA 2 | 3 | 4 | load = loading.load 5 | dump = loading.dump 6 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/schema/__init__.py: -------------------------------------------------------------------------------- 1 | from .extra import ( # noqa 2 | PrimitiveValueSchema, 3 | AdditionalPropertiesOpts, 4 | AdditionalPropertiesSchema, 5 | ) 6 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/schema/extra.py: -------------------------------------------------------------------------------- 1 | from marshmallow import Schema, SchemaOpts, fields, ValidationError 2 | from marshmallow import pre_load, pre_dump 3 | 4 | 5 | class PrimitiveValueSchema: 6 | schema_class = None 7 | key = "value" 8 | missing_value = None 9 | 10 | def __init__(self, *args, **kwargs): 11 | self.schema = self.__class__.schema_class(*args, **kwargs) 12 | 13 | def _fix_exception(self, exc): # xxx: side effect 14 | if hasattr(exc, "data") and self.key in exc.data: 15 | exc.data = exc.data[self.key] 16 | if ( 17 | hasattr(exc, "messages") 18 | and hasattr(exc.messages, "keys") 19 | and self.key in exc.messages 20 | ): 21 | exc.messages = exc.messages[self.key] 22 | exc.args = tuple([exc.messages, *exc.args[1:]]) 23 | if hasattr(exc, "valid_data") and self.key in exc.valid_data: 24 | exc.valid_data = exc.valid_data[self.key] 25 | return exc 26 | 27 | def load(self, value): # don't support many 28 | try: 29 | r = self._do_load(value) 30 | except ValidationError as e: 31 | self._fix_exception(e) 32 | raise e.with_traceback(e.__traceback__) 33 | return r.get(self.key) or self.missing_value 34 | 35 | def _do_load(self, value): 36 | data = {self.key: value} 37 | return self.schema.load(data) 38 | 39 | def dump(self, value): # don't support many 40 | try: 41 | r = self._do_dump(value) 42 | except ValidationError as e: 43 | self._fix_exception(e) 44 | raise e.with_traceback(e.__traceback__) 45 | return r.get(self.key) or self.missing_value 46 | 47 | def _do_dump(self, value): 48 | data = {self.key: value} 49 | return self.schema.dump(data) 50 | 51 | 52 | class AdditionalPropertiesOpts(SchemaOpts): 53 | def __init__(self, meta, **kwargs): 54 | super().__init__(meta, **kwargs) 55 | self.additional_field = getattr(meta, "additional_field", fields.Field) 56 | 57 | 58 | def make_additional_properties_schema_class(Schema): 59 | class AdditionalPropertiesSchema(Schema): 60 | """ 61 | support addtionalProperties 62 | 63 | class MySchema(AdditionalPropertiesSchema): 64 | class Meta: 65 | additional_field = fields.Integer() 66 | """ 67 | 68 | OPTIONS_CLASS = AdditionalPropertiesOpts 69 | 70 | @pre_load 71 | def wrap_load_dynamic_additionals(self, data, *, many=False, partial=False): 72 | diff = set(data.keys()).difference(self.load_fields.keys()) 73 | for name in diff: 74 | f = self.opts.additional_field 75 | self.load_fields[name] = f() if callable(f) else f 76 | return data 77 | 78 | @pre_dump 79 | def wrap_dump_dynamic_additionals(self, data, *, many=False, partial=False): 80 | diff = set(data.keys()).difference(self.dump_fields.keys()) 81 | for name in diff: 82 | f = self.opts.additional_field 83 | self.dump_fields[name] = f() if callable(f) else f 84 | return data 85 | 86 | return AdditionalPropertiesSchema 87 | 88 | 89 | AdditionalPropertiesSchema = make_additional_properties_schema_class(Schema) 90 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/schema/legacy.py: -------------------------------------------------------------------------------- 1 | from collections import namedtuple 2 | 3 | import marshmallow 4 | 5 | from . import extra 6 | 7 | 8 | #: Return type of :meth:`Schema.dump` including serialized data and errors 9 | MarshalResult = namedtuple("MarshalResult", ["data", "errors"]) 10 | #: Return type of :meth:`Schema.load`, including deserialized data and errors 11 | UnmarshalResult = namedtuple("UnmarshalResult", ["data", "errors"]) 12 | 13 | 14 | class LegacySchema(marshmallow.Schema): 15 | """legacy version""" 16 | 17 | strict = None 18 | 19 | def __init__(self, strict=None, **kwargs): 20 | if strict is None: 21 | strict = self.__class__.strict 22 | self.strict = strict 23 | super().__init__(**kwargs) 24 | 25 | def dump(self, obj, many=None): 26 | if many is None: 27 | many = self.many 28 | try: 29 | d = super().dump(obj, many=many) 30 | return MarshalResult(data=d, errors=None) 31 | except marshmallow.ValidationError as e: 32 | if self.strict: 33 | raise 34 | d = [] if many else {} 35 | return MarshalResult(data=d, errors=e) 36 | 37 | def dumps(self, obj, many=None, *args, **kwargs): 38 | if many is None: 39 | many = self.many 40 | try: 41 | d = super().dumps(obj, *args, many=many, **kwargs) 42 | return MarshalResult(data=d, errors=None) 43 | except marshmallow.ValidationError as e: 44 | if self.strict: 45 | raise 46 | d = [] if many else {} 47 | d = self.opts.render_module.dumps(d, *args, **kwargs) 48 | return MarshalResult(data=d, errors=e) 49 | 50 | def load(self, data, many=None, partial=None, unknown=None): 51 | if many is None: 52 | many = self.many 53 | try: 54 | d = super().load(data, many=many, partial=partial, unknown=unknown) 55 | return UnmarshalResult(data=d, errors=None) 56 | except marshmallow.ValidationError as e: 57 | if self.strict: 58 | raise 59 | d = [] if many else {} 60 | return UnmarshalResult(data=d, errors=e) 61 | 62 | def loads(self, json_data, many=None, partial=None, unknown=None, **kwargs): 63 | if many is None: 64 | many = self.many 65 | data = self.opts.render_module.loads(json_data, **kwargs) 66 | try: 67 | d = super().load(data, many=many, partial=partial, unknown=unknown) 68 | return UnmarshalResult(data=d, errors=None) 69 | except marshmallow.ValidationError as e: 70 | if self.strict: 71 | raise 72 | d = [] if many else {} 73 | return UnmarshalResult(data=d, errors=e) 74 | 75 | 76 | class LegacyPrimitiveValueSchema(extra.PrimitiveValueSchema): 77 | strict = None 78 | 79 | def __init__(self, strict=None, **kwargs): 80 | if strict is None: 81 | strict = self.__class__.strict 82 | self.strict = strict 83 | super().__init__(**kwargs) 84 | 85 | def _fix_return_value(self, r): 86 | data = r.data 87 | if data and self.key in data: 88 | data = data[self.key] 89 | errors = r.errors 90 | if errors: 91 | errors = self._fix_exception(errors) 92 | return r.__class__(data=data, errors=errors) 93 | 94 | def load(self, value): # don't support many 95 | r = super()._do_load(value) 96 | return self._fix_return_value(r) 97 | 98 | def dump(self, value): # don't support many 99 | r = super()._do_dump(value) 100 | return self._fix_return_value(r) 101 | 102 | 103 | AdditionalPropertiesOpts = extra.AdditionalPropertiesOpts 104 | 105 | LegacyAdditionalPropertiesSchema = extra.make_additional_properties_schema_class( 106 | marshmallow.Schema 107 | ) 108 | 109 | Schema = LegacySchema 110 | AdditionalPropertiesSchema = LegacyAdditionalPropertiesSchema 111 | PrimitiveValueSchema = LegacyPrimitiveValueSchema 112 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/codegen_candidates.py: -------------------------------------------------------------------------------- 1 | CANDIDATES = [ 2 | ("simple-object", "00simple-object.json", "00simple-object.py"), 3 | ("simple-primitive", "01simple-primitive.json", "01simple-primitive.py"), 4 | ("nesting", "02nesting.json", "02nesting.py"), 5 | ("self-nesting", "03self-nesting.json", "03self-nesting.py"), 6 | ("inline-nesting", "04inline-nesting.json", "04inline-nesting.py"), 7 | ( 8 | "additionalProperties-without-properties", 9 | "05additionalProperties-without-properties.json", 10 | "05additionalProperties-without-properties.py", 11 | ), 12 | ( 13 | "additionalProperties", 14 | "06additionalProperties.json", 15 | "06additionalProperties.py", 16 | ), 17 | ( 18 | "additionalProperties-with-bool", 19 | "07additionalProperties-with-bool.json", 20 | "07additionalProperties-with-bool.py", 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/00additional.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | from swagger_marshmallow_codegen.schema import AdditionalPropertiesSchema 6 | 7 | 8 | class Box(AdditionalPropertiesSchema): 9 | name = fields.String() 10 | 11 | class Meta: 12 | additional_field = fields.Integer() 13 | 14 | 15 | 16 | class Box2(AdditionalPropertiesSchema): 17 | name = fields.String() 18 | 19 | class Meta: 20 | additional_field = fields.Nested(lambda: Box()) 21 | 22 | 23 | 24 | class Box3(AdditionalPropertiesSchema): 25 | name = fields.String() 26 | 27 | class Meta: 28 | additional_field = fields.String() 29 | 30 | 31 | 32 | class Box4(Schema): 33 | box = fields.Dict(keys=fields.String(), values=fields.String()) 34 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/00allOf.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | from marshmallow.validate import OneOf 6 | from swagger_marshmallow_codegen.validate import Range 7 | 8 | 9 | class Pet(Schema): 10 | name = fields.String(required=True) 11 | petType = fields.String(required=True) 12 | 13 | 14 | class Cat(Pet): 15 | """A representation of a cat""" 16 | huntingSkill = fields.String(required=True, description='The measured skill for hunting', validate=[OneOf(choices=['clueless', 'lazy', 'adventurous', 'aggressive'], labels=[])]) 17 | 18 | 19 | class Dog(Pet): 20 | """A representation of a dog""" 21 | packSize = fields.Integer(required=True, description='the size of the pack the dog is from', validate=[Range(min=0, max=None, min_inclusive=True, max_inclusive=True)]) 22 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/00allOf2.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | 6 | 7 | class Person0(Schema): 8 | """original (no. 1)""" 9 | name = fields.String() 10 | 11 | 12 | class Mix(Schema): 13 | """no. 2""" 14 | age = fields.Integer() 15 | 16 | 17 | class Person(Person0, Mix): 18 | pass 19 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/00commit.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | 6 | 7 | class Commit(Schema): 8 | author = fields.Nested(lambda: CommitAuthor()) 9 | commit = fields.Nested(lambda: CommitCommit()) 10 | files = fields.List(fields.Nested(lambda: CommitFilesItem())) 11 | 12 | 13 | class CommitFilesItem(Schema): 14 | additions = fields.Integer() 15 | blob_url = fields.String() 16 | changes = fields.Integer() 17 | deletions = fields.Integer() 18 | filename = fields.String() 19 | patch = fields.String() 20 | raw_url = fields.String() 21 | status = fields.String() 22 | 23 | 24 | class CommitCommit(Schema): 25 | author = fields.Nested(lambda: CommitCommitAuthor()) 26 | committer = fields.Nested(lambda: CommitCommitCommitter()) 27 | message = fields.String() 28 | tree = fields.Nested(lambda: CommitCommitTree()) 29 | url = fields.String() 30 | 31 | 32 | class CommitCommitTree(Schema): 33 | sha = fields.String() 34 | url = fields.String() 35 | 36 | 37 | class CommitCommitCommitter(Schema): 38 | date = fields.String(description='ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ') 39 | email = fields.String() 40 | name = fields.String() 41 | 42 | 43 | class CommitCommitAuthor(Schema): 44 | date = fields.String(description='ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ') 45 | email = fields.String() 46 | name = fields.String() 47 | 48 | 49 | class CommitAuthor(Schema): 50 | avatar_url = fields.String() 51 | gravatar_id = fields.String() 52 | id = fields.Integer() 53 | login = fields.String() 54 | url = fields.String() 55 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/00default.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | import datetime 6 | from collections import OrderedDict 7 | 8 | 9 | class X(Schema): 10 | string = fields.String(missing=lambda: 'default') 11 | integer = fields.Integer(missing=lambda: 10) 12 | boolean = fields.Boolean(missing=lambda: True) 13 | datetime = fields.AwareDateTime(missing=lambda: datetime.datetime(2000, 1, 1, 1, 1, 1, tzinfo=datetime.timezone.utc)) 14 | date = fields.Date(missing=lambda: datetime.date(2000, 1, 1)) 15 | object = fields.Nested(lambda: XObject(), missing=lambda: OrderedDict([('name', 'foo'), ('age', 20)])) 16 | array = fields.List(fields.Integer(), missing=lambda: [1, 2, 3]) 17 | 18 | 19 | class XObject(Schema): 20 | name = fields.String(missing=lambda: 'foo') 21 | age = fields.Integer(missing=lambda: 20) 22 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/00emojis.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | 6 | 7 | class Emojis(Schema): 8 | n100 = fields.String(data_key='100') 9 | n1234 = fields.String(data_key='1234') 10 | n1 = fields.String(data_key='1') 11 | x_1 = fields.String(data_key='-1') 12 | n8ball = fields.String(data_key='8ball') 13 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/00empty.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | from marshmallow import ( 3 | Schema, 4 | fields, 5 | ) 6 | 7 | 8 | class Empty(Schema): 9 | pass 10 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/00enum.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | from marshmallow.validate import OneOf 6 | from swagger_marshmallow_codegen.validate import MultipleOf 7 | 8 | 9 | class Person(Schema): 10 | name = fields.String(required=True) 11 | money = fields.Integer(validate=[OneOf(choices=[1, 5, 10, 50, 100, 500, 1000, 5000, 10000], labels=[])]) 12 | deposit = fields.Integer(validate=[MultipleOf(n=10000)]) 13 | color = fields.String(required=True, validate=[OneOf(choices=['R', 'G', 'B'], labels=[])]) 14 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/00items.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | from swagger_marshmallow_codegen.validate import ( 6 | ItemsRange, 7 | Unique, 8 | ) 9 | 10 | 11 | class A(Schema): 12 | nums = fields.List(fields.Integer(), validate=[ItemsRange(min=1, max=10, min_inclusive=True, max_inclusive=True), Unique()]) 13 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/00length.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | from marshmallow.validate import Length 6 | 7 | 8 | class X(Schema): 9 | s0 = fields.String() 10 | s1 = fields.String(validate=[Length(min=None, max=10, equal=None)]) 11 | s2 = fields.String(validate=[Length(min=5, max=None, equal=None)]) 12 | s3 = fields.String(validate=[Length(min=5, max=10, equal=None)]) 13 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/00list_with_options.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | from marshmallow.validate import OneOf 6 | 7 | 8 | class Member(Schema): 9 | name = fields.String() 10 | color = fields.String(validate=[OneOf(choices=['r', 'g', 'b'], labels=[])]) 11 | xs = fields.List(fields.String(validate=[OneOf(choices=['x', 'y', 'z'], labels=[])])) 12 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/00maximum.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | from swagger_marshmallow_codegen.validate import Range 6 | 7 | 8 | class X(Schema): 9 | n0 = fields.Number(validate=[Range(min=None, max=100, min_inclusive=True, max_inclusive=True)]) 10 | n1 = fields.Number(validate=[Range(min=None, max=100, min_inclusive=True, max_inclusive=False)]) 11 | n2 = fields.Number(validate=[Range(min=None, max=100, min_inclusive=True, max_inclusive=True)]) 12 | m0 = fields.Number(validate=[Range(min=100, max=None, min_inclusive=True, max_inclusive=True)]) 13 | m1 = fields.Number(validate=[Range(min=100, max=None, min_inclusive=False, max_inclusive=True)]) 14 | m2 = fields.Number(validate=[Range(min=100, max=None, min_inclusive=True, max_inclusive=True)]) 15 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/00nullable.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | 6 | 7 | class Person(Schema): 8 | name = fields.String() 9 | age = fields.Integer(allow_none=True) 10 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/00paths.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | from marshmallow.validate import ( 6 | Length, 7 | Regexp, 8 | ) 9 | import re 10 | from swagger_marshmallow_codegen.validate import Range 11 | 12 | 13 | class Pet(Schema): 14 | id = fields.String(description='Unique identifier', dump_only=True) 15 | name = fields.String(required=True, description="Pet's name", validate=[Length(min=1, max=100, equal=None)]) 16 | animal_type = fields.String(required=True, description='Kind of animal', validate=[Length(min=1, max=None, equal=None)]) 17 | tags = fields.Field(description='Custom tags') 18 | created = fields.AwareDateTime(description='Creation time', dump_only=True) 19 | 20 | 21 | class PetsInput: 22 | class Get: 23 | """ 24 | Get all pets 25 | """ 26 | 27 | class Query(Schema): 28 | animal_type = fields.String(validate=[Regexp(regex=re.compile('^[a-zA-Z0-9]*$'))]) 29 | limit = fields.Integer(missing=lambda: 100, validate=[Range(min=0, max=None, min_inclusive=True, max_inclusive=True)]) 30 | 31 | 32 | 33 | 34 | class PetsPetIdInput: 35 | class Get: 36 | """ 37 | Get a single pet 38 | """ 39 | 40 | class Path(Schema): 41 | pet_id = fields.String(required=True, description="Pet's Unique identifier", validate=[Regexp(regex=re.compile('^[a-zA-Z0-9-]+$'))]) 42 | 43 | 44 | class Put: 45 | """ 46 | Create or update a pet 47 | """ 48 | 49 | class Body(Pet): 50 | pass 51 | 52 | class Path(Schema): 53 | pet_id = fields.String(required=True, description="Pet's Unique identifier", validate=[Regexp(regex=re.compile('^[a-zA-Z0-9-]+$'))]) 54 | 55 | 56 | class Delete: 57 | """ 58 | Remove a pet 59 | """ 60 | 61 | class Path(Schema): 62 | pet_id = fields.String(required=True, description="Pet's Unique identifier", validate=[Regexp(regex=re.compile('^[a-zA-Z0-9-]+$'))]) 63 | 64 | 65 | 66 | 67 | class PetsOutput: 68 | class Get200(Pet): 69 | """Return pets""" 70 | def __init__(self, *args, **kwargs): 71 | kwargs['many'] = True 72 | super().__init__(*args, **kwargs) 73 | 74 | 75 | 76 | 77 | class PetsPetIdOutput: 78 | class Get200(Pet): 79 | """Return pet""" 80 | pass 81 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/00patternProperties.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/podhmo/swagger-marshmallow-codegen/a21787d49f627c75241eb3a56a03b2d5107dfba4/swagger_marshmallow_codegen/tests/legacy_dst/00patternProperties.py -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/00person.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | 6 | 7 | class Person(Schema): 8 | name = fields.String() 9 | age = fields.Integer() 10 | 11 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/00primitiveapi.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | from swagger_marshmallow_codegen.schema import PrimitiveValueSchema 6 | 7 | 8 | class IntsInput: 9 | class Get: 10 | pass 11 | 12 | class Post: 13 | class Body(PrimitiveValueSchema): 14 | class schema_class(Schema): 15 | value = fields.List(fields.Integer()) 16 | 17 | 18 | 19 | 20 | 21 | class IntsOutput: 22 | class Get200(PrimitiveValueSchema): 23 | class schema_class(Schema): 24 | value = fields.List(fields.Integer()) 25 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/00readonly.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | 6 | 7 | class Foo(Schema): 8 | value0 = fields.String() 9 | value1 = fields.String() 10 | value2 = fields.String(dump_only=True) 11 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/00regex.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | import re 6 | from marshmallow.validate import ( 7 | Regexp, 8 | Length, 9 | ) 10 | 11 | 12 | class X(Schema): 13 | team = fields.String(validate=[Regexp(regex=re.compile('team[1-9][0-9]+'))]) 14 | team2 = fields.String(validate=[Length(min=None, max=10, equal=None), Regexp(regex=re.compile('team[1-9][0-9]+'))]) 15 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/00reserved.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | 6 | 7 | class Reserved(Schema): 8 | not_ = fields.Boolean(data_key='not') 9 | as_ = fields.String(data_key='as') 10 | in_ = fields.List(fields.String(), data_key='in') 11 | yield_ = fields.String(data_key='yield') 12 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/00stat.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | 6 | 7 | class Stat(Schema): 8 | days = fields.List(fields.Integer()) 9 | days2 = fields.List(fields.Integer()) 10 | total = fields.Integer() 11 | week = fields.Integer() 12 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/00typearray.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | 6 | 7 | class Foo(Schema): 8 | value0 = fields.String() 9 | value1 = fields.String(allow_none=True) 10 | value2 = fields.String(allow_none=True) 11 | value3 = fields.String() 12 | value4 = fields.Integer() 13 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/00x_marshmallow_name.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | 6 | 7 | class Message(Schema): 8 | message = fields.String(required=True) 9 | 10 | 11 | class MessageNameInput: 12 | class Get: 13 | """ 14 | Get a message of the day 15 | """ 16 | 17 | class Path(Schema): 18 | name = fields.String(description='name to include in the message') 19 | 20 | 21 | 22 | 23 | class MessageNameOutput: 24 | class Get200(Message): 25 | """Successful response""" 26 | pass 27 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/01additional.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | from swagger_marshmallow_codegen.schema import AdditionalPropertiesSchema 6 | 7 | 8 | class Person(Schema): 9 | name = fields.String() 10 | 11 | 12 | class Data(AdditionalPropertiesSchema): 13 | 14 | class Meta: 15 | additional_field = fields.List(fields.Nested(lambda: Person())) 16 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/01allOf2.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | 6 | 7 | class Person0(Schema): 8 | """original (no. 1)""" 9 | name = fields.String() 10 | 11 | 12 | class Person(Person0): 13 | """no. 3""" 14 | age = fields.Integer() 15 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/01commit.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | 6 | 7 | class Commit(Schema): 8 | author = fields.Nested(lambda: CommitAuthor()) 9 | commit = fields.Nested(lambda: CommitCommit()) 10 | files = fields.List(fields.Nested(lambda: CommitFilesItem())) 11 | 12 | 13 | class CommitFilesItem(Schema): 14 | additions = fields.Integer() 15 | blob_url = fields.String() 16 | changes = fields.Integer() 17 | deletions = fields.Integer() 18 | filename = fields.String() 19 | patch = fields.String() 20 | raw_url = fields.String() 21 | status = fields.String() 22 | 23 | 24 | class CommitCommit(Schema): 25 | author = fields.Nested(lambda: CommitCommitAuthor()) 26 | committer = fields.Nested(lambda: CommitCommitCommitter()) 27 | message = fields.String() 28 | tree = fields.Nested(lambda: CommitCommitTree()) 29 | url = fields.String() 30 | 31 | 32 | class CommitCommitTree(Schema): 33 | sha = fields.String() 34 | url = fields.String() 35 | 36 | 37 | class CommitCommitCommitter(Schema): 38 | date = fields.String(description='ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ') 39 | email = fields.String() 40 | name = fields.String() 41 | 42 | 43 | class CommitCommitAuthor(Schema): 44 | date = fields.String(description='ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ') 45 | email = fields.String() 46 | name = fields.String() 47 | 48 | 49 | class CommitAuthor(Schema): 50 | avatar_url = fields.String() 51 | gravatar_id = fields.String() 52 | id = fields.Integer() 53 | login = fields.String() 54 | url = fields.String() 55 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/01empty.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | 6 | 7 | class Wrap(Schema): 8 | empties = fields.List(fields.Nested(lambda: WrapEmptiesItem())) 9 | 10 | 11 | class WrapEmptiesItem(Schema): 12 | pass 13 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/01paths.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | from marshmallow.validate import ( 6 | Length, 7 | Regexp, 8 | ) 9 | from swagger_marshmallow_codegen.schema import PrimitiveValueSchema 10 | import re 11 | 12 | 13 | class Label(Schema): 14 | color = fields.String(validate=[Length(min=6, max=6, equal=None)]) 15 | name = fields.String() 16 | url = fields.String() 17 | 18 | 19 | class ReposOwnerRepoIssuesNumberLabelsInput: 20 | class Delete: 21 | """ 22 | Remove all labels from an issue. 23 | """ 24 | 25 | class Header(Schema): 26 | X_GitHub_Media_Type = fields.String(data_key='X-GitHub-Media-Type', description='You can check the current version of media type in responses.\n') 27 | Accept = fields.String(description='Is used to set specified media type.') 28 | X_RateLimit_Limit = fields.Integer(data_key='X-RateLimit-Limit') 29 | X_RateLimit_Remaining = fields.Integer(data_key='X-RateLimit-Remaining') 30 | X_RateLimit_Reset = fields.Integer(data_key='X-RateLimit-Reset') 31 | X_GitHub_Request_Id = fields.Integer(data_key='X-GitHub-Request-Id') 32 | 33 | class Path(Schema): 34 | owner = fields.String(required=True, description='Name of repository owner.') 35 | repo = fields.String(required=True, description='Name of repository.') 36 | number = fields.Integer(required=True, description='Number of issue.') 37 | 38 | 39 | class Get: 40 | """ 41 | List labels on an issue. 42 | """ 43 | 44 | class Header(Schema): 45 | X_GitHub_Media_Type = fields.String(data_key='X-GitHub-Media-Type', description='You can check the current version of media type in responses.\n') 46 | Accept = fields.String(description='Is used to set specified media type.') 47 | X_RateLimit_Limit = fields.Integer(data_key='X-RateLimit-Limit') 48 | X_RateLimit_Remaining = fields.Integer(data_key='X-RateLimit-Remaining') 49 | X_RateLimit_Reset = fields.Integer(data_key='X-RateLimit-Reset') 50 | X_GitHub_Request_Id = fields.Integer(data_key='X-GitHub-Request-Id') 51 | 52 | class Path(Schema): 53 | owner = fields.String(required=True, description='Name of repository owner.') 54 | repo = fields.String(required=True, description='Name of repository.') 55 | number = fields.Integer(required=True, description='Number of issue.') 56 | 57 | 58 | class Post: 59 | """ 60 | Add labels to an issue. 61 | """ 62 | 63 | class Body(PrimitiveValueSchema): 64 | class schema_class(Schema): 65 | value = fields.List(fields.String(validate=[Regexp(regex=re.compile('.+@.+'))])) 66 | 67 | 68 | class Header(Schema): 69 | X_GitHub_Media_Type = fields.String(data_key='X-GitHub-Media-Type', description='You can check the current version of media type in responses.\n') 70 | Accept = fields.String(description='Is used to set specified media type.') 71 | X_RateLimit_Limit = fields.Integer(data_key='X-RateLimit-Limit') 72 | X_RateLimit_Remaining = fields.Integer(data_key='X-RateLimit-Remaining') 73 | X_RateLimit_Reset = fields.Integer(data_key='X-RateLimit-Reset') 74 | X_GitHub_Request_Id = fields.Integer(data_key='X-GitHub-Request-Id') 75 | 76 | class Path(Schema): 77 | owner = fields.String(required=True, description='Name of repository owner.') 78 | repo = fields.String(required=True, description='Name of repository.') 79 | number = fields.Integer(required=True, description='Number of issue.') 80 | 81 | 82 | class Put: 83 | """ 84 | Replace all labels for an issue. 85 | Sending an empty array ([]) will remove all Labels from the Issue. 86 | """ 87 | 88 | class Body(PrimitiveValueSchema): 89 | class schema_class(Schema): 90 | value = fields.List(fields.String(validate=[Regexp(regex=re.compile('.+@.+'))])) 91 | 92 | 93 | class Header(Schema): 94 | X_GitHub_Media_Type = fields.String(data_key='X-GitHub-Media-Type', description='You can check the current version of media type in responses.\n') 95 | Accept = fields.String(description='Is used to set specified media type.') 96 | X_RateLimit_Limit = fields.Integer(data_key='X-RateLimit-Limit') 97 | X_RateLimit_Remaining = fields.Integer(data_key='X-RateLimit-Remaining') 98 | X_RateLimit_Reset = fields.Integer(data_key='X-RateLimit-Reset') 99 | X_GitHub_Request_Id = fields.Integer(data_key='X-GitHub-Request-Id') 100 | 101 | class Path(Schema): 102 | owner = fields.String(required=True, description='Name of repository owner.') 103 | repo = fields.String(required=True, description='Name of repository.') 104 | number = fields.Integer(required=True, description='Number of issue.') 105 | 106 | 107 | 108 | 109 | class ReposOwnerRepoIssuesNumberLabelsOutput: 110 | class Get200(Label): 111 | """OK""" 112 | def __init__(self, *args, **kwargs): 113 | kwargs['many'] = True 114 | super().__init__(*args, **kwargs) 115 | 116 | 117 | class Post201(Label): 118 | """Created""" 119 | pass 120 | 121 | class Put201(Label): 122 | """Created""" 123 | pass 124 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/01person.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | 6 | 7 | class Person(Schema): 8 | name = fields.String(required=True) 9 | age = fields.Integer() 10 | 11 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/01x_marshmallow_name.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | 6 | 7 | class Message(Schema): 8 | message = fields.String(required=True) 9 | 10 | 11 | class MessageInput: 12 | class Get: 13 | """ 14 | Get a message of the day 15 | """ 16 | 17 | class Path(Schema): 18 | name = fields.String(description='name to include in the message') 19 | 20 | 21 | 22 | 23 | class MessageOutput: 24 | class Get200(Message): 25 | """Successful response""" 26 | pass 27 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/02additional.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | 6 | 7 | class S(Schema): 8 | x = fields.Dict(keys=fields.String(), values=fields.Integer()) 9 | y = fields.Dict(keys=fields.String(), values=fields.Integer()) 10 | z = fields.Dict(keys=fields.String(), values=fields.Integer()) 11 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/02allOf2.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | 6 | 7 | class Person0(Schema): 8 | """original (no. 1)""" 9 | name = fields.String() 10 | 11 | 12 | class Person(Person0): 13 | """no. 4""" 14 | age = fields.Integer() 15 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/02paths.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | from marshmallow.validate import ( 6 | Length, 7 | Regexp, 8 | ) 9 | from swagger_marshmallow_codegen.schema import PrimitiveValueSchema 10 | import re 11 | 12 | 13 | class Label(Schema): 14 | color = fields.String(validate=[Length(min=6, max=6, equal=None)]) 15 | name = fields.String() 16 | url = fields.String() 17 | 18 | 19 | class IssuedLabelsInput: 20 | class Delete: 21 | """ 22 | Remove all labels from an issue. 23 | """ 24 | 25 | class Header(Schema): 26 | X_GitHub_Media_Type = fields.String(data_key='X-GitHub-Media-Type', description='You can check the current version of media type in responses.\n') 27 | Accept = fields.String(description='Is used to set specified media type.') 28 | X_RateLimit_Limit = fields.Integer(data_key='X-RateLimit-Limit') 29 | X_RateLimit_Remaining = fields.Integer(data_key='X-RateLimit-Remaining') 30 | X_RateLimit_Reset = fields.Integer(data_key='X-RateLimit-Reset') 31 | X_GitHub_Request_Id = fields.Integer(data_key='X-GitHub-Request-Id') 32 | 33 | class Path(Schema): 34 | owner = fields.String(required=True, description='Name of repository owner.') 35 | repo = fields.String(required=True, description='Name of repository.') 36 | number = fields.Integer(required=True, description='Number of issue.') 37 | 38 | 39 | class Get: 40 | """ 41 | List labels on an issue. 42 | """ 43 | 44 | class Header(Schema): 45 | X_GitHub_Media_Type = fields.String(data_key='X-GitHub-Media-Type', description='You can check the current version of media type in responses.\n') 46 | Accept = fields.String(description='Is used to set specified media type.') 47 | X_RateLimit_Limit = fields.Integer(data_key='X-RateLimit-Limit') 48 | X_RateLimit_Remaining = fields.Integer(data_key='X-RateLimit-Remaining') 49 | X_RateLimit_Reset = fields.Integer(data_key='X-RateLimit-Reset') 50 | X_GitHub_Request_Id = fields.Integer(data_key='X-GitHub-Request-Id') 51 | 52 | class Path(Schema): 53 | owner = fields.String(required=True, description='Name of repository owner.') 54 | repo = fields.String(required=True, description='Name of repository.') 55 | number = fields.Integer(required=True, description='Number of issue.') 56 | 57 | 58 | class Post: 59 | """ 60 | Add labels to an issue. 61 | """ 62 | 63 | class Body(PrimitiveValueSchema): 64 | class schema_class(Schema): 65 | value = fields.List(fields.String(validate=[Regexp(regex=re.compile('.+@.+'))])) 66 | 67 | 68 | class Header(Schema): 69 | X_GitHub_Media_Type = fields.String(data_key='X-GitHub-Media-Type', description='You can check the current version of media type in responses.\n') 70 | Accept = fields.String(description='Is used to set specified media type.') 71 | X_RateLimit_Limit = fields.Integer(data_key='X-RateLimit-Limit') 72 | X_RateLimit_Remaining = fields.Integer(data_key='X-RateLimit-Remaining') 73 | X_RateLimit_Reset = fields.Integer(data_key='X-RateLimit-Reset') 74 | X_GitHub_Request_Id = fields.Integer(data_key='X-GitHub-Request-Id') 75 | 76 | class Path(Schema): 77 | owner = fields.String(required=True, description='Name of repository owner.') 78 | repo = fields.String(required=True, description='Name of repository.') 79 | number = fields.Integer(required=True, description='Number of issue.') 80 | 81 | 82 | class Put: 83 | """ 84 | Replace all labels for an issue. 85 | Sending an empty array ([]) will remove all Labels from the Issue. 86 | """ 87 | 88 | class Body(PrimitiveValueSchema): 89 | class schema_class(Schema): 90 | value = fields.List(fields.String(validate=[Regexp(regex=re.compile('.+@.+'))])) 91 | 92 | 93 | class Header(Schema): 94 | X_GitHub_Media_Type = fields.String(data_key='X-GitHub-Media-Type', description='You can check the current version of media type in responses.\n') 95 | Accept = fields.String(description='Is used to set specified media type.') 96 | X_RateLimit_Limit = fields.Integer(data_key='X-RateLimit-Limit') 97 | X_RateLimit_Remaining = fields.Integer(data_key='X-RateLimit-Remaining') 98 | X_RateLimit_Reset = fields.Integer(data_key='X-RateLimit-Reset') 99 | X_GitHub_Request_Id = fields.Integer(data_key='X-GitHub-Request-Id') 100 | 101 | class Path(Schema): 102 | owner = fields.String(required=True, description='Name of repository owner.') 103 | repo = fields.String(required=True, description='Name of repository.') 104 | number = fields.Integer(required=True, description='Number of issue.') 105 | 106 | 107 | 108 | 109 | class IssuedLabelsOutput: 110 | class Get200(Label): 111 | """OK""" 112 | def __init__(self, *args, **kwargs): 113 | kwargs['many'] = True 114 | super().__init__(*args, **kwargs) 115 | 116 | 117 | class Post201(Label): 118 | """Created""" 119 | pass 120 | 121 | class Put201(Label): 122 | """Created""" 123 | pass 124 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/02person.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | 6 | 7 | class Person(Schema): 8 | name = fields.String(required=True, description='name of something') 9 | age = fields.Integer(description='age') 10 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/03additional.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | 6 | 7 | class S(Schema): 8 | x = fields.Dict(keys=fields.String(), values=fields.Integer()) 9 | y = fields.Dict(keys=fields.String(), values=fields.Integer()) 10 | y2 = fields.Dict(keys=fields.String(), values=fields.Integer()) 11 | z = fields.Dict(keys=fields.String(), values=fields.Integer()) 12 | z2 = fields.Dict(keys=fields.String(), values=fields.Dict(keys=fields.String(), values=fields.Integer)) 13 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/03paths.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | from marshmallow.validate import ( 6 | Length, 7 | Regexp, 8 | ) 9 | from swagger_marshmallow_codegen.schema import PrimitiveValueSchema 10 | import re 11 | 12 | 13 | class Label(Schema): 14 | color = fields.String(validate=[Length(min=6, max=6, equal=None)]) 15 | name = fields.String() 16 | url = fields.String() 17 | 18 | 19 | class IssuedLabelsInput: 20 | class Delete: 21 | """ 22 | Remove all labels from an issue. 23 | """ 24 | 25 | class Header(Schema): 26 | X_GitHub_Media_Type = fields.String(data_key='X-GitHub-Media-Type', description='You can check the current version of media type in responses.\n') 27 | Accept = fields.String(description='Is used to set specified media type.') 28 | X_RateLimit_Limit = fields.Integer(data_key='X-RateLimit-Limit') 29 | X_RateLimit_Remaining = fields.Integer(data_key='X-RateLimit-Remaining') 30 | X_RateLimit_Reset = fields.Integer(data_key='X-RateLimit-Reset') 31 | X_GitHub_Request_Id = fields.Integer(data_key='X-GitHub-Request-Id') 32 | 33 | class Path(Schema): 34 | owner = fields.String(required=True, description='Name of repository owner.') 35 | repo = fields.String(required=True, description='Name of repository.') 36 | number = fields.Integer(required=True, description='Number of issue.') 37 | 38 | 39 | class Get: 40 | """ 41 | List labels on an issue. 42 | """ 43 | 44 | class Header(Schema): 45 | X_GitHub_Media_Type = fields.String(data_key='X-GitHub-Media-Type', description='You can check the current version of media type in responses.\n') 46 | Accept = fields.String(description='Is used to set specified media type.') 47 | X_RateLimit_Limit = fields.Integer(data_key='X-RateLimit-Limit') 48 | X_RateLimit_Remaining = fields.Integer(data_key='X-RateLimit-Remaining') 49 | X_RateLimit_Reset = fields.Integer(data_key='X-RateLimit-Reset') 50 | X_GitHub_Request_Id = fields.Integer(data_key='X-GitHub-Request-Id') 51 | 52 | class Path(Schema): 53 | owner = fields.String(required=True, description='Name of repository owner.') 54 | repo = fields.String(required=True, description='Name of repository.') 55 | number = fields.Integer(required=True, description='Number of issue.') 56 | 57 | 58 | class Post: 59 | """ 60 | Add labels to an issue. 61 | """ 62 | 63 | class Body(PrimitiveValueSchema): 64 | class schema_class(Schema): 65 | value = fields.List(fields.String(validate=[Regexp(regex=re.compile('.+@.+'))])) 66 | 67 | 68 | class Header(Schema): 69 | X_GitHub_Media_Type = fields.String(data_key='X-GitHub-Media-Type', description='You can check the current version of media type in responses.\n') 70 | Accept = fields.String(description='Is used to set specified media type.') 71 | X_RateLimit_Limit = fields.Integer(data_key='X-RateLimit-Limit') 72 | X_RateLimit_Remaining = fields.Integer(data_key='X-RateLimit-Remaining') 73 | X_RateLimit_Reset = fields.Integer(data_key='X-RateLimit-Reset') 74 | X_GitHub_Request_Id = fields.Integer(data_key='X-GitHub-Request-Id') 75 | 76 | class Path(Schema): 77 | owner = fields.String(required=True, description='Name of repository owner.') 78 | repo = fields.String(required=True, description='Name of repository.') 79 | number = fields.Integer(required=True, description='Number of issue.') 80 | 81 | 82 | class Put: 83 | """ 84 | Replace all labels for an issue. 85 | Sending an empty array ([]) will remove all Labels from the Issue. 86 | """ 87 | 88 | class Body(PrimitiveValueSchema): 89 | class schema_class(Schema): 90 | value = fields.List(fields.String(validate=[Regexp(regex=re.compile('.+@.+'))])) 91 | 92 | 93 | class Header(Schema): 94 | X_GitHub_Media_Type = fields.String(data_key='X-GitHub-Media-Type', description='You can check the current version of media type in responses.\n') 95 | Accept = fields.String(description='Is used to set specified media type.') 96 | X_RateLimit_Limit = fields.Integer(data_key='X-RateLimit-Limit') 97 | X_RateLimit_Remaining = fields.Integer(data_key='X-RateLimit-Remaining') 98 | X_RateLimit_Reset = fields.Integer(data_key='X-RateLimit-Reset') 99 | X_GitHub_Request_Id = fields.Integer(data_key='X-GitHub-Request-Id') 100 | 101 | class Path(Schema): 102 | owner = fields.String(required=True, description='Name of repository owner.') 103 | repo = fields.String(required=True, description='Name of repository.') 104 | number = fields.Integer(required=True, description='Number of issue.') 105 | 106 | 107 | 108 | 109 | class IssuedLabelsOutput: 110 | class Get200(Label): 111 | """OK""" 112 | def __init__(self, *args, **kwargs): 113 | kwargs['many'] = True 114 | super().__init__(*args, **kwargs) 115 | 116 | 117 | class Post201(Label): 118 | """Created""" 119 | pass 120 | 121 | class Put201(Label): 122 | """Created""" 123 | pass 124 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/03person.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | 6 | 7 | class Person(Schema): 8 | name = fields.String(required=True, description='name of something') 9 | age = fields.Integer(description='age') 10 | skills = fields.List(fields.Nested(lambda: Skill())) 11 | 12 | 13 | class Skill(Schema): 14 | name = fields.String(required=True) 15 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/04person.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | 6 | 7 | class Person(Schema): 8 | name = fields.String(required=True, description='name of something') 9 | age = fields.Integer(description='age') 10 | father = fields.Nested(lambda: Person()) 11 | mother = fields.Nested(lambda: Person()) 12 | skills = fields.List(fields.Nested(lambda: Skill())) 13 | 14 | 15 | class Skill(Schema): 16 | name = fields.String(required=True) 17 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_dst/05person.py: -------------------------------------------------------------------------------- 1 | from marshmallow import ( 2 | Schema, 3 | fields, 4 | ) 5 | 6 | 7 | class Person(Schema): 8 | name = fields.String(required=True, description='name of something') 9 | age = fields.Integer(description='age') 10 | father = fields.Nested(lambda: Father()) 11 | mother = fields.Nested(lambda: Mother()) 12 | skills = fields.List(fields.Nested(lambda: Skill())) 13 | 14 | 15 | class Father(Person): 16 | pass 17 | 18 | 19 | class Mother(Person): 20 | pass 21 | 22 | 23 | class Skill(Schema): 24 | name = fields.String(required=True) 25 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/00additional.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | Box: 3 | properties: 4 | name: 5 | type: string 6 | additionalProperties: 7 | $ref: "#/definitions/value" 8 | Box2: 9 | properties: 10 | name: 11 | type: string 12 | additionalProperties: 13 | $ref: "#/definitions/Box" 14 | Box3: 15 | properties: 16 | name: 17 | type: string 18 | additionalProperties: 19 | type: string 20 | Box4: 21 | properties: 22 | box: 23 | type: object 24 | additionalProperties: 25 | type: string 26 | value: 27 | type: integer 28 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/00allOf.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | Cat: 3 | description: A representation of a cat 4 | allOf: 5 | - $ref: '#/definitions/Pet' 6 | - type: object 7 | properties: 8 | huntingSkill: 9 | type: string 10 | description: The measured skill for hunting 11 | default: lazy 12 | enum: 13 | - clueless 14 | - lazy 15 | - adventurous 16 | - aggressive 17 | required: 18 | - huntingSkill 19 | Pet: 20 | type: object 21 | discriminator: petType 22 | properties: 23 | name: 24 | type: string 25 | petType: 26 | type: string 27 | required: 28 | - name 29 | - petType 30 | Dog: 31 | description: A representation of a dog 32 | allOf: 33 | - $ref: '#/definitions/Pet' 34 | - type: object 35 | properties: 36 | packSize: 37 | type: integer 38 | format: int32 39 | description: the size of the pack the dog is from 40 | default: 0 41 | minimum: 0 42 | required: 43 | - packSize 44 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/00allOf2.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | person0: 3 | description: original (no. 1) 4 | properties: 5 | name: 6 | type: string 7 | mix: 8 | required: ["name"] 9 | description: no. 2 10 | properties: 11 | age: 12 | type: integer 13 | person: 14 | allOf: 15 | - $ref: "#/definitions/person0" 16 | - $ref: "#/definitions/mix" 17 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/00commit.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | commit: 3 | properties: 4 | author: 5 | $ref: '#/definitions/commitAuthor' 6 | commit: 7 | $ref: '#/definitions/commitCommit' 8 | files: 9 | $ref: '#/definitions/commitFiles' 10 | type: object 11 | commitFiles: 12 | x-marshmallow-inline: commit 13 | type: array 14 | items: 15 | $ref: '#/definitions/commitFilesItem' 16 | commitFilesItem: 17 | x-marshmallow-inline: commit 18 | type: object 19 | properties: 20 | additions: 21 | type: integer 22 | blob_url: 23 | type: string 24 | changes: 25 | type: integer 26 | deletions: 27 | type: integer 28 | filename: 29 | type: string 30 | patch: 31 | type: string 32 | raw_url: 33 | type: string 34 | status: 35 | type: string 36 | commitCommit: 37 | x-marshmallow-inline: commit 38 | type: object 39 | properties: 40 | author: 41 | $ref: '#/definitions/commitCommitAuthor' 42 | committer: 43 | $ref: '#/definitions/commitCommitCommitter' 44 | message: 45 | type: string 46 | tree: 47 | $ref: '#/definitions/commitCommitTree' 48 | url: 49 | type: string 50 | commitCommitTree: 51 | x-marshmallow-inline: commit 52 | type: object 53 | properties: 54 | sha: 55 | type: string 56 | url: 57 | type: string 58 | commitCommitCommitter: 59 | x-marshmallow-inline: commit 60 | type: object 61 | properties: 62 | date: 63 | description: 'ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ' 64 | type: string 65 | email: 66 | type: string 67 | name: 68 | type: string 69 | commitCommitAuthor: 70 | x-marshmallow-inline: commit 71 | type: object 72 | properties: 73 | date: 74 | description: 'ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ' 75 | type: string 76 | email: 77 | type: string 78 | name: 79 | type: string 80 | commitAuthor: 81 | x-marshmallow-inline: commit 82 | type: object 83 | properties: 84 | avatar_url: 85 | type: string 86 | gravatar_id: 87 | type: string 88 | id: 89 | type: integer 90 | login: 91 | type: string 92 | url: 93 | type: string 94 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/00default.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | x: 3 | properties: 4 | string: 5 | type: string 6 | default: "default" 7 | integer: 8 | type: integer 9 | default: 10 10 | boolean: 11 | type: boolean 12 | default: true 13 | datetime: 14 | type: string 15 | format: date-time 16 | default: 2000-01-01T01:01:01Z 17 | date: 18 | type: string 19 | format: date 20 | default: 2000-01-01 21 | object: 22 | type: object 23 | properties: 24 | name: 25 | type: string 26 | default: foo 27 | age: 28 | type: integer 29 | default: 20 30 | default: 31 | name: foo 32 | age: 20 33 | array: 34 | type: array 35 | items: 36 | type: integer 37 | default: 38 | - 1 39 | - 2 40 | - 3 41 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/00emojis.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | emojis: 3 | type: 4 | object 5 | properties: 6 | 100: 7 | type: string 8 | 1234: 9 | type: string 10 | +1: 11 | type: string 12 | -1: 13 | type: string 14 | 8ball: 15 | type: string 16 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/00empty.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | empty: 3 | type: object 4 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/00enum.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | color: 3 | type: string 4 | enum: 5 | - R 6 | - G 7 | - B 8 | yen: 9 | type: integer 10 | enum: 11 | - 1 12 | - 5 13 | - 10 14 | - 50 15 | - 100 16 | - 500 17 | - 1000 18 | - 5000 19 | - 10000 20 | huge-yen: 21 | type: integer 22 | multipleOf: 10000 23 | person: 24 | type: object 25 | required: 26 | - name 27 | - color 28 | properties: 29 | name: 30 | type: string 31 | money: 32 | $ref: "#/definitions/yen" 33 | deposit: 34 | $ref: "#/definitions/huge-yen" 35 | color: 36 | $ref: "#/definitions/color" 37 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/00items.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | A: 3 | type: object 4 | properties: 5 | nums: 6 | type: array 7 | items: 8 | type: integer 9 | maxItems: 10 10 | minItems: 1 11 | uniqueItems: true 12 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/00length.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | x: 3 | type: object 4 | properties: 5 | s0: 6 | type: string 7 | s1: 8 | type: string 9 | maxLength: 10 10 | s2: 11 | type: string 12 | minLength: 5 13 | s3: 14 | type: string 15 | maxLength: 10 16 | minLength: 5 17 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/00list_with_options.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | ys: 3 | type: array 4 | items: 5 | type: string 6 | enum: 7 | - x 8 | - y 9 | - z 10 | member: 11 | type: object 12 | properties: 13 | name: 14 | type: string 15 | color: 16 | type: string 17 | enum: 18 | - r 19 | - g 20 | - b 21 | xs: 22 | type: array 23 | items: 24 | type: string 25 | enum: 26 | - x 27 | - y 28 | - z 29 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/00maximum.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | x: 3 | type: object 4 | properties: 5 | n0: 6 | type: number 7 | maximum: 100 8 | n1: 9 | type: number 10 | maximum: 100 11 | exclusiveMaximum: true 12 | n2: 13 | type: number 14 | maximum: 100 15 | exclusiveMaximum: false 16 | m0: 17 | type: number 18 | minimum: 100 19 | m1: 20 | type: number 21 | minimum: 100 22 | exclusiveMinimum: true 23 | m2: 24 | type: number 25 | minimum: 100 26 | exclusiveMinimum: false 27 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/00nullable.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | person: 3 | properties: 4 | name: 5 | type: string 6 | age: 7 | type: integer 8 | x-nullable: true 9 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/00paths.yaml: -------------------------------------------------------------------------------- 1 | paths: 2 | /pets: 3 | x-pyramid-route-name: pets 4 | get: 5 | tags: [Pets] 6 | operationId: get_pets 7 | summary: Get all pets 8 | parameters: 9 | - name: animal_type 10 | in: query 11 | type: string 12 | pattern: "^[a-zA-Z0-9]*$" 13 | - name: limit 14 | in: query 15 | type: integer 16 | minimum: 0 17 | default: 100 18 | responses: 19 | 200: 20 | description: Return pets 21 | schema: 22 | type: array 23 | items: 24 | $ref: '#/definitions/Pet' 25 | /pets/{pet_id}: 26 | x-pyramid-route-name: pet 27 | get: 28 | tags: [Pets] 29 | operationId: get_pet 30 | summary: Get a single pet 31 | parameters: 32 | - $ref: '#/parameters/pet_id' 33 | responses: 34 | 200: 35 | description: Return pet 36 | schema: 37 | $ref: '#/definitions/Pet' 38 | 404: 39 | description: Pet does not exist 40 | put: 41 | tags: [Pets] 42 | operationId: put_pet 43 | summary: Create or update a pet 44 | parameters: 45 | - $ref: '#/parameters/pet_id' 46 | - name: pet 47 | in: body 48 | schema: 49 | $ref: '#/definitions/Pet' 50 | responses: 51 | 200: 52 | description: Pet updated 53 | 201: 54 | description: New pet created 55 | delete: 56 | tags: [Pets] 57 | operationId: delete_pet 58 | summary: Remove a pet 59 | parameters: 60 | - $ref: '#/parameters/pet_id' 61 | responses: 62 | 204: 63 | description: Pet was deleted 64 | 404: 65 | description: Pet does not exist 66 | 67 | 68 | parameters: 69 | pet_id: 70 | name: pet_id 71 | description: "Pet's Unique identifier" 72 | in: path 73 | type: string 74 | required: true 75 | pattern: "^[a-zA-Z0-9-]+$" 76 | 77 | definitions: 78 | Pet: 79 | type: object 80 | required: 81 | - name 82 | - animal_type 83 | properties: 84 | id: 85 | type: string 86 | description: Unique identifier 87 | example: "123" 88 | readOnly: true 89 | name: 90 | type: string 91 | description: Pet's name 92 | example: "Susie" 93 | minLength: 1 94 | maxLength: 100 95 | animal_type: 96 | type: string 97 | description: Kind of animal 98 | example: "cat" 99 | minLength: 1 100 | tags: 101 | type: object 102 | description: Custom tags 103 | created: 104 | type: string 105 | format: date-time 106 | description: Creation time 107 | example: "2015-07-07T15:49:51.230+02:00" 108 | readOnly: true 109 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/00patternProperties.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | scoreSheet: 3 | type: object 4 | properties: 5 | name: 6 | type: string 7 | patternProperties: 8 | ".+Score": 9 | type: integer 10 | additionalProperties: false 11 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/00person.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | person: 3 | type: object 4 | properties: 5 | name: 6 | type: string 7 | age: 8 | type: integer 9 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/00primitiveapi.yaml: -------------------------------------------------------------------------------- 1 | paths: 2 | /ints: 3 | get: 4 | responses: 5 | 200: 6 | description: ok 7 | schema: 8 | items: 9 | type: integer 10 | post: 11 | parameters: 12 | - description: ints. 13 | in: body 14 | name: body 15 | schema: 16 | items: 17 | type: integer 18 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/00readonly.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | foo: 3 | type: object 4 | properties: 5 | value0: 6 | type: string 7 | value1: 8 | type: string 9 | readOnly: false 10 | value2: 11 | type: string 12 | readOnly: true 13 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/00regex.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | x: 3 | type: object 4 | properties: 5 | team: 6 | type: string 7 | pattern: team[1-9][0-9]+ 8 | team2: 9 | type: string 10 | pattern: team[1-9][0-9]+ 11 | maxLength: 10 12 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/00reserved.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | reserved: 3 | type: object 4 | properties: 5 | not: 6 | type: boolean 7 | as: 8 | type: string 9 | in: 10 | type: array 11 | items: 12 | type: string 13 | yield: 14 | type: string 15 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/00stat.yaml: -------------------------------------------------------------------------------- 1 | # array ref with primitive type 2 | definitions: 3 | stat: 4 | properties: 5 | days: 6 | $ref: "#/definitions/days" 7 | days2: 8 | $ref: "#/definitions/days2" 9 | total: 10 | type: integer 11 | week: 12 | type: integer 13 | days: 14 | type: array 15 | items: 16 | type: integer 17 | days2: 18 | $ref: "#/definitions/days" 19 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/00typearray.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | foo: 3 | properties: 4 | value0: 5 | type: 6 | - string 7 | value1: 8 | type: 9 | - string 10 | - null 11 | value2: 12 | type: 13 | - null 14 | - string 15 | value3: 16 | type: 17 | - string 18 | - integer 19 | value4: 20 | type: 21 | - integer 22 | - string 23 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/00x_marshmallow_name.yaml: -------------------------------------------------------------------------------- 1 | paths: 2 | /message/{name}: 3 | x-summary: Message operations 4 | x-description: Operation description in Markdown 5 | get: 6 | summary: Get a message of the day 7 | description: | 8 | Description of the operation in Markdown 9 | operationId: getMessage 10 | parameters: 11 | - name: name 12 | in: path 13 | description: name to include in the message 14 | type: string 15 | x-example: 'Hello, Adam!' 16 | responses: 17 | default: 18 | description: Bad request 19 | 200: 20 | description: Successful response 21 | schema: 22 | $ref: '#/definitions/Message' 23 | examples: 24 | 'application/json': 25 | message: 'Hello, Adam!' 26 | definitions: 27 | Message: 28 | required: 29 | - message 30 | properties: 31 | message: 32 | type: string 33 | default: 'Hello, Adam!' 34 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/01additional.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | person: 3 | type: object 4 | properties: 5 | name: 6 | type: string 7 | data: 8 | type: object 9 | additionalProperties: 10 | type: array 11 | items: 12 | $ref: "#/definitions/person" 13 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/01allOf2.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | person0: 3 | description: original (no. 1) 4 | properties: 5 | name: 6 | type: string 7 | person: 8 | allOf: 9 | - $ref: "#/definitions/person0" 10 | - required: ["name"] 11 | - description: no. 3 12 | - properties: 13 | age: 14 | type: integer 15 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/01commit.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | commit: 3 | properties: 4 | author: 5 | properties: 6 | avatar_url: 7 | type: string 8 | gravatar_id: 9 | type: string 10 | id: 11 | type: integer 12 | login: 13 | type: string 14 | url: 15 | type: string 16 | type: object 17 | commit: 18 | properties: 19 | author: 20 | properties: 21 | date: 22 | description: 'ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ' 23 | type: string 24 | email: 25 | type: string 26 | name: 27 | type: string 28 | type: object 29 | committer: 30 | properties: 31 | date: 32 | description: 'ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ' 33 | type: string 34 | email: 35 | type: string 36 | name: 37 | type: string 38 | type: object 39 | message: 40 | type: string 41 | tree: 42 | properties: 43 | sha: 44 | type: string 45 | url: 46 | type: string 47 | type: object 48 | url: 49 | type: string 50 | type: object 51 | files: 52 | items: 53 | properties: 54 | additions: 55 | type: integer 56 | blob_url: 57 | type: string 58 | changes: 59 | type: integer 60 | deletions: 61 | type: integer 62 | filename: 63 | type: string 64 | patch: 65 | type: string 66 | raw_url: 67 | type: string 68 | status: 69 | type: string 70 | type: object 71 | type: array 72 | type: object 73 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/01empty.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | wrap: 3 | type: object 4 | properties: 5 | empties: 6 | type: array 7 | items: 8 | title: empty 9 | type: object 10 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/01person.yaml: -------------------------------------------------------------------------------- 1 | # required 2 | definitions: 3 | person: 4 | type: object 5 | properties: 6 | name: 7 | type: string 8 | age: 9 | type: integer 10 | required: 11 | - name 12 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/01x_marshmallow_name.yaml: -------------------------------------------------------------------------------- 1 | paths: 2 | /message/{name}: 3 | x-summary: Message operations 4 | x-description: Operation description in Markdown 5 | x-marshmallow-name: Message 6 | get: 7 | summary: Get a message of the day 8 | description: | 9 | Description of the operation in Markdown 10 | operationId: getMessage 11 | parameters: 12 | - name: name 13 | in: path 14 | description: name to include in the message 15 | type: string 16 | x-example: 'Hello, Adam!' 17 | responses: 18 | default: 19 | description: Bad request 20 | 200: 21 | description: Successful response 22 | schema: 23 | $ref: '#/definitions/Message' 24 | examples: 25 | 'application/json': 26 | message: 'Hello, Adam!' 27 | definitions: 28 | Message: 29 | required: 30 | - message 31 | properties: 32 | message: 33 | type: string 34 | default: 'Hello, Adam!' 35 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/02additional.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | S: 3 | type: object 4 | properties: 5 | x: 6 | additionalProperties: 7 | type: integer 8 | y: 9 | additionalProperties: 10 | $ref: "#/definitions/P" 11 | z: 12 | $ref: "#/definitions/D" 13 | P: 14 | type: integer 15 | D: 16 | additionalProperties: 17 | type: integer 18 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/02allOf2.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | person0: 3 | description: original (no. 1) 4 | properties: 5 | name: 6 | type: string 7 | person: 8 | allOf: 9 | - $ref: "#/definitions/person0" 10 | description: no. 4 11 | required: ["name"] 12 | properties: 13 | age: 14 | type: integer 15 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/02empty.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | wrap: 3 | type: object 4 | properties: 5 | empties: 6 | $ref: '#/definitions/wrapEmpties' 7 | wrapEmpties: 8 | x-marshmallow-inline: wrap 9 | type: array 10 | items: 11 | $ref: '#/definitions/wrapEmptiesItem' 12 | wrapEmptiesItem: 13 | x-marshmallow-inline: wrap 14 | type: object 15 | title: empty 16 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/02paths.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | emailsPost: 3 | items: 4 | type: string 5 | pattern: ".+@.+" 6 | type: array 7 | label: 8 | properties: 9 | color: 10 | maxLength: 6 11 | minLength: 6 12 | type: string 13 | name: 14 | type: string 15 | url: 16 | type: string 17 | type: object 18 | labels: 19 | items: 20 | $ref: '#/definitions/label' 21 | type: array 22 | labelsBody: 23 | items: 24 | type: string 25 | type: array 26 | 27 | parameters: 28 | owner: 29 | description: Name of repository owner. 30 | in: path 31 | name: owner 32 | required: true 33 | type: string 34 | repo: 35 | description: Name of repository. 36 | in: path 37 | name: repo 38 | required: true 39 | type: string 40 | number: 41 | description: Number of issue. 42 | in: path 43 | name: number 44 | required: true 45 | type: integer 46 | X-Github-Media-Type: 47 | description: | 48 | You can check the current version of media type in responses. 49 | in: header 50 | name: X-GitHub-Media-Type 51 | type: string 52 | Accept: 53 | description: Is used to set specified media type. 54 | in: header 55 | name: Accept 56 | type: string 57 | X-RateLimit-Limit: 58 | in: header 59 | name: X-RateLimit-Limit 60 | type: integer 61 | X-RateLimit-Remaining: 62 | in: header 63 | name: X-RateLimit-Remaining 64 | type: integer 65 | X-RateLimit-Reset: 66 | in: header 67 | name: X-RateLimit-Reset 68 | type: integer 69 | X-GitHub-Request-Id: 70 | in: header 71 | name: X-GitHub-Request-Id 72 | type: integer 73 | 74 | 75 | responses: 76 | labels: 77 | description: OK 78 | schema: 79 | $ref: '#/definitions/labels' 80 | label-created: 81 | description: Created 82 | schema: 83 | $ref: '#/definitions/label' 84 | 85 | 86 | paths: 87 | '/repos/{owner}/{repo}/issues/{number}/labels': 88 | delete: 89 | description: Remove all labels from an issue. 90 | parameters: 91 | - $ref: "#/parameters/owner" 92 | - $ref: "#/parameters/repo" 93 | - $ref: "#/parameters/number" 94 | - $ref: "#/parameters/X-Github-Media-Type" 95 | - $ref: "#/parameters/Accept" 96 | - $ref: "#/parameters/X-RateLimit-Limit" 97 | - $ref: "#/parameters/X-RateLimit-Remaining" 98 | - $ref: "#/parameters/X-RateLimit-Reset" 99 | - $ref: "#/parameters/X-GitHub-Request-Id" 100 | responses: 101 | '204': 102 | description: | 103 | No content. 104 | '403': 105 | description: | 106 | API rate limit exceeded. See http://developer.github.com/v3/#rate-limiting 107 | for details. 108 | get: 109 | description: List labels on an issue. 110 | parameters: 111 | - $ref: "#/parameters/owner" 112 | - $ref: "#/parameters/repo" 113 | - $ref: "#/parameters/number" 114 | - $ref: "#/parameters/X-Github-Media-Type" 115 | - $ref: "#/parameters/Accept" 116 | - $ref: "#/parameters/X-RateLimit-Limit" 117 | - $ref: "#/parameters/X-RateLimit-Remaining" 118 | - $ref: "#/parameters/X-RateLimit-Reset" 119 | - $ref: "#/parameters/X-GitHub-Request-Id" 120 | responses: 121 | '200': 122 | $ref: "#/responses/labels" 123 | '403': 124 | description: | 125 | API rate limit exceeded. See http://developer.github.com/v3/#rate-limiting 126 | for details. 127 | x-marshmallow-name: IssuedLabels 128 | post: 129 | description: Add labels to an issue. 130 | parameters: 131 | - $ref: "#/parameters/owner" 132 | - $ref: "#/parameters/repo" 133 | - $ref: "#/parameters/number" 134 | - $ref: "#/parameters/X-Github-Media-Type" 135 | - $ref: "#/parameters/Accept" 136 | - $ref: "#/parameters/X-RateLimit-Limit" 137 | - $ref: "#/parameters/X-RateLimit-Remaining" 138 | - $ref: "#/parameters/X-RateLimit-Reset" 139 | - $ref: "#/parameters/X-GitHub-Request-Id" 140 | - in: body 141 | name: body 142 | required: true 143 | schema: 144 | $ref: '#/definitions/emailsPost' 145 | responses: 146 | '201': 147 | $ref: "#/responses/label-created" 148 | '403': 149 | description: | 150 | API rate limit exceeded. See http://developer.github.com/v3/#rate-limiting 151 | for details. 152 | put: 153 | description: | 154 | Replace all labels for an issue. 155 | Sending an empty array ([]) will remove all Labels from the Issue. 156 | parameters: 157 | - $ref: "#/parameters/owner" 158 | - $ref: "#/parameters/repo" 159 | - $ref: "#/parameters/number" 160 | - $ref: "#/parameters/X-Github-Media-Type" 161 | - $ref: "#/parameters/Accept" 162 | - $ref: "#/parameters/X-RateLimit-Limit" 163 | - $ref: "#/parameters/X-RateLimit-Remaining" 164 | - $ref: "#/parameters/X-RateLimit-Reset" 165 | - $ref: "#/parameters/X-GitHub-Request-Id" 166 | - in: body 167 | name: body 168 | required: true 169 | schema: 170 | $ref: '#/definitions/emailsPost' 171 | responses: 172 | '201': 173 | $ref: "#/responses/label-created" 174 | '403': 175 | description: | 176 | API rate limit exceeded. See http://developer.github.com/v3/#rate-limiting 177 | for details. 178 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/02person.yaml: -------------------------------------------------------------------------------- 1 | # simple $ref 2 | definitions: 3 | name: 4 | type: string 5 | description: "name of something" 6 | age: 7 | type: integer 8 | description: "age" 9 | person: 10 | type: object 11 | properties: 12 | name: 13 | $ref: "#/definitions/name" 14 | age: 15 | $ref: "#/definitions/age" 16 | required: 17 | - name 18 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/03additional.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | S: 3 | type: object 4 | properties: 5 | x: 6 | additionalProperties: 7 | type: integer 8 | y: 9 | additionalProperties: 10 | $ref: "#/definitions/P" 11 | y2: 12 | additionalProperties: 13 | $ref: "#/definitions/P2" 14 | z: 15 | $ref: "#/definitions/D" 16 | z2: # ng 17 | additionalProperties: 18 | $ref: "#/definitions/D" 19 | P: 20 | type: integer 21 | P2: 22 | $ref: "#/definitions/P" 23 | D: 24 | additionalProperties: 25 | type: integer 26 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/03paths.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | emailsPost: 3 | items: 4 | type: string 5 | pattern: ".+@.+" 6 | type: array 7 | label: 8 | properties: 9 | color: 10 | maxLength: 6 11 | minLength: 6 12 | type: string 13 | name: 14 | type: string 15 | url: 16 | type: string 17 | type: object 18 | labels: 19 | items: 20 | $ref: '#/definitions/label' 21 | type: array 22 | labelsBody: 23 | items: 24 | type: string 25 | type: array 26 | 27 | parameters: 28 | owner: 29 | description: Name of repository owner. 30 | in: path 31 | name: owner 32 | required: true 33 | type: string 34 | repo: 35 | description: Name of repository. 36 | in: path 37 | name: repo 38 | required: true 39 | type: string 40 | number: 41 | description: Number of issue. 42 | in: path 43 | name: number 44 | required: true 45 | type: integer 46 | X-Github-Media-Type: 47 | description: | 48 | You can check the current version of media type in responses. 49 | in: header 50 | name: X-GitHub-Media-Type 51 | type: string 52 | Accept: 53 | description: Is used to set specified media type. 54 | in: header 55 | name: Accept 56 | type: string 57 | X-RateLimit-Limit: 58 | in: header 59 | name: X-RateLimit-Limit 60 | type: integer 61 | X-RateLimit-Remaining: 62 | in: header 63 | name: X-RateLimit-Remaining 64 | type: integer 65 | X-RateLimit-Reset: 66 | in: header 67 | name: X-RateLimit-Reset 68 | type: integer 69 | X-GitHub-Request-Id: 70 | in: header 71 | name: X-GitHub-Request-Id 72 | type: integer 73 | 74 | 75 | responses: 76 | labels: 77 | description: OK 78 | schema: 79 | $ref: '#/definitions/labels' 80 | label-created: 81 | description: Created 82 | schema: 83 | $ref: '#/definitions/label' 84 | 85 | 86 | paths: 87 | '/repos/{owner}/{repo}/issues/{number}/labels': 88 | parameters: 89 | - $ref: "#/parameters/owner" 90 | - $ref: "#/parameters/repo" 91 | - $ref: "#/parameters/number" 92 | - $ref: "#/parameters/X-Github-Media-Type" 93 | - $ref: "#/parameters/Accept" 94 | - $ref: "#/parameters/X-RateLimit-Limit" 95 | - $ref: "#/parameters/X-RateLimit-Remaining" 96 | - $ref: "#/parameters/X-RateLimit-Reset" 97 | - $ref: "#/parameters/X-GitHub-Request-Id" 98 | delete: 99 | description: Remove all labels from an issue. 100 | parameters: 101 | - $ref: "#/parameters/owner" 102 | responses: 103 | '204': 104 | description: | 105 | No content. 106 | '403': 107 | description: | 108 | API rate limit exceeded. See http://developer.github.com/v3/#rate-limiting 109 | for details. 110 | get: 111 | description: List labels on an issue. 112 | parameters: 113 | - $ref: "#/parameters/owner" 114 | responses: 115 | '200': 116 | $ref: "#/responses/labels" 117 | '403': 118 | description: | 119 | API rate limit exceeded. See http://developer.github.com/v3/#rate-limiting 120 | for details. 121 | x-marshmallow-name: IssuedLabels 122 | post: 123 | description: Add labels to an issue. 124 | parameters: 125 | - $ref: "#/parameters/owner" 126 | - in: body 127 | name: body 128 | required: true 129 | schema: 130 | $ref: '#/definitions/emailsPost' 131 | responses: 132 | '201': 133 | $ref: "#/responses/label-created" 134 | '403': 135 | description: | 136 | API rate limit exceeded. See http://developer.github.com/v3/#rate-limiting 137 | for details. 138 | put: 139 | description: | 140 | Replace all labels for an issue. 141 | Sending an empty array ([]) will remove all Labels from the Issue. 142 | parameters: 143 | - $ref: "#/parameters/owner" 144 | - in: body 145 | name: body 146 | required: true 147 | schema: 148 | $ref: '#/definitions/emailsPost' 149 | responses: 150 | '201': 151 | $ref: "#/responses/label-created" 152 | '403': 153 | description: | 154 | API rate limit exceeded. See http://developer.github.com/v3/#rate-limiting 155 | for details. 156 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/03person.yaml: -------------------------------------------------------------------------------- 1 | # type array 2 | definitions: 3 | name: 4 | type: string 5 | description: "name of something" 6 | age: 7 | type: integer 8 | description: "age" 9 | person: 10 | type: object 11 | properties: 12 | name: 13 | $ref: "#/definitions/name" 14 | age: 15 | $ref: "#/definitions/age" 16 | skills: 17 | type: array 18 | items: 19 | $ref: "#/definitions/skill" 20 | required: 21 | - name 22 | skill: 23 | type: object 24 | properties: 25 | name: 26 | type: string 27 | required: 28 | - name 29 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/04person.yaml: -------------------------------------------------------------------------------- 1 | # type array 2 | definitions: 3 | name: 4 | type: string 5 | description: "name of something" 6 | age: 7 | type: integer 8 | description: "age" 9 | person: 10 | type: object 11 | properties: 12 | name: 13 | $ref: "#/definitions/name" 14 | age: 15 | $ref: "#/definitions/age" 16 | father: 17 | $ref: "#/definitions/person" 18 | mother: 19 | $ref: "#/definitions/person" 20 | skills: 21 | type: array 22 | items: 23 | $ref: "#/definitions/skill" 24 | required: 25 | - name 26 | skill: 27 | type: object 28 | properties: 29 | name: 30 | type: string 31 | required: 32 | - name 33 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/legacy_src/05person.yaml: -------------------------------------------------------------------------------- 1 | # recursive $ref 2 | definitions: 3 | name: 4 | type: string 5 | description: "name of something" 6 | age: 7 | type: integer 8 | description: "age" 9 | father: 10 | $ref: "#/definitions/person" 11 | mother: 12 | $ref: "#/definitions/person" 13 | person: 14 | type: object 15 | properties: 16 | name: 17 | $ref: "#/definitions/name" 18 | age: 19 | $ref: "#/definitions/age" 20 | father: 21 | $ref: "#/definitions/father" 22 | mother: 23 | $ref: "#/definitions/mother" 24 | skills: 25 | type: array 26 | items: 27 | $ref: "#/definitions/skill" 28 | required: 29 | - name 30 | skill: 31 | type: object 32 | properties: 33 | name: 34 | type: string 35 | required: 36 | - name 37 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/test_codegen.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | import pytest 3 | import pathlib 4 | from .testing import load_srcfile, load_dstfile, get_codegen 5 | from .codegen_candidates import CANDIDATES 6 | 7 | 8 | here = pathlib.Path(__file__).parent 9 | 10 | 11 | @pytest.mark.parametrize("msg, src_file, dst_file", CANDIDATES) 12 | def test_v2( 13 | msg, 14 | src_file, 15 | dst_file, 16 | *, 17 | src_dir=pathlib.Path("v2src"), 18 | dst_dir=pathlib.Path("v2dst") 19 | ): 20 | 21 | from swagger_marshmallow_codegen.lifting import lifting_definition 22 | from swagger_marshmallow_codegen.codegen import Context 23 | 24 | d = load_srcfile(src_dir / src_file, here=here) 25 | ctx = Context() 26 | get_codegen().codegen( 27 | lifting_definition(d), 28 | { 29 | "emit_schema": True, 30 | "emit_input": True, 31 | "emit_output": True, 32 | "header_comment": "", 33 | "emit_schema_even_primitive_type": True, 34 | "additional_properties_default": False, 35 | }, 36 | ctx=ctx, 37 | ) 38 | 39 | expected = load_dstfile(dst_dir / dst_file, here=here).rstrip("\n") 40 | actual = str(ctx.m).rstrip("\n") 41 | assert actual == expected 42 | 43 | 44 | @pytest.mark.parametrize("msg, src_file, dst_file", CANDIDATES) 45 | def test_v3( 46 | msg, 47 | src_file, 48 | dst_file, 49 | *, 50 | src_dir=pathlib.Path("v3src"), 51 | dst_dir=pathlib.Path("v3dst") 52 | ): 53 | 54 | from swagger_marshmallow_codegen.lifting import lifting_definition 55 | from swagger_marshmallow_codegen.codegen import Context 56 | 57 | d = load_srcfile(src_dir / src_file, here=here) 58 | ctx = Context() 59 | get_codegen().codegen( 60 | lifting_definition(d), 61 | { 62 | "emit_schema": True, 63 | "emit_input": True, 64 | "emit_output": True, 65 | "header_comment": "", 66 | "emit_schema_even_primitive_type": True, 67 | "additional_properties_default": False, 68 | }, 69 | ctx=ctx, 70 | ) 71 | 72 | expected = load_dstfile(dst_dir / dst_file, here=here).rstrip("\n") 73 | actual = str(ctx.m).rstrip("\n") 74 | assert actual == expected 75 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/test_codegen_legacy.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | import pytest 3 | import pathlib 4 | from .testing import load_srcfile, load_dstfile, get_codegen 5 | 6 | 7 | here = pathlib.Path(__file__).parent 8 | 9 | 10 | @pytest.mark.parametrize( 11 | "src_file, dst_file, header_comment", 12 | [ 13 | ("./legacy_src/00person.yaml", "./legacy_dst/00person.py", ""), 14 | ("./legacy_src/01person.yaml", "./legacy_dst/01person.py", ""), 15 | ("./legacy_src/02person.yaml", "./legacy_dst/02person.py", ""), 16 | ("./legacy_src/03person.yaml", "./legacy_dst/03person.py", ""), 17 | ("./legacy_src/04person.yaml", "./legacy_dst/04person.py", ""), 18 | ("./legacy_src/05person.yaml", "./legacy_dst/05person.py", ""), 19 | ("./legacy_src/00commit.yaml", "./legacy_dst/00commit.py", ""), 20 | ("./legacy_src/01commit.yaml", "./legacy_dst/01commit.py", ""), 21 | ("./legacy_src/00emojis.yaml", "./legacy_dst/00emojis.py", ""), 22 | ("./legacy_src/00stat.yaml", "./legacy_dst/00stat.py", ""), 23 | ("./legacy_src/00default.yaml", "./legacy_dst/00default.py", ""), 24 | ("./legacy_src/00maximum.yaml", "./legacy_dst/00maximum.py", ""), 25 | ("./legacy_src/00length.yaml", "./legacy_dst/00length.py", ""), 26 | ("./legacy_src/00regex.yaml", "./legacy_dst/00regex.py", ""), 27 | ("./legacy_src/00enum.yaml", "./legacy_dst/00enum.py", ""), 28 | ("./legacy_src/00items.yaml", "./legacy_dst/00items.py", ""), 29 | ("./legacy_src/00readonly.yaml", "./legacy_dst/00readonly.py", ""), 30 | ("./legacy_src/00allOf.yaml", "./legacy_dst/00allOf.py", ""), 31 | ("./legacy_src/00allOf2.yaml", "./legacy_dst/00allOf2.py", ""), 32 | ("./legacy_src/01allOf2.yaml", "./legacy_dst/01allOf2.py", ""), 33 | ("./legacy_src/02allOf2.yaml", "./legacy_dst/02allOf2.py", ""), 34 | ("./legacy_src/00paths.yaml", "./legacy_dst/00paths.py", ""), 35 | ("./legacy_src/01paths.yaml", "./legacy_dst/01paths.py", ""), 36 | ("./legacy_src/02paths.yaml", "./legacy_dst/02paths.py", ""), 37 | ("./legacy_src/03paths.yaml", "./legacy_dst/03paths.py", ""), 38 | ("./legacy_src/00empty.yaml", "./legacy_dst/00empty.py", "# flake8: noqa"), 39 | ("./legacy_src/01empty.yaml", "./legacy_dst/01empty.py", ""), 40 | ( 41 | "./legacy_src/00list_with_options.yaml", 42 | "./legacy_dst/00list_with_options.py", 43 | "", 44 | ), 45 | ("./legacy_src/00reserved.yaml", "./legacy_dst/00reserved.py", ""), 46 | ("./legacy_src/00typearray.yaml", "./legacy_dst/00typearray.py", ""), 47 | ("./legacy_src/00additional.yaml", "./legacy_dst/00additional.py", ""), 48 | ("./legacy_src/01additional.yaml", "./legacy_dst/01additional.py", ""), 49 | ("./legacy_src/02additional.yaml", "./legacy_dst/02additional.py", ""), 50 | ("./legacy_src/03additional.yaml", "./legacy_dst/03additional.py", ""), 51 | ("./legacy_src/00nullable.yaml", "./legacy_dst/00nullable.py", ""), 52 | ("./legacy_src/00primitiveapi.yaml", "./legacy_dst/00primitiveapi.py", ""), 53 | # ("./legacy_src/00patternProperties.yaml", "./legacy_dst/00patternProperties.py"), not supported yet 54 | # x-marshmallow-name 55 | ( 56 | "./legacy_src/00x_marshmallow_name.yaml", 57 | "./legacy_dst/00x_marshmallow_name.py", 58 | "", 59 | ), 60 | ( 61 | "./legacy_src/00x_marshmallow_name.yaml", 62 | "./legacy_dst/00x_marshmallow_name.py", 63 | "", 64 | ), 65 | ], 66 | ) 67 | def test( 68 | src_file: str, dst_file: str, header_comment: str, 69 | ): 70 | from swagger_marshmallow_codegen.lifting import lifting_definition 71 | from swagger_marshmallow_codegen.codegen import Context 72 | 73 | d = load_srcfile(src_file, here=here) 74 | ctx = Context() 75 | 76 | get_codegen().codegen( 77 | lifting_definition(d), 78 | { 79 | "emit_schema": True, 80 | "emit_input": True, 81 | "emit_output": True, 82 | "header_comment": header_comment, 83 | "additional_properties_default": False, 84 | }, 85 | ctx=ctx, 86 | ) 87 | 88 | expected = load_dstfile(dst_file, here=here).rstrip("\n") 89 | actual = str(ctx.m).rstrip("\n") 90 | assert actual == expected 91 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/test_legacy_schema.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from marshmallow import fields, ValidationError 3 | 4 | 5 | class Tests(unittest.TestCase): 6 | def _getTargetClass(self): 7 | from swagger_marshmallow_codegen.schema.legacy import LegacySchema 8 | 9 | return LegacySchema 10 | 11 | def test_load_ok(self): 12 | class S(self._getTargetClass()): 13 | name = fields.String(required=True) 14 | 15 | r = S().load({"name": "foo"}) 16 | self.assertEqual(r.data, {"name": "foo"}) 17 | self.assertEqual(r.errors, None) 18 | 19 | def test_load_ok__many(self): 20 | class S(self._getTargetClass()): 21 | name = fields.String(required=True) 22 | 23 | r = S().load([{"name": "foo"}], many=True) 24 | self.assertEqual(r.data, [{"name": "foo"}]) 25 | self.assertEqual(r.errors, None) 26 | 27 | def test_load_ok__many2(self): 28 | class S(self._getTargetClass()): 29 | name = fields.String(required=True) 30 | 31 | r = S(many=True).load([{"name": "foo"}]) 32 | self.assertEqual(r.data, [{"name": "foo"}]) 33 | self.assertEqual(r.errors, None) 34 | 35 | def test_load_ng(self): 36 | class S(self._getTargetClass()): 37 | name = fields.String(required=True) 38 | 39 | r = S().load({}) 40 | self.assertEqual(r.data, {}) 41 | self.assertIsInstance(r.errors, ValidationError) 42 | 43 | def test_load_ng__many(self): 44 | class S(self._getTargetClass()): 45 | name = fields.String(required=True) 46 | 47 | r = S().load([{}], many=True) 48 | self.assertEqual(r.data, []) 49 | self.assertIsInstance(r.errors, ValidationError) 50 | 51 | def test_load_ng__many2(self): 52 | class S(self._getTargetClass()): 53 | name = fields.String(required=True) 54 | 55 | r = S(many=True).load([{}]) 56 | self.assertEqual(r.data, []) 57 | self.assertIsInstance(r.errors, ValidationError) 58 | 59 | def test_load_ng__strict(self): 60 | class S(self._getTargetClass()): 61 | name = fields.String(required=True) 62 | 63 | with self.assertRaises(ValidationError): 64 | S(strict=True).load({}) 65 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/test_lifting.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | import pathlib 3 | import json 4 | import pytest 5 | from .testing import load_srcfile 6 | 7 | here = pathlib.Path(__file__).parent 8 | 9 | 10 | @pytest.mark.parametrize( 11 | "unflatten_file, flatten_file", 12 | [ 13 | ("./legacy_src/01commit.yaml", "./legacy_src/00commit.yaml"), 14 | ("./legacy_src/01empty.yaml", "./legacy_src/02empty.yaml"), 15 | ], 16 | ) 17 | def test( 18 | unflatten_file, flatten_file, 19 | ): 20 | from swagger_marshmallow_codegen.lifting import lifting_definition as _callFUT 21 | 22 | d = load_srcfile(unflatten_file, here=here) 23 | got = _callFUT(d) 24 | want = load_srcfile(flatten_file, here=here) 25 | assert json.dumps(got, indent=2, sort_keys=True) == json.dumps( 26 | want, indent=2, sort_keys=True 27 | ) 28 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/test_validate.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from collections import namedtuple 3 | 4 | 5 | class RangeTests(unittest.TestCase): 6 | def _makeOne(self, *args, **kwargs): 7 | from swagger_marshmallow_codegen.validate import Range 8 | 9 | return Range(*args, **kwargs) 10 | 11 | def test_maximum(self): 12 | from marshmallow.validate import ValidationError 13 | 14 | C = namedtuple("C", "maximum, exclusive_maximum, value, ok") 15 | candidates = [ 16 | C(maximum=100, exclusive_maximum=False, value=99, ok=True), 17 | C(maximum=100, exclusive_maximum=False, value=100, ok=True), 18 | C(maximum=100, exclusive_maximum=False, value=101, ok=False), 19 | C(maximum=100, exclusive_maximum=True, value=99, ok=True), 20 | C(maximum=100, exclusive_maximum=True, value=100, ok=False), 21 | C(maximum=100, exclusive_maximum=True, value=101, ok=False), 22 | ] 23 | for c in candidates: 24 | with self.subTest( 25 | maximum=c.maximum, exclusive_maximum=c.exclusive_maximum, value=c.value 26 | ): 27 | target = self._makeOne( 28 | max=c.maximum, max_inclusive=not c.exclusive_maximum 29 | ) 30 | try: 31 | target(c.value) 32 | self.assertTrue(c.ok) 33 | except ValidationError: 34 | self.assertFalse(c.ok) 35 | 36 | def test_minimum(self): 37 | from marshmallow.validate import ValidationError 38 | 39 | C = namedtuple("C", "minimum, exclusive_minimum, value, ok") 40 | candidates = [ 41 | C(minimum=100, exclusive_minimum=False, value=99, ok=False), 42 | C(minimum=100, exclusive_minimum=False, value=100, ok=True), 43 | C(minimum=100, exclusive_minimum=False, value=101, ok=True), 44 | C(minimum=100, exclusive_minimum=True, value=99, ok=False), 45 | C(minimum=100, exclusive_minimum=True, value=100, ok=False), 46 | C(minimum=100, exclusive_minimum=True, value=101, ok=True), 47 | ] 48 | for c in candidates: 49 | with self.subTest( 50 | minimum=c.minimum, exclusive_minimum=c.exclusive_minimum, value=c.value 51 | ): 52 | target = self._makeOne( 53 | min=c.minimum, min_inclusive=not c.exclusive_minimum 54 | ) 55 | try: 56 | target(c.value) 57 | self.assertTrue(c.ok) 58 | except ValidationError: 59 | self.assertFalse(c.ok) 60 | 61 | 62 | class ItemsRangeTests(unittest.TestCase): 63 | def _makeOne(self, *args, **kwargs): 64 | from swagger_marshmallow_codegen.validate import ItemsRange 65 | 66 | return ItemsRange(*args, **kwargs) 67 | 68 | def test_it(self): 69 | from marshmallow.validate import ValidationError 70 | 71 | C = namedtuple("C", "kwargs, value, ok") 72 | 73 | # minItems: 1 74 | min1_kwargs = dict(min=1, max=None, min_inclusive=True, max_inclusive=True) 75 | 76 | candidates = [ 77 | C(kwargs=min1_kwargs, value=[], ok=False), 78 | C(kwargs=min1_kwargs, value=[1], ok=True), 79 | ] 80 | for c in candidates: 81 | with self.subTest(kwargs=c.kwargs, value=c.value): 82 | target = self._makeOne(**c.kwargs) 83 | try: 84 | target(c.value) 85 | self.assertTrue(c.ok) 86 | except ValidationError: 87 | self.assertFalse(c.ok) 88 | 89 | 90 | class MultipleOfTests(unittest.TestCase): 91 | def _makeOne(self, *args, **kwargs): 92 | from swagger_marshmallow_codegen.validate import MultipleOf 93 | 94 | return MultipleOf(*args, **kwargs) 95 | 96 | def test_it(self): 97 | from marshmallow.validate import ValidationError 98 | 99 | C = namedtuple("C", "n, value, ok") 100 | candidates = [ 101 | C(n=1, value=99, ok=True), 102 | C(n=2, value=99, ok=False), 103 | C(n=2, value=100, ok=True), 104 | C(n=2.0, value=99.0, ok=False), 105 | C(n=2.0, value=100.0, ok=True), 106 | ] 107 | for c in candidates: 108 | with self.subTest(n=c.n, value=c.value): 109 | target = self._makeOne(n=c.n) 110 | try: 111 | target(c.value) 112 | self.assertTrue(c.ok) 113 | except ValidationError: 114 | self.assertFalse(c.ok) 115 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/testing.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | import typing as t 3 | import pathlib 4 | 5 | 6 | def get_codegen(): 7 | from swagger_marshmallow_codegen.codegen import Codegen as _TargetClass 8 | from swagger_marshmallow_codegen.resolver import Resolver 9 | from swagger_marshmallow_codegen.dispatcher import FormatDispatcher 10 | 11 | resolver = Resolver(FormatDispatcher()) 12 | return _TargetClass(resolver=resolver) 13 | 14 | 15 | def load_srcfile( 16 | src: t.Union[str, pathlib.Path], *, here: pathlib.Path 17 | ) -> t.Dict[str, t.Any]: 18 | from swagger_marshmallow_codegen.loading import load 19 | 20 | filepath = here / src 21 | with filepath.open() as rf: 22 | return load(rf) 23 | 24 | 25 | def load_dstfile(dst: t.Union[str, pathlib.Path], *, here: pathlib.Path) -> str: 26 | try: 27 | filepath = here / dst 28 | with filepath.open() as rf: 29 | return rf.read() 30 | except FileNotFoundError: 31 | return "# dummy\n" 32 | -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/v2dst: -------------------------------------------------------------------------------- 1 | ../../shapes/expected -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/v2src: -------------------------------------------------------------------------------- 1 | ../../shapes/v2 -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/v3dst: -------------------------------------------------------------------------------- 1 | ../../shapes/expected -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/tests/v3src: -------------------------------------------------------------------------------- 1 | ../../shapes/v3 -------------------------------------------------------------------------------- /swagger_marshmallow_codegen/validate.py: -------------------------------------------------------------------------------- 1 | import typing as t 2 | from marshmallow import validate as v 3 | from marshmallow.validate import Length, Regexp, OneOf # NOQA 4 | 5 | 6 | Range = v.Range 7 | 8 | 9 | class MultipleOf(v.Validator): 10 | message = "{input} is Not divisible by {n}" 11 | 12 | def __init__(self, n=1, error=None): 13 | self.n = n 14 | self.error = error 15 | 16 | def _repr_args(self): 17 | return "n={0!r}".format(self.n) 18 | 19 | def _format_error(self, value): 20 | return self.message.format(input=value, n=self.n) 21 | 22 | def __call__(self, value): 23 | if value % self.n != 0: 24 | raise v.ValidationError(self._format_error(value)) 25 | return value 26 | 27 | 28 | class ItemsRange(v.Range): 29 | """for maxItems and minItems""" 30 | 31 | message_min = "Must be {min_op} {{min}} items." 32 | message_max = "Must be {max_op} {{max}} items." 33 | message_all = "Must be {min_op} {{min}} and {max_op} {{max}} items." 34 | 35 | message_gte = "greater than or equal to" 36 | message_gt = "greater than" 37 | message_lte = "less than or equal to" 38 | message_lt = "less than" 39 | 40 | def __call__(self, value: t.Any) -> t.Any: 41 | return super().__call__(len(value)) 42 | 43 | 44 | class Unique(v.Validator): 45 | message = "{input} is Not unique" 46 | 47 | def __init__(self, error=None): 48 | self.error = error 49 | 50 | def _repr_args(self): 51 | return "" 52 | 53 | def _format_error(self, value): 54 | return self.message.format(input=value) 55 | 56 | def __call__(self, value): 57 | if len(value) != len(set(value)): 58 | raise v.ValidationError(self._format_error(value)) 59 | return value 60 | --------------------------------------------------------------------------------