Codebase list apispec / fresh-releases/upstream
Import upstream version 5.2.2 Kali Janitor 2 years ago
30 changed file(s) with 605 addition(s) and 385 deletion(s). Raw diff Collapse all Expand all
0 name: build
1 on:
2 push:
3 branches: ["dev"]
4 tags: ["*"]
5 pull_request:
6 # Run builds nightly to catch incompatibilities with new marshmallow releases
7 schedule:
8 - cron: "0 0 * * *"
9 jobs:
10 lint:
11 name: lint
12 runs-on: ubuntu-latest
13 steps:
14 - uses: actions/checkout@v2
15 - uses: actions/setup-python@v2
16 - run: python -m pip install --upgrade pip wheel
17 - run: pip install tox
18 - run: tox -elint
19 tests:
20 name: ${{ matrix.name }}
21 runs-on: ${{ matrix.os }}
22 strategy:
23 fail-fast: false
24 matrix:
25 include:
26 - {name: '3.7-ma3', python: '3.7', os: ubuntu-latest, tox: py37-marshmallow3}
27 - {name: '3.10-ma3', python: '3.10', os: ubuntu-latest, tox: py310-marshmallow3}
28 - {name: '3.10-madev', python: '3.10', os: ubuntu-latest, tox: py310-marshmallowdev}
29 steps:
30 - uses: actions/checkout@v2
31 - uses: actions/setup-python@v2
32 with:
33 python-version: ${{ matrix.python }}
34 - run: python -m pip install --upgrade pip wheel
35 - run: pip install tox
36 - run: tox -e${{ matrix.tox }}
37 release:
38 needs: [lint, tests]
39 name: PyPI release
40 if: startsWith(github.ref, 'refs/tags')
41 runs-on: ubuntu-latest
42 steps:
43 - uses: actions/checkout@v2
44 - uses: actions/setup-python@v2
45 - name: install requirements
46 run: python -m pip install build twine
47 - name: build dists
48 run: python -m build
49 - name: check package metadata
50 run: twine check dist/*
51 - name: publish
52 run: twine upload -u __token__ -p ${{ secrets.PYPI_API_TOKEN }} dist/*
00 repos:
11 - repo: https://github.com/asottile/pyupgrade
2 rev: v2.11.0
2 rev: v2.32.0
33 hooks:
44 - id: pyupgrade
5 args: [--py36-plus]
5 args: [--py37-plus]
66 - repo: https://github.com/python/black
7 rev: 20.8b1
7 rev: 22.3.0
88 hooks:
99 - id: black
1010 language_version: python3
11 - repo: https://gitlab.com/pycqa/flake8
12 rev: 3.9.0
11 - repo: https://github.com/pycqa/flake8
12 rev: 4.0.1
1313 hooks:
1414 - id: flake8
15 additional_dependencies: [flake8-bugbear==21.4.3]
15 additional_dependencies: [flake8-bugbear==22.4.25]
1616 - repo: https://github.com/asottile/blacken-docs
17 rev: v1.10.0
17 rev: v1.12.1
1818 hooks:
1919 - id: blacken-docs
20 additional_dependencies: [black==20.8b1]
20 additional_dependencies: [black==22.3.0]
21 - repo: https://github.com/pre-commit/mirrors-mypy
22 rev: v0.950
23 hooks:
24 - id: mypy
25 additional_dependencies: ["marshmallow>=3,<4", "types-PyYAML"]
7272 - Luke Whitehorn `<https://github.com/Anti-Distinctlyminty>`_
7373 - François Magimel `<https://github.com/Linkid>`_
7474 - Stefan van der Walt `<https://github.com/stefanv>`_
75 - `<https://github.com/kasium>`_
76 - Edwin Erdmanis `@vorticity <https://github.com/vorticity>`_
00 Changelog
11 ---------
22
3 5.2.2 (2022-05-13)
4 ******************
5
6 Bug fixes:
7
8 - Fix schema property ordering regression in ``ApiSpec.to_yaml()`` (:issue:`768`).
9 Thanks :user:`vorticity` for the PR.
10
11 5.2.1 (2022-05-01)
12 ******************
13
14 Bug fixes:
15
16 - Fix type hints for ``APISpec.path`` and ``BasePlugin`` methods (:pr:`765`).
17
18 5.2.0 (2022-04-29)
19 ******************
20
21 Features:
22
23 - Use ``raise from`` whenever possible (:pr:`763`).
24
25 Refactoring:
26
27 - Use a ``tuple`` rather than a ``namedtuple`` for "schema key" (:pr:`725`).
28
29 Other changes:
30
31 - Add type hints (:pr:`747`). Thanks :user:`kasium` for the PR.
32 - Test against Python 3.10 (:pr:`724`).
33 - Drop support for Python 3.6 (:pr:`727`).
34 - Switch to Github Actions for CI (:pr:`751`).
35
336 5.1.1 (2021-09-27)
437 ******************
538
942
1043 Other changes:
1144
12 * Don't build universal wheels. We don't support Python 2 anymore.
45 - Don't build universal wheels. We don't support Python 2 anymore.
1346 (:pr:`705`)
14 * Make the build reproducible (:pr:`#669`).
47 - Make the build reproducible (:pr:`669`).
1548
1649 5.1.0 (2021-08-10)
1750 ******************
767800
768801 Bug fixes:
769802
770 * [apispec.ext.flask]: Don't document view methods that aren't included
803 - [apispec.ext.flask]: Don't document view methods that aren't included
771804 in ``app.add_url_rule(..., methods=[...]))`` (:issue:`173`). Thanks :user:`ukaratay`.
772805
773806 0.27.0 (2017-10-30)
775808
776809 Features:
777810
778 * [apispec.core]: Add ``register_operation_helper``.
779
780 Bug fixes:
781
782 * Order of plugins does not matter (:issue:`136`).
811 - [apispec.core]: Add ``register_operation_helper``.
812
813 Bug fixes:
814
815 - Order of plugins does not matter (:issue:`136`).
783816
784817 Thanks :user:`yoichi` for these changes.
785818
788821
789822 Features:
790823
791 * [apispec.ext.marshmallow]: Generate "enum" property with single entry
824 - [apispec.ext.marshmallow]: Generate "enum" property with single entry
792825 when the ``validate.Equal`` validator is used (:issue:`155`). Thanks
793826 :user:`Bangertm` for the suggestion and PR.
794827
795828 Bug fixes:
796829
797 * Allow OPTIONS to be documented (:issue:`162`). Thanks :user:`buxx` for
830 - Allow OPTIONS to be documented (:issue:`162`). Thanks :user:`buxx` for
798831 the PR.
799 * Fix regression from 0.25.3 that caused a ``KeyError`` (:issue:`163`). Thanks
832 - Fix regression from 0.25.3 that caused a ``KeyError`` (:issue:`163`). Thanks
800833 :user:`yoichi`.
801834
802835 0.25.4 (2017-10-09)
804837
805838 Bug fixes:
806839
807 * [apispec.ext.marshmallow]: Fix swagger location mapping for ``default_in``
840 - [apispec.ext.marshmallow]: Fix swagger location mapping for ``default_in``
808841 param in fields2parameters (:issue:`156`). Thanks :user:`decaz`.
809842
810843 0.25.3 (2017-09-27)
812845
813846 Bug fixes:
814847
815 * [apispec.ext.marshmallow]: Correctly handle multiple fields with
848 - [apispec.ext.marshmallow]: Correctly handle multiple fields with
816849 ``location=json`` (:issue:`75`). Thanks :user:`shaicantor` for
817850 reporting and thanks :user:`yoichi` for the patch.
818851
822855
823856 Bug fixes:
824857
825 * [apispec.ext.marshmallow]: Avoid AttributeError when passing non-dict
858 - [apispec.ext.marshmallow]: Avoid AttributeError when passing non-dict
826859 items to path objects (:issue:`151`). Thanks :user:`yoichi`.
827860
828861 0.25.1 (2017-08-23)
830863
831864 Bug fixes:
832865
833 * [apispec.ext.marshmallow]: Fix ``use_instances`` when ``many=True`` is
866 - [apispec.ext.marshmallow]: Fix ``use_instances`` when ``many=True`` is
834867 set (:issue:`148`). Thanks :user:`theirix`.
835868
836869 0.25.0 (2017-08-15)
838871
839872 Features:
840873
841 * [apispec.ext.marshmallow]: Add ``use_instances`` parameter to
874 - [apispec.ext.marshmallow]: Add ``use_instances`` parameter to
842875 ``fields2paramters`` (:issue:`144`). Thanks :user:`theirix`.
843876
844877 Other changes:
845878
846 * Don't swallow ``YAMLError`` when YAML parsing fails
879 - Don't swallow ``YAMLError`` when YAML parsing fails
847880 (:issue:`135`). Thanks :user:`djanderson` for the suggestion
848881 and the PR.
849882
852885
853886 Features:
854887
855 * [apispec.ext.marshmallow]: Add ``swagger.map_to_swagger_field``
888 - [apispec.ext.marshmallow]: Add ``swagger.map_to_swagger_field``
856889 decorator to support custom field classes (:issue:`120`). Thanks
857890 :user:`frol` for the suggestion and thanks :user:`dradetsky` for the
858891 PR.
862895
863896 Bug fixes:
864897
865 * [apispec.ext.marshmallow]: Fix swagger location mapping for
898 - [apispec.ext.marshmallow]: Fix swagger location mapping for
866899 ``default_in`` param in `property2parameter` (:issue:`142`). Thanks
867900 :user:`decaz`.
868901
869902 0.23.0 (2017-08-03)
870903 +++++++++++++++++++
871904
872 * Pass `operations` constructed by plugins to downstream marshmallow
905 - Pass `operations` constructed by plugins to downstream marshmallow
873906 plugin (:issue:`138`). Thanks :user:`yoichi`.
874 * [apispec.ext.marshmallow] Generate parameter specification from marshmallow Schemas (:issue:`127`).
907 - [apispec.ext.marshmallow] Generate parameter specification from marshmallow Schemas (:issue:`127`).
875908 Thanks :user:`ewalker11` for the suggestion thanks :user:`yoichi` for the PR.
876 * [apispec.ext.flask] Add support for Flask MethodViews (:issue:`85`,
909 - [apispec.ext.flask] Add support for Flask MethodViews (:issue:`85`,
877910 :issue:`125`). Thanks :user:`lafrech` and :user:`boosh` for the
878911 suggestion. Thanks :user:`djanderson` and :user:`yoichi` for the PRs.
879912
880913 0.22.3 (2017-07-16)
881914 +++++++++++++++++++
882915
883 * Release wheel distribution.
916 - Release wheel distribution.
884917
885918 0.22.2 (2017-07-12)
886919 +++++++++++++++++++
887920
888921 Bug fixes:
889922
890 * [apispec.ext.marshmallow]: Properly handle callable ``default`` values
923 - [apispec.ext.marshmallow]: Properly handle callable ``default`` values
891924 in output spec (:issue:`131`). Thanks :user:`NightBlues`.
892925
893926 0.22.1 (2017-06-25)
895928
896929 Bug fixes:
897930
898 * [apispec.ext.marshmallow]: Include ``default`` in output spec when
931 - [apispec.ext.marshmallow]: Include ``default`` in output spec when
899932 ``False`` is the default for a ``Boolean`` field (:issue:`130`).
900933 Thanks :user:`nebularazer`.
901934
904937
905938 Features:
906939
907 * [apispec.ext.bottle] Added bottle plugin (:issue:`128`). Thanks :user:`lucasrc`.
940 - [apispec.ext.bottle] Added bottle plugin (:issue:`128`). Thanks :user:`lucasrc`.
908941
909942 0.21.0 (2017-04-21)
910943 +++++++++++++++++++
911944
912945 Features:
913946
914 * [apispec.ext.marshmallow] Sort list of required field names in generated spec (:issue:`124`). Thanks :user:`dradetsky`.
947 - [apispec.ext.marshmallow] Sort list of required field names in generated spec (:issue:`124`). Thanks :user:`dradetsky`.
915948
916949 0.20.1 (2017-04-18)
917950 +++++++++++++++++++
918951
919952 Bug fixes:
920953
921 * [apispec.ext.tornado]: Fix compatibility with Tornado>=4.5.
922 * [apispec.ext.tornado]: Fix adding paths for handlers with coroutine methods in Python 2 (:issue:`99`).
954 - [apispec.ext.tornado]: Fix compatibility with Tornado>=4.5.
955 - [apispec.ext.tornado]: Fix adding paths for handlers with coroutine methods in Python 2 (:issue:`99`).
923956
924957 0.20.0 (2017-03-19)
925958 +++++++++++++++++++
926959
927960 Features:
928961
929 * [apispec.core]: Definition helper functions receive the ``definition`` keyword argument, which is the current state of the definition (:issue:`122`). Thanks :user:`martinlatrille` for the PR.
930
931 Other changes:
932
933 * [apispec.ext.marshmallow] *Backwards-incompatible*: Remove ``dump`` parameter from ``schema2parameters``, ``fields2parameters``, and ``field2parameter`` (:issue:`114`). Thanks :user:`lafrech` and :user:`frol` for the feedback and :user:`lafrech` for the PR.
962 - [apispec.core]: Definition helper functions receive the ``definition`` keyword argument, which is the current state of the definition (:issue:`122`). Thanks :user:`martinlatrille` for the PR.
963
964 Other changes:
965
966 - [apispec.ext.marshmallow] *Backwards-incompatible*: Remove ``dump`` parameter from ``schema2parameters``, ``fields2parameters``, and ``field2parameter`` (:issue:`114`). Thanks :user:`lafrech` and :user:`frol` for the feedback and :user:`lafrech` for the PR.
934967
935968 0.19.0 (2017-03-05)
936969 +++++++++++++++++++
937970
938971 Features:
939972
940 * [apispec.core]: Add ``extra_fields`` parameter to `APISpec.definition` (:issue:`110`). Thanks :user:`lafrech` for the PR.
941 * [apispec.ext.marshmallow]: Preserve the order of ``choices`` (:issue:`113`). Thanks :user:`frol` for the PR.
942
943 Bug fixes:
944
945 * [apispec.ext.marshmallow]: 'discriminator' is no longer valid as field metadata. It should be defined by passing ``extra_fields={'discriminator': '...'}`` to `APISpec.definition`. Thanks for reporting, :user:`lafrech`.
946 * [apispec.ext.marshmallow]: Allow additional properties when translating ``Nested`` fields using ``allOf`` (:issue:`108`). Thanks :user:`lafrech` for the suggestion and the PR.
947 * [apispec.ext.marshmallow]: Respect ``dump_only`` and ``load_only`` specified in ``class Meta`` (:issue:`84`). Thanks :user:`lafrech` for the fix.
948
949 Other changes:
950
951 * Drop support for Python 3.3.
973 - [apispec.core]: Add ``extra_fields`` parameter to `APISpec.definition` (:issue:`110`). Thanks :user:`lafrech` for the PR.
974 - [apispec.ext.marshmallow]: Preserve the order of ``choices`` (:issue:`113`). Thanks :user:`frol` for the PR.
975
976 Bug fixes:
977
978 - [apispec.ext.marshmallow]: 'discriminator' is no longer valid as field metadata. It should be defined by passing ``extra_fields={'discriminator': '...'}`` to `APISpec.definition`. Thanks for reporting, :user:`lafrech`.
979 - [apispec.ext.marshmallow]: Allow additional properties when translating ``Nested`` fields using ``allOf`` (:issue:`108`). Thanks :user:`lafrech` for the suggestion and the PR.
980 - [apispec.ext.marshmallow]: Respect ``dump_only`` and ``load_only`` specified in ``class Meta`` (:issue:`84`). Thanks :user:`lafrech` for the fix.
981
982 Other changes:
983
984 - Drop support for Python 3.3.
952985
953986
954987 0.18.0 (2017-02-19)
956989
957990 Features:
958991
959 * [apispec.ext.marshmallow]: Translate ``allow_none`` on ``Fields`` to ``x-nullable`` (:issue:`66`). Thanks :user:`lafrech`.
992 - [apispec.ext.marshmallow]: Translate ``allow_none`` on ``Fields`` to ``x-nullable`` (:issue:`66`). Thanks :user:`lafrech`.
960993
961994 0.17.4 (2017-02-16)
962995 +++++++++++++++++++
963996
964997 Bug fixes:
965998
966 * [apispec.ext.marshmallow]: Fix corruption of ``Schema._declared_fields`` when serializing an APISpec (:issue:`107`). Thanks :user:`serebrov` for the catch and patch.
999 - [apispec.ext.marshmallow]: Fix corruption of ``Schema._declared_fields`` when serializing an APISpec (:issue:`107`). Thanks :user:`serebrov` for the catch and patch.
9671000
9681001 0.17.3 (2017-01-21)
9691002 +++++++++++++++++++
9701003
9711004 Bug fixes:
9721005
973 * [apispec.ext.marshmallow]: Fix behavior when passing `Schema` instances to `APISpec.definition`. The `Schema's` class will correctly be registered as a an available `ref` (:issue:`84`). Thanks :user:`lafrech` for reporting and for the PR.
1006 - [apispec.ext.marshmallow]: Fix behavior when passing `Schema` instances to `APISpec.definition`. The `Schema's` class will correctly be registered as a an available `ref` (:issue:`84`). Thanks :user:`lafrech` for reporting and for the PR.
9741007
9751008 0.17.2 (2017-01-03)
9761009 +++++++++++++++++++
9771010
9781011 Bug fixes:
9791012
980 * [apispec.ext.tornado]: Remove usage of ``inspect.getargspec`` for Python >= 3.3 (:issue:`102`). Thanks :user:`matijabesednik`.
1013 - [apispec.ext.tornado]: Remove usage of ``inspect.getargspec`` for Python >= 3.3 (:issue:`102`). Thanks :user:`matijabesednik`.
9811014
9821015 0.17.1 (2016-11-19)
9831016 +++++++++++++++++++
9841017
9851018 Bug fixes:
9861019
987 * [apispec.ext.marshmallow]: Prevent unnecessary warning when generating specs for marshmallow Schema's with autogenerated fields (:issue:`95`). Thanks :user:`khorolets` reporting and for the PR.
988 * [apispec.ext.marshmallow]: Correctly translate ``Length`` validator to `minItems` and `maxItems` for array-type fields (``Nested`` and ``List``) (:issue:`97`). Thanks :user:`YuriHeupa` for reporting and for the PR.
1020 - [apispec.ext.marshmallow]: Prevent unnecessary warning when generating specs for marshmallow Schema's with autogenerated fields (:issue:`95`). Thanks :user:`khorolets` reporting and for the PR.
1021 - [apispec.ext.marshmallow]: Correctly translate ``Length`` validator to `minItems` and `maxItems` for array-type fields (``Nested`` and ``List``) (:issue:`97`). Thanks :user:`YuriHeupa` for reporting and for the PR.
9891022
9901023 0.17.0 (2016-10-30)
9911024 +++++++++++++++++++
9921025
9931026 Features:
9941027
995 * [apispec.ext.marshmallow]: Add support for properties that start with `x-`. Thanks :user:`martinlatrille` for the PR.
1028 - [apispec.ext.marshmallow]: Add support for properties that start with `x-`. Thanks :user:`martinlatrille` for the PR.
9961029
9971030 0.16.0 (2016-10-12)
9981031 +++++++++++++++++++
9991032
10001033 Features:
10011034
1002 * [apispec.core]: Allow ``description`` to be passed to ``APISpec.definition`` (:issue:`93`). Thanks :user:`martinlatrille`.
1035 - [apispec.core]: Allow ``description`` to be passed to ``APISpec.definition`` (:issue:`93`). Thanks :user:`martinlatrille`.
10031036
10041037 0.15.0 (2016-10-02)
10051038 +++++++++++++++++++
10061039
10071040 Features:
10081041
1009 * [apispec.ext.marshmallow]: Allow ``'query'`` to be passed as a field location (:issue:`89`). Thanks :user:`lafrech`.
1010
1011 Bug fixes:
1012
1013 * [apispec.ext.flask]: Properly strip off ``basePath`` when ``APPLICATION_ROOT`` is set on a Flask app's config (:issue:`78`). Thanks :user:`deckar01` for reporting and :user:`asteinlein` for the PR.
1042 - [apispec.ext.marshmallow]: Allow ``'query'`` to be passed as a field location (:issue:`89`). Thanks :user:`lafrech`.
1043
1044 Bug fixes:
1045
1046 - [apispec.ext.flask]: Properly strip off ``basePath`` when ``APPLICATION_ROOT`` is set on a Flask app's config (:issue:`78`). Thanks :user:`deckar01` for reporting and :user:`asteinlein` for the PR.
10141047
10151048 0.14.0 (2016-08-14)
10161049 +++++++++++++++++++
10171050
10181051 Features:
10191052
1020 * [apispec.core]: Maintain order in which paths are added to a spec (:issue:`87`). Thanks :user:`ranjanashish` for the PR.
1021 * [apispec.ext.marshmallow]: Maintain order of fields when ``ordered=True`` on Schema. Thanks again :user:`ranjanashish`.
1053 - [apispec.core]: Maintain order in which paths are added to a spec (:issue:`87`). Thanks :user:`ranjanashish` for the PR.
1054 - [apispec.ext.marshmallow]: Maintain order of fields when ``ordered=True`` on Schema. Thanks again :user:`ranjanashish`.
10221055
10231056 0.13.0 (2016-07-03)
10241057 +++++++++++++++++++
10251058
10261059 Features:
10271060
1028 * [apispec.ext.marshmallow]: Add support for ``Dict`` field (:issue:`80`). Thanks :user:`ericb` for the PR.
1029 * [apispec.ext.marshmallow]: ``dump_only`` fields add ``readOnly`` flag in OpenAPI spec (:issue:`79`). Thanks :user:`itajaja` for the suggestion and PR.
1030
1031 Bug fixes:
1032
1033 * [apispec.ext.marshmallow]: Properly exclude nested dump-only fields from parameters (:issue:`82`). Thanks :user:`incognick` for the catch and patch.
1061 - [apispec.ext.marshmallow]: Add support for ``Dict`` field (:issue:`80`). Thanks :user:`ericb` for the PR.
1062 - [apispec.ext.marshmallow]: ``dump_only`` fields add ``readOnly`` flag in OpenAPI spec (:issue:`79`). Thanks :user:`itajaja` for the suggestion and PR.
1063
1064 Bug fixes:
1065
1066 - [apispec.ext.marshmallow]: Properly exclude nested dump-only fields from parameters (:issue:`82`). Thanks :user:`incognick` for the catch and patch.
10341067
10351068 Support:
10361069
1037 * Update tasks.py for compatibility with invoke>=0.13.0.
1070 - Update tasks.py for compatibility with invoke>=0.13.0.
10381071
10391072 0.12.0 (2016-05-22)
10401073 +++++++++++++++++++
10411074
10421075 Features:
10431076
1044 * [apispec.ext.marshmallow]: Inspect validators to set additional attributes (:issue:`66`). Thanks :user:`deckar01` for the PR.
1045
1046 Bug fixes:
1047
1048 * [apispec.ext.marshmallow]: Respect ``partial`` parameters on ``Schemas`` (:issue:`74`). Thanks :user:`incognick` for reporting.
1077 - [apispec.ext.marshmallow]: Inspect validators to set additional attributes (:issue:`66`). Thanks :user:`deckar01` for the PR.
1078
1079 Bug fixes:
1080
1081 - [apispec.ext.marshmallow]: Respect ``partial`` parameters on ``Schemas`` (:issue:`74`). Thanks :user:`incognick` for reporting.
10491082
10501083 0.11.1 (2016-05-02)
10511084 +++++++++++++++++++
10521085
10531086 Bug fixes:
10541087
1055 * [apispec.ext.flask]: Flask plugin respects ``APPLICATION_ROOT`` from app's config (:issue:`69`). Thanks :user:`deckar01` for the catch and patch.
1056 * [apispec.ext.marshmallow]: Fix support for plural schema instances (:issue:`71`). Thanks again :user:`deckar01`.
1088 - [apispec.ext.flask]: Flask plugin respects ``APPLICATION_ROOT`` from app's config (:issue:`69`). Thanks :user:`deckar01` for the catch and patch.
1089 - [apispec.ext.marshmallow]: Fix support for plural schema instances (:issue:`71`). Thanks again :user:`deckar01`.
10571090
10581091 0.11.0 (2016-04-12)
10591092 +++++++++++++++++++
10601093
10611094 Features:
10621095
1063 * Support vendor extensions on paths (:issue:`65`). Thanks :user:`lucascosta` for the PR.
1064 * *Backwards-incompatible*: Remove support for old versions (<=0.15.0) of webargs.
1065
1066 Bug fixes:
1067
1068 * Fix error message when plugin does not have a ``setup()`` function.
1069 * [apispec.ext.marshmallow] Fix bug in introspecting self-referencing marshmallow fields, i.e. ``fields.Nested('self')`` (:issue:`55`). Thanks :user:`whoiswes` for reporting.
1070 * [apispec.ext.marshmallow] ``field2property`` no longer pops off ``location`` from a field's metadata (:issue:`67`).
1096 - Support vendor extensions on paths (:issue:`65`). Thanks :user:`lucascosta` for the PR.
1097 - *Backwards-incompatible*: Remove support for old versions (<=0.15.0) of webargs.
1098
1099 Bug fixes:
1100
1101 - Fix error message when plugin does not have a ``setup()`` function.
1102 - [apispec.ext.marshmallow] Fix bug in introspecting self-referencing marshmallow fields, i.e. ``fields.Nested('self')`` (:issue:`55`). Thanks :user:`whoiswes` for reporting.
1103 - [apispec.ext.marshmallow] ``field2property`` no longer pops off ``location`` from a field's metadata (:issue:`67`).
10711104
10721105 Support:
10731106
1074 * Lots of new docs, including a User Guide and improved extension docs.
1107 - Lots of new docs, including a User Guide and improved extension docs.
10751108
10761109 0.10.1 (2016-04-09)
10771110 +++++++++++++++++++
10801113
10811114 Features:
10821115
1083 * Add Tornado extension (:issue:`62`).
1084
1085 Bug fixes:
1086
1087 * Compatibility fix with marshmallow>=2.7.0 (:issue:`64`).
1088 * Fix bug that raised error for Swagger parameters that didn't include the ``in`` key (:issue:`63`).
1116 - Add Tornado extension (:issue:`62`).
1117
1118 Bug fixes:
1119
1120 - Compatibility fix with marshmallow>=2.7.0 (:issue:`64`).
1121 - Fix bug that raised error for Swagger parameters that didn't include the ``in`` key (:issue:`63`).
10891122
10901123 Big thanks :user:`lucascosta` for all these changes.
10911124
10941127
10951128 Bug fixes:
10961129
1097 * Fix generation of metadata for ``Nested`` fields (:issue:`61`). Thanks :user:`martinlatrille`.
1130 - Fix generation of metadata for ``Nested`` fields (:issue:`61`). Thanks :user:`martinlatrille`.
10981131
10991132 0.9.0 (2016-03-13)
11001133 ++++++++++++++++++
11011134
11021135 Features:
11031136
1104 * Add ``APISpec.add_tags`` method for adding Swagger tags. Thanks :user:`martinlatrille`.
1105
1106 Bug fixes:
1107
1108 * Fix bug in marshmallow extension where metadata was being lost when converting marshmallow ``Schemas`` when ``many=False``. Thanks again :user:`martinlatrille`.
1109
1110 Other changes:
1111
1112 * Remove duplicate ``SWAGGER_VERSION`` from ``api.ext.marshmallow.swagger``.
1137 - Add ``APISpec.add_tags`` method for adding Swagger tags. Thanks :user:`martinlatrille`.
1138
1139 Bug fixes:
1140
1141 - Fix bug in marshmallow extension where metadata was being lost when converting marshmallow ``Schemas`` when ``many=False``. Thanks again :user:`martinlatrille`.
1142
1143 Other changes:
1144
1145 - Remove duplicate ``SWAGGER_VERSION`` from ``api.ext.marshmallow.swagger``.
11131146
11141147 Support:
11151148
1116 * Update docs to reflect rename of Swagger to OpenAPI.
1149 - Update docs to reflect rename of Swagger to OpenAPI.
11171150
11181151
11191152 0.8.0 (2016-03-06)
11211154
11221155 Features:
11231156
1124 * ``apispec.ext.marshmallow.swagger.schema2jsonschema`` properly introspects ``Schema`` instances when ``many=True`` (:issue:`53`). Thanks :user:`frol` for the PR.
1125
1126 Bug fixes:
1127
1128 * Fix error reporting when an invalid object is passed to ``schema2jsonschema`` or ``schema2parameters`` (:issue:`52`). Thanks again :user:`frol`.
1157 - ``apispec.ext.marshmallow.swagger.schema2jsonschema`` properly introspects ``Schema`` instances when ``many=True`` (:issue:`53`). Thanks :user:`frol` for the PR.
1158
1159 Bug fixes:
1160
1161 - Fix error reporting when an invalid object is passed to ``schema2jsonschema`` or ``schema2parameters`` (:issue:`52`). Thanks again :user:`frol`.
11291162
11301163 0.7.0 (2016-02-11)
11311164 ++++++++++++++++++
11321165
11331166 Features:
11341167
1135 * ``APISpec.add_path`` accepts ``Path`` objects (:issue:`49`). Thanks :user:`Trii` for the suggestion and the implementation.
1136
1137 Bug fixes:
1138
1139 * Use correct field name in "required" array when ``load_from`` and ``dump_to`` are used (:issue:`48`). Thanks :user:`benbeadle` for the catch and patch.
1168 - ``APISpec.add_path`` accepts ``Path`` objects (:issue:`49`). Thanks :user:`Trii` for the suggestion and the implementation.
1169
1170 Bug fixes:
1171
1172 - Use correct field name in "required" array when ``load_from`` and ``dump_to`` are used (:issue:`48`). Thanks :user:`benbeadle` for the catch and patch.
11401173
11411174 0.6.0 (2016-01-04)
11421175 ++++++++++++++++++
11431176
11441177 Features:
11451178
1146 * Add ``APISpec#add_parameter`` for adding common Swagger parameter objects. Thanks :user:`jta`.
1147 * The field name in a spec will be adjusted if a ``Field's`` ``load_from`` and ``dump_to`` attributes are the same. :issue:`43`. Thanks again :user:`jta`.
1148
1149 Bug fixes:
1150
1151 * Fix bug that caused a stack overflow when adding nested Schemas to an ``APISpec`` (:issue:`31`, :issue:`41`). Thanks :user:`alapshin` and :user:`itajaja` for reporting. Thanks :user:`itajaja` for the patch.
1179 - Add ``APISpec#add_parameter`` for adding common Swagger parameter objects. Thanks :user:`jta`.
1180 - The field name in a spec will be adjusted if a ``Field's`` ``load_from`` and ``dump_to`` attributes are the same. :issue:`43`. Thanks again :user:`jta`.
1181
1182 Bug fixes:
1183
1184 - Fix bug that caused a stack overflow when adding nested Schemas to an ``APISpec`` (:issue:`31`, :issue:`41`). Thanks :user:`alapshin` and :user:`itajaja` for reporting. Thanks :user:`itajaja` for the patch.
11521185
11531186 0.5.0 (2015-12-13)
11541187 ++++++++++++++++++
11551188
1156 * ``schema2jsonschema`` and ``schema2parameters`` can introspect a marshmallow ``Schema`` instance as well as a ``Schema`` class (:issue:`37`). Thanks :user:`frol`.
1157 * *Backwards-incompatible*: The first argument to ``schema2jsonschema`` and ``schema2parameters`` was changed from ``schema_cls`` to ``schema``.
1158
1159 Bug fixes:
1160
1161 * Handle conflicting signatures for plugin helpers. Thanks :user:`AndrewPashkin` for the catch and patch.
1189 - ``schema2jsonschema`` and ``schema2parameters`` can introspect a marshmallow ``Schema`` instance as well as a ``Schema`` class (:issue:`37`). Thanks :user:`frol`.
1190 - *Backwards-incompatible*: The first argument to ``schema2jsonschema`` and ``schema2parameters`` was changed from ``schema_cls`` to ``schema``.
1191
1192 Bug fixes:
1193
1194 - Handle conflicting signatures for plugin helpers. Thanks :user:`AndrewPashkin` for the catch and patch.
11621195
11631196 0.4.2 (2015-11-23)
11641197 ++++++++++++++++++
11651198
1166 * Skip dump-only fields when ``dump=False`` is passed to ``schema2parameters`` and ``fields2parameters``. Thanks :user:`frol`.
1167
1168 Bug fixes:
1169
1170 * Raise ``SwaggerError`` when ``validate_swagger`` fails. Thanks :user:`frol`.
1199 - Skip dump-only fields when ``dump=False`` is passed to ``schema2parameters`` and ``fields2parameters``. Thanks :user:`frol`.
1200
1201 Bug fixes:
1202
1203 - Raise ``SwaggerError`` when ``validate_swagger`` fails. Thanks :user:`frol`.
11711204
11721205 0.4.1 (2015-10-19)
11731206 ++++++++++++++++++
11741207
1175 * Correctly pass ``dump`` parameter to ``field2parameters``.
1208 - Correctly pass ``dump`` parameter to ``field2parameters``.
11761209
11771210 0.4.0 (2015-10-18)
11781211 ++++++++++++++++++
11791212
1180 * Add ``dump`` parameter to ``field2property`` (:issue:`32`).
1213 - Add ``dump`` parameter to ``field2property`` (:issue:`32`).
11811214
11821215 0.3.0 (2015-10-02)
11831216 ++++++++++++++++++
11841217
1185 * Rename and repackage as "apispec".
1186 * Support ``enum`` field of JSON Schema based on ``OneOf`` and ``ContainsOnly`` validators.
1218 - Rename and repackage as "apispec".
1219 - Support ``enum`` field of JSON Schema based on ``OneOf`` and ``ContainsOnly`` validators.
11871220
11881221 0.2.0 (2015-09-27)
11891222 ++++++++++++++++++
11901223
1191 * Add ``schema2parameters``, ``fields2parameters``, and ``field2parameters``.
1192 * Removed ``Fixed`` from ``swagger.FIELD_MAPPING`` for compatibility with marshmallow>=2.0.0.
1224 - Add ``schema2parameters``, ``fields2parameters``, and ``field2parameters``.
1225 - Removed ``Fixed`` from ``swagger.FIELD_MAPPING`` for compatibility with marshmallow>=2.0.0.
11931226
11941227 0.1.0 (2015-09-13)
11951228 ++++++++++++++++++
11961229
1197 * First release.
1230 - First release.
00 include LICENSE
11 include *.rst
2 include src/apispec/py.typed
23 recursive-include tests *
34 recursive-include docs *
45 recursive-exclude docs *.pyc
4242
4343 $ pip install -U apispec
4444
45 When using marshmallow pluging, ensure a compatible marshmallow version is used: ::
45 When using the marshmallow plugin, ensure a compatible marshmallow version is used: ::
4646
4747 $ pip install -U apispec[marshmallow]
4848
+0
-44
azure-pipelines.yml less more
0 trigger:
1 branches:
2 include: [dev, test-me-*]
3 tags:
4 include: ['*']
5
6 # Run builds nightly to catch incompatibilities with new marshmallow releases
7 schedules:
8 - cron: "0 0 * * *"
9 displayName: Daily midnight build
10 branches:
11 include:
12 - dev
13 always: "true"
14
15 resources:
16 repositories:
17 - repository: sloria
18 type: github
19 endpoint: github
20 name: sloria/azure-pipeline-templates
21 ref: refs/heads/sloria
22
23 jobs:
24 - template: job--python-tox.yml@sloria
25 parameters:
26 toxenvs:
27 - lint
28
29 - py36-marshmallow3
30 - py37-marshmallow3
31 - py38-marshmallow3
32 - py39-marshmallow3
33
34 - py39-marshmallowdev
35
36 - docs
37 os: linux
38 - template: job--pypi-release.yml@sloria
39 parameters:
40 python: "3.9"
41 distributions: "sdist bdist_wheel"
42 dependsOn:
43 - tox_linux
00 Install
11 =======
2
3 **apispec** requires Python >= 3.6.
42
53 From the PyPI
64 -------------
7171 # 'title': 'Gisty',
7272 # 'version': '1.0.0'},
7373 # 'openapi': '3.0.2',
74 # 'paths': OrderedDict([('/gist/{gist_id}',
75 # {'get': {'responses': {'200': {'content': {'application/json': {'schema': {'$ref': '#/definitions/Gist'}}}}}}})]),
74 # 'paths': {'/gist/{gist_id}':
75 # {'get': {'responses': {'200': {'content': {'application/json': {'schema': {'$ref': '#/definitions/Gist'}}}}}}}},
7676 # 'tags': []}
7777
7878 Use `to_yaml <apispec.APISpec.to_yaml>` to export your spec to YAML.
11 ===========================
22
33 This section documents migration paths to new releases.
4
5 Upgrading to 5.0.0
6 ------------------
7
8 Upgrading to 4.0.0
9 ------------------
10
11 location is ignored in field metadata
12 *************************************
13
14 ``location`` parameter is ignored in ``Field`` metadata. A ``Schema`` can only
15 have a single location.
16
17 A ``Schema`` with fields from different locations must be split into multiple
18 ``Schema``s.
19
20 Upgrading to 3.0.0
21 ------------------
422
523 Upgrading to 2.0.0
624 ------------------
2341 """Path helper that parses docstrings for operations. Adds a
2442 ``func`` parameter to `apispec.APISpec.path`.
2543 """
26 operations = load_operations_from_docstring(func.__doc__)
27 return Path(path=path, operations=operations)
44 operations.update(load_operations_from_docstring(func.__doc__))
2845
2946 Components must be referenced by ID, not full path
3047 **************************************************
9999 # 'title': 'Gisty',
100100 # 'version': '1.0.0'},
101101 # 'openapi': '3.0.2',
102 # 'paths': OrderedDict(),
102 # 'paths': {},
103103 # 'tags': []}
104104
105105 Our application will have a Flask route for the gist detail endpoint.
155155 # 'title': 'Gisty',
156156 # 'version': '1.0.0'},
157157 # 'openapi': '3.0.2',
158 # 'paths': OrderedDict([('/gists/{gist_id}',
159 # OrderedDict([('get',
160 # {'parameters': [{'in': 'path',
158 # 'paths': {'/gists/{gist_id}': {'get': {'parameters': [{'in': 'path',
161159 # 'name': 'gist_id',
162160 # 'required': True,
163161 # 'schema': {'format': 'int32',
164162 # 'type': 'integer'}}],
165 # 'responses': {200: {'content': {'application/json': {'schema': {'$ref': '#/components/schemas/Gist'}}}}}})]))]),
163 # 'responses': {200: {'content': {'application/json': {'schema': {'$ref': '#/components/schemas/Gist'}}}}}}}},
166164 # 'tags': []}
167165
168166 If your API uses `method-based dispatching <http://flask.pocoo.org/docs/0.12/views/#method-based-dispatching>`_, the process is similar. Note that the method no longer needs to be included in the docstring.
2323
2424 .. code-block:: python
2525
26 from apispec import Path, BasePlugin
26 from apispec import BasePlugin
2727 from apispec.yaml_utils import load_operations_from_docstring
2828
2929
146146
147147 spec.path(path="/gists/{gist_id}", func=gist_detail)
148148 print(dict(spec.to_dict()["paths"]))
149 # {'/gists/{gist_id}': OrderedDict([('get', {'responses': {200: {'content': {'application/json': {'schema': '#/definitions/Gist'}}}}})])}
149 # {'/gists/{gist_id}': {'get': {'responses': {200: {'content': {'application/json': {'schema': '#/definitions/Gist'}}}}}}}
150150
151151
152152 Next Steps
55 max-line-length = 110
66 max-complexity = 18
77 select = B,C,E,F,W,T4,B9
8
9 [mypy]
10 ignore_missing_imports = true
11 warn_unreachable = true
12 warn_unused_ignores = true
13 warn_redundant_casts = true
14 no_implicit_optional = true
44 "marshmallow": ["marshmallow>=3.13.0"],
55 "yaml": ["PyYAML>=3.10"],
66 "validation": ["prance[osv]>=0.11"],
7 "lint": ["flake8==3.9.2", "flake8-bugbear==21.9.1", "pre-commit~=2.4"],
7 "lint": [
8 "flake8==4.0.1",
9 "flake8-bugbear==22.4.25",
10 "pre-commit~=2.4",
11 "mypy==0.950",
12 "types-PyYAML",
13 ],
814 "docs": [
915 "marshmallow>=3.13.0",
10 "pyyaml==5.4.1",
11 "sphinx==4.2.0",
12 "sphinx-issues==1.2.0",
16 "pyyaml==6.0",
17 "sphinx==4.5.0",
18 "sphinx-issues==3.0.1",
1319 "sphinx-rtd-theme==1.0.0",
1420 ],
1521 }
6066 license="MIT",
6167 zip_safe=False,
6268 keywords="apispec swagger openapi specification oas documentation spec rest api",
63 python_requires=">=3.6",
69 python_requires=">=3.7",
6470 classifiers=[
6571 "License :: OSI Approved :: MIT License",
6672 "Programming Language :: Python :: 3",
67 "Programming Language :: Python :: 3.6",
6873 "Programming Language :: Python :: 3.7",
6974 "Programming Language :: Python :: 3.8",
7075 "Programming Language :: Python :: 3.9",
76 "Programming Language :: Python :: 3.10",
7177 "Programming Language :: Python :: 3 :: Only",
7278 ],
7379 test_suite="tests",
22 from .core import APISpec
33 from .plugin import BasePlugin
44
5 __version__ = "5.1.1"
5 __version__ = "5.2.2"
66 __all__ = ["APISpec", "BasePlugin"]
00 """Core apispec classes and functions."""
1 from collections import OrderedDict
1
2 from __future__ import annotations
3 from collections.abc import Sequence
4
5
6 import typing
27 from copy import deepcopy
38 import warnings
49
1116 )
1217 from .utils import OpenAPIVersion, deepupdate, COMPONENT_SUBSECTIONS, build_reference
1318
19 if typing.TYPE_CHECKING:
20 from .plugin import BasePlugin
21
22
1423 VALID_METHODS_OPENAPI_V2 = ["get", "post", "put", "patch", "delete", "head", "options"]
1524
1625 VALID_METHODS_OPENAPI_V3 = VALID_METHODS_OPENAPI_V2 + ["trace"]
2534 They became sub-fields of "components" top-level field in OAS v3.
2635 """
2736
28 def __init__(self, plugins, openapi_version):
37 def __init__(
38 self,
39 plugins: Sequence[BasePlugin],
40 openapi_version: OpenAPIVersion,
41 ) -> None:
2942 self._plugins = plugins
3043 self.openapi_version = openapi_version
31 self.schemas = {}
32 self.responses = {}
33 self.parameters = {}
34 self.headers = {}
35 self.examples = {}
36 self.security_schemes = {}
37 self.schemas_lazy = {}
38 self.responses_lazy = {}
39 self.parameters_lazy = {}
40 self.headers_lazy = {}
41 self.examples_lazy = {}
44 self.schemas: dict[str, dict] = {}
45 self.responses: dict[str, dict] = {}
46 self.parameters: dict[str, dict] = {}
47 self.headers: dict[str, dict] = {}
48 self.examples: dict[str, dict] = {}
49 self.security_schemes: dict[str, dict] = {}
50 self.schemas_lazy: dict[str, dict] = {}
51 self.responses_lazy: dict[str, dict] = {}
52 self.parameters_lazy: dict[str, dict] = {}
53 self.headers_lazy: dict[str, dict] = {}
54 self.examples_lazy: dict[str, dict] = {}
4255
4356 self._subsections = {
4457 "schema": self.schemas,
5669 "example": self.examples_lazy,
5770 }
5871
59 def to_dict(self):
72 def to_dict(self) -> dict[str, dict]:
6073 return {
6174 COMPONENT_SUBSECTIONS[self.openapi_version.major][k]: v
6275 for k, v in self._subsections.items()
6376 if v != {}
6477 }
6578
66 def _register_component(self, obj_type, component_id, component, *, lazy=False):
79 def _register_component(
80 self,
81 obj_type: str,
82 component_id: str,
83 component: dict,
84 *,
85 lazy: bool = False,
86 ) -> None:
6787 subsection = (self._subsections if lazy is False else self._subsections_lazy)[
6888 obj_type
6989 ]
7090 subsection[component_id] = component
7191
72 def _do_register_lazy_component(self, obj_type, component_id):
92 def _do_register_lazy_component(
93 self,
94 obj_type: str,
95 component_id: str,
96 ) -> None:
7397 component_buffer = self._subsections_lazy[obj_type]
7498 # If component was lazy registered, register it for real
7599 if component_id in component_buffer:
77101 component_id
78102 )
79103
80 def get_ref(self, obj_type, obj_or_component_id):
104 def get_ref(
105 self,
106 obj_type: str,
107 obj_or_component_id: dict | str,
108 ) -> dict:
81109 """Return object or reference
82110
83111 If obj is a dict, it is assumed to be a complete description and it is returned as is.
95123 obj_type, self.openapi_version.major, obj_or_component_id
96124 )
97125
98 def schema(self, component_id, component=None, *, lazy=False, **kwargs):
126 def schema(
127 self,
128 component_id: str,
129 component: dict | None = None,
130 *,
131 lazy: bool = False,
132 **kwargs: typing.Any,
133 ) -> Components:
99134 """Add a new schema to the spec.
100135
101136 :param str component_id: identifier by which schema may be referenced
135170 self._register_component("schema", component_id, ret, lazy=lazy)
136171 return self
137172
138 def response(self, component_id, component=None, *, lazy=False, **kwargs):
173 def response(
174 self,
175 component_id: str,
176 component: dict | None = None,
177 *,
178 lazy: bool = False,
179 **kwargs: typing.Any,
180 ) -> Components:
139181 """Add a response which can be referenced.
140182
141183 :param str component_id: ref_id to use as reference
159201 return self
160202
161203 def parameter(
162 self, component_id, location, component=None, *, lazy=False, **kwargs
163 ):
204 self,
205 component_id: str,
206 location: str,
207 component: dict | None = None,
208 *,
209 lazy: bool = False,
210 **kwargs: typing.Any,
211 ) -> Components:
164212 """Add a parameter which can be referenced.
165213
166214 :param str component_id: identifier by which parameter may be referenced
191239 self._register_component("parameter", component_id, ret, lazy=lazy)
192240 return self
193241
194 def header(self, component_id, component, *, lazy=False, **kwargs):
242 def header(
243 self,
244 component_id: str,
245 component: dict,
246 *,
247 lazy: bool = False,
248 **kwargs: typing.Any,
249 ) -> Components:
195250 """Add a header which can be referenced.
196251
197252 :param str component_id: identifier by which header may be referenced
216271 self._register_component("header", component_id, ret, lazy=lazy)
217272 return self
218273
219 def example(self, component_id, component, *, lazy=False):
274 def example(
275 self, component_id: str, component: dict, *, lazy: bool = False
276 ) -> Components:
220277 """Add an example which can be referenced
221278
222279 :param str component_id: identifier by which example may be referenced
232289 self._register_component("example", component_id, component, lazy=lazy)
233290 return self
234291
235 def security_scheme(self, component_id, component):
292 def security_scheme(self, component_id: str, component: dict) -> Components:
236293 """Add a security scheme which can be referenced.
237294
238295 :param str component_id: component_id to use as reference
245302 self._register_component("security_scheme", component_id, component)
246303 return self
247304
248 def _resolve_schema(self, obj):
305 def _resolve_schema(self, obj) -> None:
249306 """Replace schema reference as string with a $ref if needed
250307
251308 Also resolve references in the schema
254311 obj["schema"] = self.get_ref("schema", obj["schema"])
255312 self._resolve_refs_in_schema(obj["schema"])
256313
257 def _resolve_examples(self, obj):
314 def _resolve_examples(self, obj) -> None:
258315 """Replace example reference as string with a $ref"""
259316 for name, example in obj.get("examples", {}).items():
260317 obj["examples"][name] = self.get_ref("example", example)
261318
262 def _resolve_refs_in_schema(self, schema):
319 def _resolve_refs_in_schema(self, schema: dict) -> None:
263320 if "properties" in schema:
264321 for key in schema["properties"]:
265322 schema["properties"][key] = self.get_ref(
278335 schema["not"] = self.get_ref("schema", schema["not"])
279336 self._resolve_refs_in_schema(schema["not"])
280337
281 def _resolve_refs_in_parameter_or_header(self, parameter_or_header):
338 def _resolve_refs_in_parameter_or_header(self, parameter_or_header) -> None:
282339 self._resolve_schema(parameter_or_header)
283340 self._resolve_examples(parameter_or_header)
284341
285 def _resolve_refs_in_request_body(self, request_body):
342 def _resolve_refs_in_request_body(self, request_body) -> None:
286343 # requestBody is OpenAPI v3+
287344 for media_type in request_body["content"].values():
288345 self._resolve_schema(media_type)
289346 self._resolve_examples(media_type)
290347
291 def _resolve_refs_in_response(self, response):
348 def _resolve_refs_in_response(self, response) -> None:
292349 if self.openapi_version.major < 3:
293350 self._resolve_schema(response)
294351 else:
300357 self._resolve_refs_in_parameter_or_header(response["headers"][name])
301358 # TODO: Resolve link refs when Components supports links
302359
303 def _resolve_refs_in_operation(self, operation):
360 def _resolve_refs_in_operation(self, operation) -> None:
304361 if "parameters" in operation:
305362 parameters = []
306363 for parameter in operation["parameters"]:
311368 if "requestBody" in operation:
312369 self._resolve_refs_in_request_body(operation["requestBody"])
313370 if "responses" in operation:
314 responses = OrderedDict()
371 responses = {}
315372 for code, response in operation["responses"].items():
316373 response = self.get_ref("response", response)
317374 self._resolve_refs_in_response(response)
318375 responses[code] = response
319376 operation["responses"] = responses
320377
321 def resolve_refs_in_path(self, path):
378 def resolve_refs_in_path(self, path) -> None:
322379 if "parameters" in path:
323380 parameters = []
324381 for parameter in path["parameters"]:
353410 See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#openapi-object
354411 """
355412
356 def __init__(self, title, version, openapi_version, plugins=(), **options):
413 def __init__(
414 self,
415 title: str,
416 version: str,
417 openapi_version: OpenAPIVersion | str,
418 plugins: Sequence[BasePlugin] = (),
419 **options: typing.Any,
420 ) -> None:
357421 self.title = title
358422 self.version = version
359423 self.openapi_version = OpenAPIVersion(openapi_version)
361425 self.plugins = plugins
362426
363427 # Metadata
364 self._tags = []
365 self._paths = OrderedDict()
428 self._tags: list[dict] = []
429 self._paths: dict = {}
366430
367431 # Components
368432 self.components = Components(self.plugins, self.openapi_version)
371435 for plugin in self.plugins:
372436 plugin.init_spec(self)
373437
374 def to_dict(self):
375 ret = {
438 def to_dict(self) -> dict[str, typing.Any]:
439 ret: dict[str, typing.Any] = {
376440 "paths": self._paths,
377441 "info": {"title": self.title, "version": self.version},
378442 }
389453 ret = deepupdate(ret, self.options)
390454 return ret
391455
392 def to_yaml(self, yaml_dump_kwargs=None):
456 def to_yaml(self, yaml_dump_kwargs: typing.Any | None = None) -> str:
393457 """Render the spec to YAML. Requires PyYAML to be installed.
394458
395459 :param dict yaml_dump_kwargs: Additional keyword arguments to pass to `yaml.dump`
398462
399463 return dict_to_yaml(self.to_dict(), yaml_dump_kwargs)
400464
401 def tag(self, tag):
465 def tag(self, tag: dict) -> APISpec:
402466 """Store information about a tag.
403467
404468 :param dict tag: the dictionary storing information about the tag.
408472
409473 def path(
410474 self,
411 path=None,
475 path: str | None = None,
412476 *,
413 operations=None,
414 summary=None,
415 description=None,
416 parameters=None,
417 **kwargs,
418 ):
477 operations: dict[str, typing.Any] | None = None,
478 summary: str | None = None,
479 description: str | None = None,
480 parameters: list[dict] | None = None,
481 **kwargs: typing.Any,
482 ) -> APISpec:
419483 """Add a new path object to the spec.
420484
421485 https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#path-item-object
429493 """
430494 # operations and parameters must be deepcopied because they are mutated
431495 # in _clean_operations and operation helpers and path may be called twice
432 operations = deepcopy(operations) or OrderedDict()
496 operations = deepcopy(operations) or {}
433497 parameters = deepcopy(parameters) or []
434498
435499 # Execute path helpers
467531
468532 return self
469533
470 def _clean_parameters(self, parameters):
534 def _clean_parameters(
535 self,
536 parameters: list[dict],
537 ) -> list[dict]:
471538 """Ensure that all parameters with "in" equal to "path" are also required
472539 as required by the OpenAPI specification, as well as normalizing any
473540 references to global parameters and checking for duplicates parameters
503570
504571 return parameters
505572
506 def _clean_operations(self, operations):
573 def _clean_operations(
574 self,
575 operations: dict[str, dict],
576 ) -> None:
507577 """Ensure that all parameters with "in" equal to "path" are also required
508578 as required by the OpenAPI specification, as well as normalizing any
509579 references to global parameters. Also checks for invalid HTTP methods.
528598 operation["parameters"]
529599 )
530600 if "responses" in operation:
531 responses = OrderedDict()
601 responses = {}
532602 for code, response in operation["responses"].items():
533603 try:
534604 code = int(code) # handles IntEnums like http.HTTPStatus
6868 # 'type': 'object'}}
6969
7070 """
71
72 from __future__ import annotations
73
7174 import warnings
72
73 from apispec import BasePlugin
75 import typing
76
77 from marshmallow import Schema
78
79 from apispec import BasePlugin, APISpec
80 from apispec.utils import OpenAPIVersion
7481 from .common import resolve_schema_instance, make_schema_key, resolve_schema_cls
7582 from .openapi import OpenAPIConverter
7683 from .schema_resolver import SchemaResolver
7784
7885
79 def resolver(schema):
86 def resolver(schema: type[Schema]) -> str:
8087 """Default schema name resolver function that strips 'Schema' from the end of the class name."""
8188 schema_cls = resolve_schema_cls(schema)
8289 name = schema_cls.__name__
105112 Converter = OpenAPIConverter
106113 Resolver = SchemaResolver
107114
108 def __init__(self, schema_name_resolver=None):
115 def __init__(
116 self,
117 schema_name_resolver: typing.Callable[[type[Schema]], str] | None = None,
118 ) -> None:
109119 super().__init__()
110120 self.schema_name_resolver = schema_name_resolver or resolver
111 self.spec = None
112 self.openapi_version = None
113 self.converter = None
114 self.resolver = None
115
116 def init_spec(self, spec):
121 self.spec: APISpec | None = None
122 self.openapi_version: OpenAPIVersion | None = None
123 self.converter: OpenAPIConverter | None = None
124 self.resolver: SchemaResolver | None = None
125
126 def init_spec(self, spec: APISpec) -> None:
117127 super().init_spec(spec)
118128 self.spec = spec
119129 self.openapi_version = spec.openapi_version
186196 self.resolver.resolve_response(response)
187197 return response
188198
189 def header_helper(self, header, **kwargs):
199 def header_helper(self, header: dict, **kwargs: typing.Any):
190200 """Header component helper that allows using a marshmallow
191201 :class:`Schema <marshmallow.Schema>` in header definition.
192202
193203 :param dict header: header fields. May contain a marshmallow
194204 Schema class or instance.
195205 """
206 assert self.resolver # needed for mypy
196207 self.resolver.resolve_schema(header)
197208 return header
198209
199 def operation_helper(self, operations, **kwargs):
210 def operation_helper(
211 self,
212 path: str | None = None,
213 operations: dict | None = None,
214 **kwargs: typing.Any,
215 ) -> None:
216 assert self.resolver # needed for mypy
200217 self.resolver.resolve_operations(operations)
201218
202 def warn_if_schema_already_in_spec(self, schema_key):
219 def warn_if_schema_already_in_spec(self, schema_key: str) -> None:
203220 """Method to warn the user if the schema has already been added to the
204221 spec.
205222 """
223 assert self.converter # needed for mypy
206224 if schema_key in self.converter.refs:
207225 warnings.warn(
208226 "{} has already been added to the spec. Adding it twice may "
00 """Utilities to get schema instances/classes"""
1
2 from __future__ import annotations
13
24 import copy
35 import warnings
4 from collections import namedtuple, OrderedDict
6
7 from apispec.core import Components
58
69 import marshmallow
710
912 MODIFIERS = ["only", "exclude", "load_only", "dump_only", "partial"]
1013
1114
12 def resolve_schema_instance(schema):
15 def resolve_schema_instance(
16 schema: type[marshmallow.Schema] | marshmallow.Schema | str,
17 ) -> marshmallow.Schema:
1318 """Return schema instance for given schema (instance or class).
1419
1520 :param type|Schema|str schema: instance, class or class name of marshmallow.Schema
1924 return schema()
2025 if isinstance(schema, marshmallow.Schema):
2126 return schema
22 return marshmallow.class_registry.get_class(schema)()
27 return marshmallow.class_registry.get_class(schema)() # type: ignore
2328
2429
2530 def resolve_schema_cls(schema):
3540 return marshmallow.class_registry.get_class(schema)
3641
3742
38 def get_fields(schema, *, exclude_dump_only=False):
43 def get_fields(schema, *, exclude_dump_only: bool = False):
3944 """Return fields from schema.
4045
4146 :param Schema schema: A marshmallow Schema instance or a class object
7176 )
7277
7378
74 def filter_excluded_fields(fields, Meta, *, exclude_dump_only):
79 def filter_excluded_fields(fields, Meta, *, exclude_dump_only: bool) -> dict:
7580 """Filter fields that should be ignored in the OpenAPI spec.
7681
7782 :param dict fields: A dictionary of fields name field object pairs
8287 if exclude_dump_only:
8388 exclude.extend(getattr(Meta, "dump_only", []))
8489
85 filtered_fields = OrderedDict(
86 (key, value)
90 filtered_fields = {
91 key: value
8792 for key, value in fields.items()
8893 if key not in exclude and not (exclude_dump_only and value.dump_only)
89 )
94 }
9095
9196 return filtered_fields
9297
9398
94 def make_schema_key(schema):
99 def make_schema_key(schema: marshmallow.Schema) -> tuple:
95100 if not isinstance(schema, marshmallow.Schema):
96101 raise TypeError("can only make a schema key based on a Schema instance.")
97102 modifiers = []
104109 # Unhashable iterable (list, set)
105110 attribute = frozenset(attribute)
106111 modifiers.append(attribute)
107 return SchemaKey(schema.__class__, *modifiers)
112 return tuple([schema.__class__, *modifiers])
108113
109114
110 SchemaKey = namedtuple("SchemaKey", ["SchemaClass"] + MODIFIERS)
111
112
113 def get_unique_schema_name(components, name, counter=0):
115 def get_unique_schema_name(components: Components, name: str, counter: int = 0) -> str:
114116 """Function to generate a unique name based on the provided name and names
115117 already in the spec. Will append a number to the name to make it unique if
116118 the name is already in the spec.
88 import re
99 import functools
1010 import operator
11 import typing
1112 import warnings
1213
1314 import marshmallow
1415 from marshmallow.orderedset import OrderedSet
16
17 from apispec.utils import OpenAPIVersion
1518
1619
1720 RegexType = type(re.compile(""))
8689 """Adds methods for converting marshmallow fields to an OpenAPI properties."""
8790
8891 field_mapping = DEFAULT_FIELD_MAPPING
92 openapi_version: OpenAPIVersion
8993
9094 def init_attribute_functions(self):
9195 self.attribute_functions = [
153157 setattr(self, func.__name__, bound_func)
154158 self.attribute_functions.append(bound_func)
155159
156 def field2property(self, field):
160 def field2property(self, field: marshmallow.fields.Field) -> dict:
157161 """Return the JSON Schema property definition given a marshmallow
158162 :class:`Field <marshmallow.fields.Field>`.
159163
165169 :param Field field: A marshmallow field.
166170 :rtype: dict, a Property Object
167171 """
168 ret = {}
172 ret: dict = {}
169173
170174 for attr_func in self.attribute_functions:
171175 ret.update(attr_func(field, ret=ret))
172176
173177 return ret
174178
175 def field2type_and_format(self, field, **kwargs):
179 def field2type_and_format(
180 self, field: marshmallow.fields.Field, **kwargs: typing.Any
181 ) -> dict:
176182 """Return the dictionary of OpenAPI type and format based on the field type.
177183
178184 :param Field field: A marshmallow field.
201207
202208 return ret
203209
204 def field2default(self, field, **kwargs):
210 def field2default(
211 self, field: marshmallow.fields.Field, **kwargs: typing.Any
212 ) -> dict:
205213 """Return the dictionary containing the field's default value.
206214
207215 Will first look for a `default` key in the field's metadata and then
217225 else:
218226 default = field.load_default
219227 if default is not marshmallow.missing and not callable(default):
220 default = field._serialize(default, None, None)
228 default = field._serialize(default, None, None) # type:ignore
221229 ret["default"] = default
222230 return ret
223231
224 def field2choices(self, field, **kwargs):
232 def field2choices(
233 self, field: marshmallow.fields.Field, **kwargs: typing.Any
234 ) -> dict:
225235 """Return the dictionary of OpenAPI field attributes for valid choices definition.
226236
227237 :param Field field: A marshmallow field.
230240 attributes = {}
231241
232242 comparable = [
233 validator.comparable
243 validator.comparable # type:ignore
234244 for validator in field.validators
235245 if hasattr(validator, "comparable")
236246 ]
238248 attributes["enum"] = comparable
239249 else:
240250 choices = [
241 OrderedSet(validator.choices)
251 OrderedSet(validator.choices) # type:ignore
242252 for validator in field.validators
243253 if hasattr(validator, "choices")
244254 ]
247257
248258 return attributes
249259
250 def field2read_only(self, field, **kwargs):
260 def field2read_only(
261 self, field: marshmallow.fields.Field, **kwargs: typing.Any
262 ) -> dict:
251263 """Return the dictionary of OpenAPI field attributes for a dump_only field.
252264
253265 :param Field field: A marshmallow field.
258270 attributes["readOnly"] = True
259271 return attributes
260272
261 def field2write_only(self, field, **kwargs):
273 def field2write_only(
274 self, field: marshmallow.fields.Field, **kwargs: typing.Any
275 ) -> dict:
262276 """Return the dictionary of OpenAPI field attributes for a load_only field.
263277
264278 :param Field field: A marshmallow field.
269283 attributes["writeOnly"] = True
270284 return attributes
271285
272 def field2nullable(self, field, ret):
286 def field2nullable(self, field: marshmallow.fields.Field, ret) -> dict:
273287 """Return the dictionary of OpenAPI field attributes for a nullable field.
274288
275289 :param Field field: A marshmallow field.
276290 :rtype: dict
277291 """
278 attributes = {}
292 attributes: dict = {}
279293 if field.allow_none:
280294 if self.openapi_version.major < 3:
281295 attributes["x-nullable"] = True
285299 attributes["type"] = [*make_type_list(ret.get("type")), "null"]
286300 return attributes
287301
288 def field2range(self, field, ret):
302 def field2range(self, field: marshmallow.fields.Field, ret) -> dict:
289303 """Return the dictionary of OpenAPI field attributes for a set of
290304 :class:`Range <marshmallow.validators.Range>` validators.
291305
309323 )
310324 return make_min_max_attributes(validators, min_attr, max_attr)
311325
312 def field2length(self, field, **kwargs):
326 def field2length(
327 self, field: marshmallow.fields.Field, **kwargs: typing.Any
328 ) -> dict:
313329 """Return the dictionary of OpenAPI field attributes for a set of
314330 :class:`Length <marshmallow.validators.Length>` validators.
315331
333349 max_attr = "maxItems" if is_array else "maxLength"
334350
335351 equal_list = [
336 validator.equal for validator in validators if validator.equal is not None
352 validator.equal # type:ignore
353 for validator in validators
354 if validator.equal is not None # type:ignore
337355 ]
338356 if equal_list:
339357 return {min_attr: equal_list[0], max_attr: equal_list[0]}
340358
341359 return make_min_max_attributes(validators, min_attr, max_attr)
342360
343 def field2pattern(self, field, **kwargs):
361 def field2pattern(
362 self, field: marshmallow.fields.Field, **kwargs: typing.Any
363 ) -> dict:
344364 """Return the dictionary of OpenAPI field attributes for a
345365 :class:`Regexp <marshmallow.validators.Regexp>` validator.
346366
356376 if isinstance(getattr(v, "regex", None), RegexType)
357377 )
358378 v = next(regex_validators, None)
359 attributes = {} if v is None else {"pattern": v.regex.pattern}
379 attributes = {} if v is None else {"pattern": v.regex.pattern} # type:ignore
360380
361381 if next(regex_validators, None) is not None:
362382 warnings.warn(
367387
368388 return attributes
369389
370 def metadata2properties(self, field, **kwargs):
390 def metadata2properties(
391 self, field: marshmallow.fields.Field, **kwargs: typing.Any
392 ) -> dict:
371393 """Return a dictionary of properties extracted from field metadata.
372394
373395 Will include field metadata that are valid properties of `OpenAPI schema
399421 }
400422 return ret
401423
402 def nested2properties(self, field, ret):
424 def nested2properties(self, field: marshmallow.fields.Field, ret) -> dict:
403425 """Return a dictionary of properties from :class:`Nested <marshmallow.fields.Nested` fields.
404426
405427 Typically provides a reference object and will add the schema to the spec
415437 if isinstance(field, marshmallow.fields.Nested) and not isinstance(
416438 field, marshmallow.fields.Pluck
417439 ):
418 schema_dict = self.resolve_nested_schema(field.schema)
440 schema_dict = self.resolve_nested_schema(field.schema) # type:ignore
419441 if ret and "$ref" in schema_dict:
420442 ret.update({"allOf": [schema_dict]})
421443 else:
422444 ret.update(schema_dict)
423445 return ret
424446
425 def pluck2properties(self, field, **kwargs):
447 def pluck2properties(self, field, **kwargs: typing.Any) -> dict:
426448 """Return a dictionary of properties from :class:`Pluck <marshmallow.fields.Pluck` fields.
427449
428450 Pluck effectively trans-includes a field from another schema into this,
437459 return {"type": "array", "items": ret} if field.many else ret
438460 return {}
439461
440 def list2properties(self, field, **kwargs):
462 def list2properties(self, field, **kwargs: typing.Any) -> dict:
441463 """Return a dictionary of properties from :class:`List <marshmallow.fields.List>` fields.
442464
443465 Will provide an `items` property based on the field's `inner` attribute
450472 ret["items"] = self.field2property(field.inner)
451473 return ret
452474
453 def dict2properties(self, field, **kwargs):
475 def dict2properties(self, field, **kwargs: typing.Any) -> dict:
454476 """Return a dictionary of properties from :class:`Dict <marshmallow.fields.Dict>` fields.
455477
456478 Only applicable for Marshmallow versions greater than 3. Will provide an
466488 ret["additionalProperties"] = self.field2property(value_field)
467489 return ret
468490
469 def timedelta2properties(self, field, **kwargs):
491 def timedelta2properties(self, field, **kwargs: typing.Any) -> dict:
470492 """Return a dictionary of properties from :class:`TimeDelta <marshmallow.fields.TimeDelta>` fields.
471493
472494 Adds a `x-unit` vendor property based on the field's `precision` attribute
495517 return types
496518
497519
498 def make_min_max_attributes(validators, min_attr, max_attr):
520 def make_min_max_attributes(validators, min_attr, max_attr) -> dict:
499521 """Return a dictionary of minimum and maximum attributes based on a list
500522 of validators. If either minimum or maximum values are not present in any
501523 of the validator objects that attribute will be omitted.
55 This module is treated as private API.
66 Users should not need to use this module directly.
77 """
8 from collections import OrderedDict
8
9 from __future__ import annotations
910
1011 import marshmallow
1112 from marshmallow.utils import is_collection
1213
14 from apispec import APISpec
1315 from apispec.utils import OpenAPIVersion
1416 from apispec.exceptions import APISpecError
1517 from .field_converter import FieldConverterMixin
4648 spec
4749 """
4850
49 def __init__(self, openapi_version, schema_name_resolver, spec):
51 def __init__(
52 self,
53 openapi_version: OpenAPIVersion | str,
54 schema_name_resolver,
55 spec: APISpec,
56 ) -> None:
5057 self.openapi_version = OpenAPIVersion(openapi_version)
5158 self.schema_name_resolver = schema_name_resolver
5259 self.spec = spec
5360 self.init_attribute_functions()
5461 # Schema references
55 self.refs = {}
62 self.refs: dict = {}
5663
5764 def resolve_nested_schema(self, schema):
5865 """Return the OpenAPI representation of a marshmallow Schema.
7885 if not name:
7986 try:
8087 json_schema = self.schema2jsonschema(schema_instance)
81 except RuntimeError:
88 except RuntimeError as exc:
8289 raise APISpecError(
8390 "Name resolver returned None for schema {schema} which is "
8491 "part of a chain of circular referencing schemas. Please"
8592 " ensure that the schema_name_resolver passed to"
8693 " MarshmallowPlugin returns a string for all circular"
8794 " referencing schemas.".format(schema=schema)
88 )
95 ) from exc
8996 if getattr(schema, "many", False):
9097 return {"type": "array", "items": json_schema}
9198 return json_schema
94101 return self.get_ref_dict(schema_instance)
95102
96103 def schema2parameters(
97 self, schema, *, location, name="body", required=False, description=None
104 self,
105 schema,
106 *,
107 location,
108 name: str = "body",
109 required: bool = False,
110 description: str | None = None,
98111 ):
99112 """Return an array of OpenAPI parameters given a given marshmallow
100113 :class:`Schema <marshmallow.Schema>`. If `location` is "body", then return an array
134147 for field_name, field_obj in fields.items()
135148 ]
136149
137 def _field2parameter(self, field, *, name, location):
150 def _field2parameter(
151 self, field: marshmallow.fields.Field, *, name: str, location: str
152 ):
138153 """Return an OpenAPI parameter as a `dict`, given a marshmallow
139154 :class:`Field <marshmallow.Field>`.
140155
141156 https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#parameterObject
142157 """
143 ret = {"in": location, "name": name}
158 ret: dict = {"in": location, "name": name}
144159
145160 partial = getattr(field.parent, "partial", False)
146161 ret["required"] = field.required and (
147 not partial or (is_collection(partial) and field.name not in partial)
162 not partial
163 or (is_collection(partial) and field.name not in partial) # type:ignore
148164 )
149165
150166 prop = self.field2property(field)
176192 fields = get_fields(schema)
177193 Meta = getattr(schema, "Meta", None)
178194 partial = getattr(schema, "partial", None)
179 ordered = getattr(Meta, "ordered", False)
180
181 jsonschema = self.fields2jsonschema(fields, partial=partial, ordered=ordered)
195
196 jsonschema = self.fields2jsonschema(fields, partial=partial)
182197
183198 if hasattr(Meta, "title"):
184199 jsonschema["title"] = Meta.title
189204
190205 return jsonschema
191206
192 def fields2jsonschema(self, fields, *, ordered=False, partial=None):
207 def fields2jsonschema(self, fields, *, partial=None):
193208 """Return the JSON Schema Object given a mapping between field names and
194209 :class:`Field <marshmallow.Field>` objects.
195210
196211 :param dict fields: A dictionary of field name field object pairs
197 :param bool ordered: Whether to preserve the order in which fields were declared
198212 :param bool|tuple partial: Whether to override a field's required flag.
199213 If `True` no fields will be set as required. If an iterable fields
200214 in the iterable will not be marked as required.
201215 :rtype: dict, a JSON Schema Object
202216 """
203 jsonschema = {"type": "object", "properties": OrderedDict() if ordered else {}}
217 jsonschema = {"type": "object", "properties": {}}
204218
205219 for field_name, field_obj in fields.items():
206220 observed_field_name = field_obj.data_key or field_name
00 """Base class for Plugin classes."""
11
2 from __future__ import annotations
3
4 import typing
25
36 from .exceptions import PluginMethodNotImplementedError
7 from .core import APISpec
48
59
610 class BasePlugin:
711 """Base class for APISpec plugin classes."""
812
9 def init_spec(self, spec):
13 def init_spec(self, spec: APISpec) -> None:
1014 """Initialize plugin with APISpec object
1115
1216 :param APISpec spec: APISpec object this plugin instance is attached to
1317 """
1418
15 def schema_helper(self, name, definition, **kwargs):
19 def schema_helper(
20 self, name: str, definition: dict, **kwargs: typing.Any
21 ) -> dict | None:
1622 """May return definition as a dict.
1723
1824 :param str name: Identifier by which schema may be referenced
2127 """
2228 raise PluginMethodNotImplementedError
2329
24 def response_helper(self, response, **kwargs):
30 def response_helper(self, response: dict, **kwargs: typing.Any) -> dict | None:
2531 """May return response component description as a dict.
2632
2733 :param dict response: Response fields
2935 """
3036 raise PluginMethodNotImplementedError
3137
32 def parameter_helper(self, parameter, **kwargs):
38 def parameter_helper(self, parameter: dict, **kwargs: typing.Any) -> dict | None:
3339 """May return parameter component description as a dict.
3440
3541 :param dict parameter: Parameter fields
3743 """
3844 raise PluginMethodNotImplementedError
3945
40 def header_helper(self, header, **kwargs):
46 def header_helper(self, header: dict, **kwargs: typing.Any) -> dict | None:
4147 """May return header component description as a dict.
4248
4349 :param dict header: Header fields
4551 """
4652 raise PluginMethodNotImplementedError
4753
48 def path_helper(self, path=None, operations=None, parameters=None, **kwargs):
54 def path_helper(
55 self,
56 path: str | None = None,
57 operations: dict | None = None,
58 parameters: list[dict] | None = None,
59 **kwargs: typing.Any,
60 ) -> str | None:
4961 """May return a path as string and mutate operations dict and parameters list.
5062
5163 :param str path: Path to the resource
6577 """
6678 raise PluginMethodNotImplementedError
6779
68 def operation_helper(self, path=None, operations=None, **kwargs):
80 def operation_helper(
81 self,
82 path: str | None = None,
83 operations: dict | None = None,
84 **kwargs: typing.Any,
85 ) -> None:
6986 """May mutate operations.
7087
7188 :param str path: Path to the resource
(New empty file)
00 """Various utilities for parsing OpenAPI operations from docstrings and validating against
11 the OpenAPI spec.
22 """
3
4 from __future__ import annotations
5
36 import re
47 import json
8 import typing
59
610 from distutils import version
711
812 from apispec import exceptions
13
14 if typing.TYPE_CHECKING:
15 from apispec.core import APISpec
916
1017
1118 COMPONENT_SUBSECTIONS = {
2633 }
2734
2835
29 def build_reference(component_type, openapi_major_version, component_name):
36 def build_reference(
37 component_type: str, openapi_major_version: int, component_name: str
38 ) -> dict[str, str]:
3039 """Return path to reference
3140
3241 :param str component_type: Component type (schema, parameter, response, security_scheme)
4251 }
4352
4453
45 def validate_spec(spec):
54 def validate_spec(spec: APISpec) -> bool:
4655 """Validate the output of an :class:`APISpec` object against the
4756 OpenAPI specification.
4857
6170 "validate_spec requires prance to be installed. "
6271 "You can install all validation requirements using:\n"
6372 " pip install 'apispec[validation]'"
64 )
73 ) from error
6574 parser_kwargs = {}
6675 if spec.openapi_version.version[0] == 3:
6776 parser_kwargs["backend"] = "openapi-spec-validator"
6877 try:
6978 prance.BaseParser(spec_string=json.dumps(spec.to_dict()), **parser_kwargs)
7079 except prance.ValidationError as err:
71 raise exceptions.OpenAPIError(*err.args)
80 raise exceptions.OpenAPIError(*err.args) from err
7281 else:
7382 return True
7483
94103 MIN_INCLUSIVE_VERSION = version.LooseVersion("2.0")
95104 MAX_EXCLUSIVE_VERSION = version.LooseVersion("4.0")
96105
97 def __init__(self, openapi_version):
106 def __init__(self, openapi_version: version.LooseVersion | str) -> None:
98107 if isinstance(openapi_version, version.LooseVersion):
99108 openapi_version = openapi_version.vstring
100109 if (
108117 super().__init__(openapi_version)
109118
110119 @property
111 def major(self):
112 return self.version[0]
120 def major(self) -> int:
121 return int(self.version[0])
113122
114123 @property
115 def minor(self):
116 return self.version[1]
124 def minor(self) -> int:
125 return int(self.version[1])
117126
118127 @property
119 def patch(self):
120 return self.version[2]
128 def patch(self) -> int:
129 return int(self.version[2])
121130
122131
123132 # from django.contrib.admindocs.utils
124 def trim_docstring(docstring):
133 def trim_docstring(docstring: str) -> str:
125134 """Uniformly trims leading/trailing whitespace from docstrings.
126135
127136 Based on http://www.python.org/peps/pep-0257.html#handling-docstring-indentation
136145
137146
138147 # from rest_framework.utils.formatting
139 def dedent(content):
148 def dedent(content: str) -> str:
140149 """
141150 Remove leading indent from a block of text.
142151 Used when generating descriptions from docstrings.
159168
160169
161170 # http://stackoverflow.com/a/8310229
162 def deepupdate(original, update):
171 def deepupdate(original: dict, update: dict) -> dict:
163172 """Recursively update a dict.
164173
165174 Subdict's won't be overwritten but also updated.
00 """YAML utilities"""
11
2 from collections import OrderedDict
2 from __future__ import annotations
3
34 import yaml
5 import typing
46
57 from apispec.utils import trim_docstring, dedent
68
79
8 class YAMLDumper(yaml.Dumper):
9 @staticmethod
10 def _represent_dict(dumper, instance):
11 return dumper.represent_mapping("tag:yaml.org,2002:map", instance.items())
10 def dict_to_yaml(dic: dict, yaml_dump_kwargs: typing.Any | None = None) -> str:
11 """Serializes a dictionary to YAML."""
12 yaml_dump_kwargs = yaml_dump_kwargs or {}
13
14 # By default, don't sort alphabetically to respect schema field ordering
15 yaml_dump_kwargs.setdefault("sort_keys", False)
16 return yaml.dump(dic, **yaml_dump_kwargs)
1217
1318
14 yaml.add_representer(OrderedDict, YAMLDumper._represent_dict, Dumper=YAMLDumper)
15
16
17 def dict_to_yaml(dic, yaml_dump_kwargs=None):
18 if yaml_dump_kwargs is None:
19 yaml_dump_kwargs = {}
20 return yaml.dump(dic, Dumper=YAMLDumper, **yaml_dump_kwargs)
21
22
23 def load_yaml_from_docstring(docstring):
19 def load_yaml_from_docstring(docstring: str) -> dict:
2420 """Loads YAML from docstring."""
2521 split_lines = trim_docstring(docstring).split("\n")
2622
4137 PATH_KEYS = {"get", "put", "post", "delete", "options", "head", "patch"}
4238
4339
44 def load_operations_from_docstring(docstring):
40 def load_operations_from_docstring(docstring: str) -> dict:
4541 """Return a dictionary of OpenAPI operations parsed from a
4642 a docstring.
4743 """
4242 class SelfReferencingSchema(Schema):
4343 id = fields.Int()
4444 single = fields.Nested(lambda: SelfReferencingSchema())
45 many = fields.Nested(lambda: SelfReferencingSchema(many=True))
45 multiple = fields.Nested(lambda: SelfReferencingSchema(many=True))
4646
4747
4848 class OrderedSchema(Schema):
00 import copy
1 from collections import OrderedDict
21 from http import HTTPStatus
32
43 import pytest
666665 def test_path_methods_maintain_order(self, spec):
667666 methods = ["get", "post", "put", "patch", "delete", "head", "options"]
668667 for method in methods:
669 spec.path(path="/path", operations=OrderedDict({method: {}}))
668 spec.path(path="/path", operations={method: {}})
670669 assert list(spec.to_dict()["paths"]["/path"]) == methods
671670
672671 def test_path_merges_paths(self, spec):
12361236 def test_self_referencing_field_many(self, spec):
12371237 spec.components.schema("SelfReference", schema=SelfReferencingSchema)
12381238 definitions = get_schemas(spec)
1239 result = definitions["SelfReference"]["properties"]["many"]
1239 result = definitions["SelfReference"]["properties"]["multiple"]
12401240 assert result == {
12411241 "type": "array",
12421242 "items": build_ref(spec, "schema", "SelfReference"),
00 import pytest
1 from collections import OrderedDict
21 from datetime import datetime
32
43 from marshmallow import EXCLUDE, fields, INCLUDE, RAISE, Schema, validate
101100
102101 res = openapi.schema2jsonschema(BandSchema(partial=("drummer",)))
103102 assert res["required"] == ["bassist"]
104
105 @pytest.mark.parametrize("ordered_schema", (True, False))
106 def test_ordered(self, openapi, ordered_schema):
107 class BandSchema(Schema):
108 class Meta:
109 ordered = ordered_schema
110
111 drummer = fields.Str()
112 bassist = fields.Str()
113
114 res = openapi.schema2jsonschema(BandSchema)
115 assert isinstance(res["properties"], OrderedDict) == ordered_schema
116
117 res = openapi.schema2jsonschema(BandSchema())
118 assert isinstance(res["properties"], OrderedDict) == ordered_schema
119103
120104 def test_no_required_fields(self, openapi):
121105 class BandSchema(Schema):
3030 def test_dict_to_yaml_unicode():
3131 assert yaml_utils.dict_to_yaml({"가": "나"}) == '"\\uAC00": "\\uB098"\n'
3232 assert yaml_utils.dict_to_yaml({"가": "나"}, {"allow_unicode": True}) == "가: 나\n"
33
34
35 def test_dict_to_yaml_keys_are_not_sorted_by_default():
36 assert yaml_utils.dict_to_yaml({"herp": 1, "derp": 2}) == "herp: 1\nderp: 2\n"
37
38
39 def test_dict_to_yaml_keys_can_be_sorted_with_yaml_dump_kwargs():
40 assert (
41 yaml_utils.dict_to_yaml(
42 {"herp": 1, "derp": 2}, yaml_dump_kwargs={"sort_keys": True}
43 )
44 == "derp: 2\nherp: 1\n"
45 )
00 [tox]
11 envlist=
22 lint
3 py{36,37,38,39}-marshmallow3
4 py38-marshmallowdev
3 py{37,38,39,310}-marshmallow3
4 py310-marshmallowdev
55 docs
66
77 [testenv]