pax_global_header00006660000000000000000000000064140743634410014520gustar00rootroot0000000000000052 comment=7d2af3104e3c01ce4f9a1580faf40d8179079008 graphene-2.1.9/000077500000000000000000000000001407436344100133225ustar00rootroot00000000000000graphene-2.1.9/.coveragerc000066400000000000000000000000521407436344100154400ustar00rootroot00000000000000[run] omit = graphene/pyutils/*,*/tests/* graphene-2.1.9/.editorconfig000066400000000000000000000003001407436344100157700ustar00rootroot00000000000000# http://editorconfig.org root = true [*] charset = utf-8 end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true [*.{py,rst,ini}] indent_style = space indent_size = 4 graphene-2.1.9/.github/000077500000000000000000000000001407436344100146625ustar00rootroot00000000000000graphene-2.1.9/.github/stale.yml000066400000000000000000000012541407436344100165170ustar00rootroot00000000000000# Number of days of inactivity before an issue becomes stale daysUntilStale: 60 # Number of days of inactivity before a stale issue is closed daysUntilClose: 7 # Issues with these labels will never be considered stale exemptLabels: - pinned - security # Label to use when marking an issue as stale staleLabel: wontfix # Comment to post when marking an issue as stale. Set to `false` to disable markComment: > This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. # Comment to post when closing a stale issue. Set to `false` to disable closeComment: false graphene-2.1.9/.github/workflows/000077500000000000000000000000001407436344100167175ustar00rootroot00000000000000graphene-2.1.9/.github/workflows/deploy.yml000066400000000000000000000010571407436344100207410ustar00rootroot00000000000000name: πŸš€ Deploy to PyPI on: push: tags: - 'v*' jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Python 3.9 uses: actions/setup-python@v2 with: python-version: 3.9 - name: Build wheel and source tarball run: | pip install wheel python setup.py sdist bdist_wheel - name: Publish a Python distribution to PyPI uses: pypa/gh-action-pypi-publish@v1.1.0 with: user: __token__ password: ${{ secrets.pypi_password }} graphene-2.1.9/.github/workflows/lint.yml000066400000000000000000000007531407436344100204150ustar00rootroot00000000000000name: Lint on: [push, pull_request] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Python 3.9 uses: actions/setup-python@v2 with: python-version: 3.9 - name: Install dependencies run: | python -m pip install --upgrade pip pip install tox - name: Run lint πŸ’… run: tox env: TOXENV: pre-commit - name: Run mypy run: tox env: TOXENV: mypy graphene-2.1.9/.github/workflows/tests.yml000066400000000000000000000011551407436344100206060ustar00rootroot00000000000000name: Tests on: [push, pull_request] jobs: build: runs-on: ubuntu-latest strategy: max-parallel: 4 matrix: python-version: ["2.7", "3.6", "3.7", "3.8", "3.9", "3.10-dev"] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip pip install tox tox-gh-actions - name: Test with tox run: tox env: TOXENV: ${{ matrix.toxenv }} graphene-2.1.9/.gitignore000066400000000000000000000020231407436344100153070ustar00rootroot00000000000000# Created by https://www.gitignore.io ### Python ### # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] # C extensions *.so # Distribution / packaging .Python env/ venv/ .venv/ build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ *.egg-info/ .installed.cfg *.egg # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .coverage.* .cache .pytest_cache nosetests.xml coverage.xml *,cover # Translations *.mo *.pot # Django stuff: *.log # Sphinx documentation docs/_build/ # PyBuilder target/ /tests/django.sqlite /graphene/index.json /graphene/meta.json /meta.json /index.json /docs/playground/graphene-js/pypyjs-release-nojit/ /docs/static/playground/lib /docs/static/playground # PyCharm .idea *.iml # Databases *.sqlite3 .vscode .mypy_cache graphene-2.1.9/.isort.cfg000066400000000000000000000002151407436344100152170ustar00rootroot00000000000000[settings] known_third_party = aniso8601,graphql,graphql_relay,promise,pytest,pytz,pyutils,setuptools,six,snapshottest,sphinx_graphene_theme graphene-2.1.9/.pre-commit-config.yaml000066400000000000000000000012461407436344100176060ustar00rootroot00000000000000repos: - repo: git://github.com/pre-commit/pre-commit-hooks rev: v2.1.0 hooks: - id: check-merge-conflict - id: check-json - id: check-yaml - id: debug-statements - id: end-of-file-fixer exclude: ^docs/.*$ - id: pretty-format-json args: - --autofix - id: trailing-whitespace exclude: README.md - repo: https://github.com/asottile/pyupgrade rev: v1.12.0 hooks: - id: pyupgrade - repo: https://github.com/ambv/black rev: 21.6b0 hooks: - id: black language_version: python3 - repo: https://github.com/PyCQA/flake8 rev: 3.9.2 hooks: - id: flake8 graphene-2.1.9/.travis.yml000066400000000000000000000025321407436344100154350ustar00rootroot00000000000000language: python dist: xenial python: - "2.7" - "3.5" - "3.6" - "3.7" install: - pip install tox tox-travis script: tox after_success: - pip install coveralls - coveralls cache: directories: - $HOME/.cache/pip - $HOME/.cache/pre-commit stages: - test - name: deploy if: tag IS present jobs: fast_finish: true include: - env: TOXENV=pre-commit python: 3.7 - env: TOXENV=mypy python: 3.7 - stage: deploy python: 3.7 after_success: true deploy: provider: pypi user: syrusakbary on: tags: true password: secure: LHOp9DvYR+70vj4YVY8+JRNCKUOfYZREEUY3+4lMUpY7Zy5QwDfgEMXG64ybREH9dFldpUqVXRj53eeU3spfudSfh8NHkgqW7qihez2AhSnRc4dK6ooNfB+kLcSoJ4nUFGxdYImABc4V1hJvflGaUkTwDNYVxJF938bPaO797IvSbuI86llwqkvuK2Vegv9q/fy9sVGaF9VZIs4JgXwR5AyDR7FBArl+S84vWww4vTFD33hoE88VR4QvFY3/71BwRtQrnCMm7AOm31P9u29yi3bpzQpiOR2rHsgrsYdm597QzFKVxYwsmf9uAx2bpbSPy2WibunLePIvOFwm8xcfwnz4/J4ONBc5PSFmUytTWpzEnxb0bfUNLuYloIS24V6OZ8BfAhiYZ1AwySeJCQDM4Vk1V8IF6trTtyx5EW/uV9jsHCZ3LFsAD7UnFRTosIgN3SAK3ZWCEk5oF2IvjecsolEfkRXB3q9EjMkkuXRUeFDH2lWJLgNE27BzY6myvZVzPmfwZUsPBlPD/6w+WLSp97Rjgr9zS3T1d4ddqFM4ZYu04f2i7a/UUQqG+itzzuX5DWLPvzuNt37JB45mB9IsvxPyXZ6SkAcLl48NGyKok1f3vQnvphkfkl4lni29woKhaau8xlsuEDrcwOoeAsVcZXiItg+l+z2SlIwM0A06EvQ= distributions: "sdist bdist_wheel" graphene-2.1.9/LICENSE000066400000000000000000000020771407436344100143350ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2015-Present Syrus Akbary Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. graphene-2.1.9/MANIFEST.in000066400000000000000000000001751407436344100150630ustar00rootroot00000000000000global-exclude tests/* recursive-exclude tests * recursive-exclude tests_py35 * recursive-exclude examples * include LICENSE graphene-2.1.9/Makefile000066400000000000000000000010561407436344100147640ustar00rootroot00000000000000.PHONY: help help: @echo "Please use \`make ' where is one of" @grep -E '^\.PHONY: [a-zA-Z_-]+ .*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = "(: |##)"}; {printf "\033[36m%-30s\033[0m %s\n", $$2, $$3}' .PHONY: install-dev ## Install development dependencies install-dev: pip install -e ".[test]" test: py.test graphene .PHONY: docs ## Generate docs docs: install-dev cd docs && make install && make html .PHONY: docs-live ## Generate docs with live reloading docs-live: install-dev cd docs && make install && make livehtml graphene-2.1.9/README.md000066400000000000000000000117541407436344100146110ustar00rootroot00000000000000**We are looking for contributors**! Please check the [ROADMAP](https://github.com/graphql-python/graphene/blob/master/ROADMAP.md) to see how you can help ❀️ --- # ![Graphene Logo](http://graphene-python.org/favicon.png) [Graphene](http://graphene-python.org) [![Build Status](https://travis-ci.org/graphql-python/graphene.svg?branch=master)](https://travis-ci.org/graphql-python/graphene) [![PyPI version](https://badge.fury.io/py/graphene.svg)](https://badge.fury.io/py/graphene) [![Coverage Status](https://coveralls.io/repos/graphql-python/graphene/badge.svg?branch=master&service=github)](https://coveralls.io/github/graphql-python/graphene?branch=master) ## Introduction [Graphene](http://graphene-python.org) is a Python library for building GraphQL schemas/types fast and easily. - **Easy to use:** Graphene helps you use GraphQL in Python without effort. - **Relay:** Graphene has builtin support for Relay. - **Data agnostic:** Graphene supports any kind of data source: SQL (Django, SQLAlchemy), NoSQL, custom Python objects, etc. We believe that by providing a complete API you could plug Graphene anywhere your data lives and make your data available through GraphQL. ## Integrations Graphene has multiple integrations with different frameworks: | integration | Package | | ----------------- | --------------------------------------------------------------------------------------- | | Django | [graphene-django](https://github.com/graphql-python/graphene-django/) | | SQLAlchemy | [graphene-sqlalchemy](https://github.com/graphql-python/graphene-sqlalchemy/) | | Google App Engine | [graphene-gae](https://github.com/graphql-python/graphene-gae/) | | Peewee | _In progress_ ([Tracking Issue](https://github.com/graphql-python/graphene/issues/289)) | Also, Graphene is fully compatible with the GraphQL spec, working seamlessly with all GraphQL clients, such as [Relay](https://github.com/facebook/relay), [Apollo](https://github.com/apollographql/apollo-client) and [gql](https://github.com/graphql-python/gql). ## Installation For instaling graphene, just run this command in your shell ```bash pip install "graphene>=2.0" ``` ## 2.0 Upgrade Guide Please read [UPGRADE-v2.0.md](/UPGRADE-v2.0.md) to learn how to upgrade. ## Examples Here is one example for you to get started: ```python class Query(graphene.ObjectType): hello = graphene.String(description='A typical hello world') def resolve_hello(self, info): return 'World' schema = graphene.Schema(query=Query) ``` Then Querying `graphene.Schema` is as simple as: ```python query = ''' query SayHello { hello } ''' result = schema.execute(query) ``` If you want to learn even more, you can also check the following [examples](examples/): - **Basic Schema**: [Starwars example](examples/starwars) - **Relay Schema**: [Starwars Relay example](examples/starwars_relay) ## Documentation Documentation and links to additional resources are available at https://docs.graphene-python.org/en/latest/ ## Contributing After cloning this repo, create a [virtualenv](https://virtualenv.pypa.io/en/stable/) and ensure dependencies are installed by running: ```sh virtualenv venv source venv/bin/activate pip install -e ".[test]" ``` Well-written tests and maintaining good test coverage is important to this project. While developing, run new and existing tests with: ```sh py.test graphene/relay/tests/test_node.py # Single file py.test graphene/relay # All tests in directory ``` Add the `-s` flag if you have introduced breakpoints into the code for debugging. Add the `-v` ("verbose") flag to get more detailed test output. For even more detailed output, use `-vv`. Check out the [pytest documentation](https://docs.pytest.org/en/latest/) for more options and test running controls. You can also run the benchmarks with: ```sh py.test graphene --benchmark-only ``` Graphene supports several versions of Python. To make sure that changes do not break compatibility with any of those versions, we use `tox` to create virtualenvs for each Python version and run tests with that version. To run against all Python versions defined in the `tox.ini` config file, just run: ```sh tox ``` If you wish to run against a specific version defined in the `tox.ini` file: ```sh tox -e py36 ``` Tox can only use whatever versions of Python are installed on your system. When you create a pull request, Travis will also be running the same tests and report the results, so there is no need for potential contributors to try to install every single version of Python on their own system ahead of time. We appreciate opening issues and pull requests to make graphene even more stable & useful! ### Building Documentation The documentation is generated using the excellent [Sphinx](http://www.sphinx-doc.org/) and a custom theme. An HTML version of the documentation is produced by running: ```sh make docs ``` graphene-2.1.9/README.rst000066400000000000000000000141651407436344100150200ustar00rootroot00000000000000**We are looking for contributors**! Please check the `ROADMAP `__ to see how you can help ❀️ -------------- |Graphene Logo| `Graphene `__ |Build Status| |PyPI version| |Coverage Status| ========================================================================================================= Introduction ------------ `Graphene `__ is a Python library for building GraphQL schemas/types fast and easily. - **Easy to use:** Graphene helps you use GraphQL in Python without effort. - **Relay:** Graphene has builtin support for Relay. - **Data agnostic:** Graphene supports any kind of data source: SQL (Django, SQLAlchemy), NoSQL, custom Python objects, etc. We believe that by providing a complete API you could plug Graphene anywhere your data lives and make your data available through GraphQL. Integrations ------------ Graphene has multiple integrations with different frameworks: +---------------------+----------------------------------------------------------------------------------------------+ | integration | Package | +=====================+==============================================================================================+ | Django | `graphene-django `__ | +---------------------+----------------------------------------------------------------------------------------------+ | SQLAlchemy | `graphene-sqlalchemy `__ | +---------------------+----------------------------------------------------------------------------------------------+ | Google App Engine | `graphene-gae `__ | +---------------------+----------------------------------------------------------------------------------------------+ | Peewee | *In progress* (`Tracking Issue `__) | +---------------------+----------------------------------------------------------------------------------------------+ Also, Graphene is fully compatible with the GraphQL spec, working seamlessly with all GraphQL clients, such as `Relay `__, `Apollo `__ and `gql `__. Installation ------------ For instaling graphene, just run this command in your shell .. code:: bash pip install "graphene>=2.0" 2.0 Upgrade Guide ----------------- Please read `UPGRADE-v2.0.md `__ to learn how to upgrade. Examples -------- Here is one example for you to get started: .. code:: python class Query(graphene.ObjectType): hello = graphene.String(description='A typical hello world') def resolve_hello(self, info): return 'World' schema = graphene.Schema(query=Query) Then Querying ``graphene.Schema`` is as simple as: .. code:: python query = ''' query SayHello { hello } ''' result = schema.execute(query) If you want to learn even more, you can also check the following `examples `__: - **Basic Schema**: `Starwars example `__ - **Relay Schema**: `Starwars Relay example `__ Documentation ------------- Documentation and links to additional resources are available at https://docs.graphene-python.org/en/latest/ Contributing ------------ After cloning this repo, create a `virtualenv `__ and ensure dependencies are installed by running: .. code:: sh virtualenv venv source venv/bin/activate pip install -e ".[test]" Well-written tests and maintaining good test coverage is important to this project. While developing, run new and existing tests with: .. code:: sh py.test graphene/relay/tests/test_node.py # Single file py.test graphene/relay # All tests in directory Add the ``-s`` flag if you have introduced breakpoints into the code for debugging. Add the ``-v`` ("verbose") flag to get more detailed test output. For even more detailed output, use ``-vv``. Check out the `pytest documentation `__ for more options and test running controls. You can also run the benchmarks with: .. code:: sh py.test graphene --benchmark-only Graphene supports several versions of Python. To make sure that changes do not break compatibility with any of those versions, we use ``tox`` to create virtualenvs for each Python version and run tests with that version. To run against all Python versions defined in the ``tox.ini`` config file, just run: .. code:: sh tox If you wish to run against a specific version defined in the ``tox.ini`` file: .. code:: sh tox -e py36 Tox can only use whatever versions of Python are installed on your system. When you create a pull request, Travis will also be running the same tests and report the results, so there is no need for potential contributors to try to install every single version of Python on their own system ahead of time. We appreciate opening issues and pull requests to make graphene even more stable & useful! Building Documentation ~~~~~~~~~~~~~~~~~~~~~~ The documentation is generated using the excellent `Sphinx `__ and a custom theme. An HTML version of the documentation is produced by running: .. code:: sh make docs .. |Graphene Logo| image:: http://graphene-python.org/favicon.png .. |Build Status| image:: https://travis-ci.org/graphql-python/graphene.svg?branch=master :target: https://travis-ci.org/graphql-python/graphene .. |PyPI version| image:: https://badge.fury.io/py/graphene.svg :target: https://badge.fury.io/py/graphene .. |Coverage Status| image:: https://coveralls.io/repos/graphql-python/graphene/badge.svg?branch=master&service=github :target: https://coveralls.io/github/graphql-python/graphene?branch=master graphene-2.1.9/ROADMAP.md000066400000000000000000000114521407436344100147320ustar00rootroot00000000000000# GraphQL Python Roadmap In order to move Graphene and the GraphQL Python ecosystem forward it's essential to be clear with the community on next steps, so we can move uniformly. _πŸ‘‹ If you have more ideas on how to move the Graphene ecosystem forward, don't hesistate to [open a PR](https://github.com/graphql-python/graphene/edit/master/ROADMAP.md)_ ## Now - [ ] Continue to support v2.x with security releases - [ ] Last major/feature release is cut and graphene-* libraries should pin to that version number ## Next New features will only be developed on version 3 of ecosystem libraries. ### [Core-Next](https://github.com/graphql-python/graphql-core-next) Targeted as v3 of [graphql-core](https://pypi.org/project/graphql-core/), Python 3 only ### Graphene - [ ] Integrate with the core-next API and resolve all breaking changes - [ ] GraphQL types from type annotations - [See issue](https://github.com/graphql-python/graphene/issues/729) - [ ] Add support for coroutines in Connection, Mutation (abstracting out Promise requirement) - [See PR](https://github.com/graphql-python/graphene/pull/824) ### Graphene-* - [ ] Integrate with the graphene core-next API and resolve all breaking changes ### *-graphql - [ ] Integrate with the graphql core-next API and resolve all breaking changes ## Ongoing Initiatives - [ ] Improve documentation, especially for new users to the library - [ ] Recipes for β€œquick start” that people can ideally use/run ## Dependent Libraries | Repo | Release Manager | CODEOWNERS | Pinned | next/master created | Labels Standardized | | ---------------------------------------------------------------------------- | --------------- | ---------- | ---------- | ------------------- | ------------------- | | [graphene](https://github.com/graphql-python/graphene) | ekampf | βœ… | | βœ… | | | [graphql-core](https://github.com/graphql-python/graphql-core) | Cito | βœ… | N/A | N/A | | | [graphql-core-next](https://github.com/graphql-python/graphql-core-next) | Cito | βœ… | N/A | N/A | | | [graphql-server-core](https://github.com/graphql-python/graphql-server-core) | Cito | | βœ… | βœ… | | | [gql](https://github.com/graphql-python/gql) | ekampf | | | | | | [gql-next](https://github.com/graphql-python/gql-next) | ekampf | | N/A | N/A | | | ...[aiohttp](https://github.com/graphql-python/aiohttp-graphql) | | | | | | | ...[django](https://github.com/graphql-python/graphene-django) | mvanlonden | | βœ… | βœ… | | | ...[sanic](https://github.com/graphql-python/sanic-graphql) | ekampf | | | | | | ...[flask](https://github.com/graphql-python/flask-graphql) | | | | | | | ...[webob](https://github.com/graphql-python/webob-graphql) | | | | | | | ...[tornado](https://github.com/graphql-python/graphene-tornado) | ewhauser | | PR created | βœ… | | | ...[ws](https://github.com/graphql-python/graphql-ws) | Cito/dfee | | βœ… | βœ… | | | ...[gae](https://github.com/graphql-python/graphene-gae) | ekampf | | PR created | βœ… | | | ...[sqlalchemy](https://github.com/graphql-python/graphene-sqlalchemy) | jnak/Nabell | βœ… | βœ… | βœ… | | | ...[mongo](https://github.com/graphql-python/graphene-mongo) | | | βœ… | βœ… | | | ...[relay-py](https://github.com/graphql-python/graphql-relay-py) | Cito | | | | | | ...[wsgi](https://github.com/moritzmhmk/wsgi-graphql) | | | | | | graphene-2.1.9/UPGRADE-v1.0.md000066400000000000000000000115131407436344100154160ustar00rootroot00000000000000# v1.0 Upgrade Guide Big changes from v0.10.x to 1.0. While on the surface a lot of this just looks like shuffling around API, the entire codebase has been rewritten to handle some really great use cases and improved performance. ## Backwards Compatibility and Deprecation Warnings This has been a community project from the start, we need your help making the upgrade as smooth as possible for everybody! We have done our best to provide backwards compatibility with deprecated APIs. ## Deprecations - `with_context` is no longer needed. Resolvers now always take the context argument. Before: ```python def resolve_xxx(root, args, info): # ... ``` With 1.0: ```python def resolve_xxx(root, args, context, info): # ... ``` - `ObjectType` and `Interface` no longer accept the `abstract` option in the `Meta`. Inheriting fields should be now achieved using `AbstractType` inheritance. Before: ```python class MyBaseQuery(graphene.ObjectType): my_field = String() class Meta: abstract = True class Query(MyBaseQuery): pass ``` With 1.0: ```python class MyBaseQuery(graphene.AbstractType): my_field = String() class Query(MyBaseQuery, graphene.ObjectType): pass ``` - The `type_name` option in the Meta in types is now `name` - Type references no longer work with strings, but with functions. Before: ```python class Query(graphene.ObjectType): user = graphene.Field('User') users = graphene.List('User') ``` With 1.0: ```python class Query(graphene.ObjectType): user = graphene.Field(lambda: User) users = graphene.List(lambda: User) ``` ## Schema Schemas in graphene `1.0` are `Immutable`, that means that once you create a `graphene.Schema` any change in their attributes will not have any effect. The `name` argument is removed from the Schema. The arguments `executor` and `middlewares` are also removed from the `Schema` definition. You can still use them, but by calling explicitly in the `execute` method in `graphql`. ```python # Old way schema = graphene.Schema(name='My Schema') schema.query = Query schema.mutation = Mutation # New way schema = graphene.Schema( query=Query, mutation=Mutation ) ``` ## Interfaces For implementing an Interface in an ObjectType, you have to add it onto `Meta.interfaces`. Like: ```python from graphene import Interface, ObjectType, String class Character(Interface): name = String() class Human(Character): # Old way, Human will still be an Interface pass class Droid(ObjectType): # New way, you have to specify the ObjectType class Meta: interfaces = (Character, ) ``` ## Mutations Mutation fields have changed the way of usage, before if you have the mutation `MyMutation` you only have to reference with `graphene.Field(MyMutation)` now it's simply `MyMutation.Field()` Example: ```python from graphene import ObjectType, Mutation, String class ReverseString(Mutation): class Input: input = String(required=True) reversed = String() def mutate(root, args, context, info): reversed = args.get('input')[::-1] return ReverseString(reversed=reversed) class Query(ObjectType): reverse_string = graphene.Field(ReverseString) # Old way, will not include the mutation arguments by default reverse_string = ReverseString.Field() ``` ## Nodes Apart from implementing as shown in the previous section, to use the node field you have to specify the node Type. Example: ```python from graphene import ObjectType, relay class Query(ObjectType): node = relay.NodeField() # Old way, NodeField no longer exists. Use Node.Field node = relay.Node.Field() # New way ``` Also, if you wanted to create an `ObjectType` that implements `Node`, you have to do it explicity. ## Django The Django integration with Graphene now has an independent package: `graphene-django`. For installing, you have to replace the old `graphene[django]` with `graphene-django`. - As the package is now independent, you now have to import from `graphene_django`. - **DjangoNode no longer exists**, please use `relay.Node` instead: ```python from graphene.relay import Node from graphene_django import DjangoObjectType class Droid(DjangoObjectType): class Meta: interfaces = (Node, ) ``` ## SQLAlchemy The SQLAlchemy integration with Graphene now has an independent package: `graphene-sqlalchemy`. For installing, you have to replace the old `graphene[sqlalchemy]` with `graphene-sqlalchemy`. - As the package is now independent, you have to import now from `graphene_sqlalchemy`. - **SQLAlchemyNode no longer exists**, please use `relay.Node` instead: ```python from graphene.relay import Node from graphene_sqlalchemy import SQLAlchemyObjectType class Droid(SQLAlchemyObjectType): class Meta: interfaces = (Node, ) ``` graphene-2.1.9/UPGRADE-v2.0.md000066400000000000000000000200341407436344100154150ustar00rootroot00000000000000# v2.0 Upgrade Guide `ObjectType`, `Interface`, `InputObjectType`, `Scalar` and `Enum` implementations have been quite simplified, without the need to define a explicit Metaclass for each subtype. It also improves the field resolvers, [simplifying the code](#simpler-resolvers) the developer has to write to use them. **Deprecations:** - [`AbstractType`](#abstracttype-deprecated) - [`resolve_only_args`](#resolve_only_args) - [`Mutation.Input`](#mutationinput) **Breaking changes:** - [`Simpler Resolvers`](#simpler-resolvers) - [`Node Connections`](#node-connections) **New Features!** - [`InputObjectType`](#inputobjecttype) - [`Meta as Class arguments`](#meta-as-class-arguments) (_only available for Python 3_) > The type metaclasses are now deleted as they are no longer necessary. If your code was depending > on this strategy for creating custom attrs, see an [example on how to do it in 2.0](https://github.com/graphql-python/graphene/blob/v2.0.0/graphene/tests/issues/test_425.py). ## Deprecations ### AbstractType deprecated AbstractType is deprecated in graphene 2.0, you can now use normal inheritance instead. Before: ```python class CommonFields(AbstractType): name = String() class Pet(CommonFields, Interface): pass ``` With 2.0: ```python class CommonFields(object): name = String() class Pet(CommonFields, Interface): pass ``` ### resolve_only_args `resolve_only_args` is now deprecated as the resolver API has been simplified. Before: ```python class User(ObjectType): name = String() @resolve_only_args def resolve_name(root): return root.name ``` With 2.0: ```python class User(ObjectType): name = String() def resolve_name(root, info): return root.name ``` ### Mutation.Input `Mutation.Input` is now deprecated in favor of using `Mutation.Arguments` (`ClientIDMutation` still uses `Input`). Before: ```python class User(Mutation): class Input: name = String() ``` With 2.0: ```python class User(Mutation): class Arguments: name = String() ``` ## Breaking Changes ### Simpler resolvers All the resolvers in graphene have been simplified. Prior to Graphene `2.0`, all resolvers required four arguments: `(root, args, context, info)`. Now, resolver `args` are passed as keyword arguments to the function, and `context` argument dissapeared in favor of `info.context`. Before: ```python my_field = graphene.String(my_arg=graphene.String()) def resolve_my_field(root, args, context, info): my_arg = args.get('my_arg') return ... ``` With 2.0: ```python my_field = graphene.String(my_arg=graphene.String()) def resolve_my_field(root, info, my_arg): return ... ``` **PS.: Take care with receiving args like `my_arg` as above. This doesn't work for optional (non-required) arguments as stantard `Connection`'s arguments (first, before, after, before).** You may need something like this: ```python def resolve_my_field(root, info, known_field1, known_field2, **args): ## get other args with: args.get('arg_key') ``` And, if you need the context in the resolver, you can use `info.context`: ```python my_field = graphene.String(my_arg=graphene.String()) def resolve_my_field(root, info, my_arg): context = info.context return ... ``` ### Node Connections Node types no longer have a `Connection` by default. In 2.0 and onwards `Connection`s should be defined explicitly. Before: ```python class User(ObjectType): class Meta: interfaces = [relay.Node] name = String() class Query(ObjectType): user_connection = relay.ConnectionField(User) ``` With 2.0: ```python class User(ObjectType): class Meta: interfaces = [relay.Node] name = String() class UserConnection(relay.Connection): class Meta: node = User class Query(ObjectType): user_connection = relay.ConnectionField(UserConnection) ``` ## Node.get_node The method `get_node` in `ObjectTypes` that have `Node` as interface, changes its API. From `def get_node(cls, id, context, info)` to `def get_node(cls, info, id)`. ```python class MyObject(ObjectType): class Meta: interfaces = (Node, ) @classmethod def get_node(cls, id, context, info): return ... ``` To: ```python class MyObject(ObjectType): class Meta: interfaces = (Node, ) @classmethod def get_node(cls, info, id): return ... ``` ## Node.get_node_from_global_id The parameters' order of `get_node_from_global_id` method has changed. You may need to adjust your [Node Root Field](http://docs.graphene-python.org/en/latest/relay/nodes/#node-root-field) and maybe other places that uses this method to obtain an object. Before: ```python class RootQuery(object): ... node = Field(relay.Node, id=ID(required=True)) def resolve_node(root, args, context, info): node = relay.Node.get_node_from_global_id(args['id'], context, info) return node ``` Now: ```python class RootQuery(object): ... node = Field(relay.Node, id=ID(required=True)) def resolve_node(root, info, id): node = relay.Node.get_node_from_global_id(info, id) return node ``` ## Mutation.mutate Now only receives (`root`, `info`, `**kwargs`) and is not a @classmethod Before: ```python class SomeMutation(Mutation): ... @classmethod def mutate(cls, instance, args, context, info): ... ``` With 2.0: ```python class SomeMutation(Mutation): ... def mutate(root, info, **args): ... ``` With 2.0 you can also get your declared (as above) `args` this way: ```python class SomeMutation(Mutation): class Arguments: first_name = String(required=True) last_name = String(required=True) ... def mutate(root, info, first_name, last_name): ... ``` ## ClientIDMutation.mutate_and_get_payload Now only receives (`root`, `info`, `**input`) ### Middlewares If you are using Middelwares, you need to some adjustments: Before: ```python class MyGrapheneMiddleware(object): def resolve(self, next_mw, root, args, context, info): ## Middleware code return next_mw(root, args, context, info) ``` With 2.0: ```python class MyGrapheneMiddleware(object): def resolve(self, next_mw, root, info, **args): context = info.context ## Middleware code info.context = context Β  Β  Β  Β return next_mw(root, info, **args) ``` ## New Features ### InputObjectType If you are using `InputObjectType`, you now can access its fields via `getattr` (`my_input.myattr`) when resolving, instead of the classic way `my_input['myattr']`. And also use custom defined properties on your input class. Example. Before: ```python class UserInput(InputObjectType): id = ID(required=True) def is_valid_input(input): return input.get('id').startswith('userid_') class Query(ObjectType): user = graphene.Field(User, input=UserInput()) @resolve_only_args def resolve_user(root, input): user_id = input.get('id') if is_valid_input(user_id): return get_user(user_id) ``` With 2.0: ```python class UserInput(InputObjectType): id = ID(required=True) @property def is_valid(root): return root.id.startswith('userid_') class Query(ObjectType): user = graphene.Field(User, input=UserInput()) def resolve_user(root, info, input): if input.is_valid: return get_user(input.id) ``` ### Meta as Class arguments Now you can use the meta options as class arguments (**ONLY PYTHON 3**). Before: ```python class Dog(ObjectType): class Meta: interfaces = [Pet] name = String() ``` With 2.0: ```python class Dog(ObjectType, interfaces=[Pet]): name = String() ``` ### Abstract types Now you can create abstact types super easily, without the need of subclassing the meta. ```python class Base(ObjectType): class Meta: abstract = True id = ID() def resolve_id(root, info): return "{type}_{id}".format( type=root.__class__.__name__, id=root.id ) ``` ### UUID Scalar In Graphene 2.0 there is a new dedicated scalar for UUIDs, `UUID`. graphene-2.1.9/bin/000077500000000000000000000000001407436344100140725ustar00rootroot00000000000000graphene-2.1.9/bin/autolinter000077500000000000000000000005011407436344100162020ustar00rootroot00000000000000#!/bin/bash # Install the required scripts with # pip install autoflake autopep8 isort autoflake ./examples/ ./graphene/ -r --remove-unused-variables --remove-all-unused-imports --in-place autopep8 ./examples/ ./graphene/ -r --in-place --experimental --aggressive --max-line-length 120 isort -rc ./examples/ ./graphene/ graphene-2.1.9/bin/convert_documentation000077500000000000000000000001101407436344100204210ustar00rootroot00000000000000#!/bin/bash pandoc README.md --from markdown --to rst -s -o README.rst graphene-2.1.9/docs/000077500000000000000000000000001407436344100142525ustar00rootroot00000000000000graphene-2.1.9/docs/Makefile000066400000000000000000000166301407436344100157200ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help help: @echo "Please use \`make ' where is one of" @grep -E '^\.PHONY: [a-zA-Z_-]+ .*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = "(: |##)"}; {printf "\033[36m%-30s\033[0m %s\n", $$2, $$3}' .PHONY: install ## to install all documentation related requirements install: pip install -r requirements.txt .PHONY: clean ## to remove all built documentation clean: rm -rf $(BUILDDIR)/* .PHONY: html ## to make standalone HTML files html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." .PHONY: dirhtml ## to make HTML files named index.html in directories dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." .PHONY: singlehtml ## to make a single large HTML file singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." .PHONY: pickle ## to make pickle files pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." .PHONY: json ## to make JSON files json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." .PHONY: htmlhelp ## to make HTML files and a HTML help project htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." .PHONY: qthelp ## to make HTML files and a qthelp project qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Graphene.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Graphene.qhc" .PHONY: applehelp ## to make an Apple Help Book applehelp: $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp @echo @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." @echo "N.B. You won't be able to view it unless you put it in" \ "~/Library/Documentation/Help or install it in your application" \ "bundle." .PHONY: devhelp ## to make HTML files and a Devhelp project devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/Graphene" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Graphene" @echo "# devhelp" .PHONY: epub ## to make an epub epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." .PHONY: epub3 ## to make an epub3 epub3: $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3 @echo @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3." .PHONY: latex ## to make LaTeX files, you can set PAPER=a4 or PAPER=letter latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." .PHONY: latexpdf ## to make LaTeX files and run them through pdflatex latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." .PHONY: latexpdfja ## to make LaTeX files and run them through platex/dvipdfmx latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." .PHONY: text ## to make text files text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." .PHONY: man ## to make manual pages man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." .PHONY: texinfo ## to make Texinfo files texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." .PHONY: info ## to make Texinfo files and run them through makeinfo info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." .PHONY: gettext ## to make PO message catalogs gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." .PHONY: changes ## to make an overview of all changed/added/deprecated items changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." .PHONY: linkcheck ## to check all external links for integrity linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." .PHONY: doctest ## to run all doctests embedded in the documentation (if enabled) doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." .PHONY: coverage ## to run coverage check of the documentation (if enabled) coverage: $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage @echo "Testing of coverage in the sources finished, look at the " \ "results in $(BUILDDIR)/coverage/python.txt." .PHONY: xml ## to make Docutils-native XML files xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." .PHONY: pseudoxml ## to make pseudoxml-XML files for display purposes pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." .PHONY: dummy ## to check syntax errors of document sources dummy: $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy @echo @echo "Build finished. Dummy builder generates no files." .PHONY: livehtml ## to build and serve live-reloading documentation livehtml: sphinx-autobuild -b html --watch ../graphene $(ALLSPHINXOPTS) $(BUILDDIR)/html graphene-2.1.9/docs/_static/000077500000000000000000000000001407436344100157005ustar00rootroot00000000000000graphene-2.1.9/docs/_static/.gitkeep000066400000000000000000000000001407436344100173170ustar00rootroot00000000000000graphene-2.1.9/docs/api/000077500000000000000000000000001407436344100150235ustar00rootroot00000000000000graphene-2.1.9/docs/api/index.rst000066400000000000000000000032201407436344100166610ustar00rootroot00000000000000API Reference ============= Schema ------ .. autoclass:: graphene.types.schema.Schema :members: .. Uncomment sections / types as API documentation is fleshed out .. in each class Object types ------------ .. autoclass:: graphene.ObjectType .. autoclass:: graphene.InputObjectType .. autoclass:: graphene.Mutation :members: Fields (Mounted Types) ---------------------- .. autoclass:: graphene.Field .. autoclass:: graphene.Argument .. autoclass:: graphene.InputField Fields (Unmounted Types) ------------------------ .. autoclass:: graphene.types.unmountedtype.UnmountedType GraphQL Scalars --------------- .. autoclass:: graphene.Int() .. autoclass:: graphene.Float() .. autoclass:: graphene.String() .. autoclass:: graphene.Boolean() .. autoclass:: graphene.ID() Graphene Scalars ---------------- .. autoclass:: graphene.Date() .. autoclass:: graphene.DateTime() .. autoclass:: graphene.Time() .. autoclass:: graphene.Decimal() .. autoclass:: graphene.UUID() .. autoclass:: graphene.JSONString() Enum ---- .. autoclass:: graphene.Enum() Structures ---------- .. autoclass:: graphene.List .. autoclass:: graphene.NonNull Type Extension -------------- .. autoclass:: graphene.Interface() .. autoclass:: graphene.Union() Execution Metadata ------------------ .. autoclass:: graphene.ResolveInfo .. autoclass:: graphene.Context .. autoclass:: graphql.execution.base.ExecutionResult .. Relay .. ----- .. .. autoclass:: graphene.Node .. .. autoclass:: graphene.GlobalID .. .. autoclass:: graphene.ClientIDMutation .. .. autoclass:: graphene.Connection .. .. autoclass:: graphene.ConnectionField .. .. autoclass:: graphene.PageInfo graphene-2.1.9/docs/conf.py000066400000000000000000000310631407436344100155540ustar00rootroot00000000000000import os import sphinx_graphene_theme on_rtd = os.environ.get("READTHEDOCS", None) == "True" # -*- coding: utf-8 -*- # # Graphene documentation build configuration file, created by # sphinx-quickstart on Sun Sep 11 18:30:51 2016. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # import os import sys sys.path.insert(0, os.path.abspath("..")) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ "sphinx.ext.autodoc", "sphinx.ext.intersphinx", "sphinx.ext.todo", "sphinx.ext.coverage", "sphinx.ext.viewcode", "sphinx.ext.napoleon", ] if not on_rtd: extensions += ["sphinx.ext.githubpages"] # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = ".rst" # The encoding of source files. # # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = "index" # General information about the project. project = u"Graphene" copyright = u"Graphene 2016" author = u"Syrus Akbary" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = u"1.0" # The full version, including alpha/beta/rc tags. release = u"1.0" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # # today = '' # # Else, today_fmt is used as the format for a strftime call. # # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] # The reST default role (used for this markup: `text`) to use for all # documents. # # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = True # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # # html_theme = 'alabaster' # if on_rtd: # html_theme = 'sphinx_rtd_theme' html_theme = "sphinx_graphene_theme" html_theme_path = [sphinx_graphene_theme.get_html_theme_path()] # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] # The name for this set of Sphinx documents. # " v documentation" by default. # # html_title = u'Graphene v1.0' # A shorter title for the navigation bar. Default is the same as html_title. # # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # # html_logo = None # The name of an image file (relative to this directory) to use as a favicon of # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ["_static"] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. # # html_extra_path = [] # If not None, a 'Last updated on:' timestamp is inserted at every page # bottom, using the given strftime format. # The empty string is equivalent to '%b %d, %Y'. # # html_last_updated_fmt = None # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # # html_additional_pages = {} # If false, no module index is generated. # # html_domain_indices = True # If false, no index is generated. # # html_use_index = True # If true, the index is split into individual pages for each letter. # # html_split_index = False # If true, links to the reST sources are added to the pages. # # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Language to be used for generating the HTML full-text search index. # Sphinx supports the following languages: # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr', 'zh' # # html_search_language = 'en' # A dictionary with options for the search language support, empty by default. # 'ja' uses this config value. # 'zh' user can custom change `jieba` dictionary path. # # html_search_options = {'type': 'default'} # The name of a javascript file (relative to the configuration directory) that # implements a search results scorer. If empty, the default will be used. # # html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. htmlhelp_basename = "Graphenedoc" # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, "Graphene.tex", u"Graphene Documentation", u"Syrus Akbary", "manual") ] # The name of an image file (relative to this directory) to place at the top of # the title page. # # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # # latex_use_parts = False # If true, show page references after internal links. # # latex_show_pagerefs = False # If true, show URL addresses after external links. # # latex_show_urls = False # Documents to append as an appendix to all manuals. # # latex_appendices = [] # It false, will not define \strong, \code, itleref, \crossref ... but only # \sphinxstrong, ..., \sphinxtitleref, ... To help avoid clash with user added # packages. # # latex_keep_old_macro_names = True # If false, no module index is generated. # # latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [(master_doc, "graphene", u"Graphene Documentation", [author], 1)] # If true, show URL addresses after external links. # # man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ( master_doc, "Graphene", u"Graphene Documentation", author, "Graphene", "One line description of project.", "Miscellaneous", ) ] # Documents to append as an appendix to all manuals. # # texinfo_appendices = [] # If false, no module index is generated. # # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # # texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. # # texinfo_no_detailmenu = False # -- Options for Epub output ---------------------------------------------- # Bibliographic Dublin Core info. epub_title = project epub_author = author epub_publisher = author epub_copyright = copyright # The basename for the epub file. It defaults to the project name. # epub_basename = project # The HTML theme for the epub output. Since the default themes are not # optimized for small screen space, using the same theme for HTML and epub # output is usually not wise. This defaults to 'epub', a theme designed to save # visual space. # # epub_theme = 'epub' # The language of the text. It defaults to the language option # or 'en' if the language is not set. # # epub_language = '' # The scheme of the identifier. Typical schemes are ISBN or URL. # epub_scheme = '' # The unique identifier of the text. This can be a ISBN number # or the project homepage. # # epub_identifier = '' # A unique identification for the text. # # epub_uid = '' # A tuple containing the cover image and cover page html template filenames. # # epub_cover = () # A sequence of (type, uri, title) tuples for the guide element of content.opf. # # epub_guide = () # HTML files that should be inserted before the pages created by sphinx. # The format is a list of tuples containing the path and title. # # epub_pre_files = [] # HTML files that should be inserted after the pages created by sphinx. # The format is a list of tuples containing the path and title. # # epub_post_files = [] # A list of files that should not be packed into the epub file. epub_exclude_files = ["search.html"] # The depth of the table of contents in toc.ncx. # # epub_tocdepth = 3 # Allow duplicate toc entries. # # epub_tocdup = True # Choose between 'default' and 'includehidden'. # # epub_tocscope = 'default' # Fix unsupported image types using the Pillow. # # epub_fix_images = False # Scale large images. # # epub_max_image_width = 0 # How to display URL addresses: 'footnote', 'no', or 'inline'. # # epub_show_urls = 'inline' # If false, no index is generated. # # epub_use_index = True # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = { "https://docs.python.org/": None, "python": ("https://docs.python.org/", None), "graphene_django": ( "http://docs.graphene-python.org/projects/django/en/latest/", None, ), "graphene_sqlalchemy": ( "http://docs.graphene-python.org/projects/sqlalchemy/en/latest/", None, ), "graphene_gae": ("http://docs.graphene-python.org/projects/gae/en/latest/", None), } graphene-2.1.9/docs/execution/000077500000000000000000000000001407436344100162555ustar00rootroot00000000000000graphene-2.1.9/docs/execution/dataloader.rst000066400000000000000000000071211407436344100211100ustar00rootroot00000000000000Dataloader ========== DataLoader is a generic utility to be used as part of your application's data fetching layer to provide a simplified and consistent API over various remote data sources such as databases or web services via batching and caching. Batching -------- Batching is not an advanced feature, it's DataLoader's primary feature. Create loaders by providing a batch loading function. .. code:: python from promise import Promise from promise.dataloader import DataLoader class UserLoader(DataLoader): def batch_load_fn(self, keys): # Here we return a promise that will result on the # corresponding user for each key in keys return Promise.resolve([get_user(id=key) for key in keys]) A batch loading function accepts a list of keys, and returns a ``Promise`` which resolves to a list of ``values``. Then load individual values from the loader. ``DataLoader`` will coalesce all individual loads which occur within a single frame of execution (executed once the wrapping promise is resolved) and then call your batch function with all requested keys. .. code:: python user_loader = UserLoader() user_loader.load(1).then(lambda user: user_loader.load(user.best_friend_id)) user_loader.load(2).then(lambda user: user_loader.load(user.best_friend_id)) A naive application may have issued *four* round-trips to a backend for the required information, but with ``DataLoader`` this application will make at most *two*. Note that loaded values are one-to-one with the keys and must have the same order. This means that if you load all values from a single query, you must make sure that you then order the query result for the results to match the keys: .. code:: python class UserLoader(DataLoader): def batch_load_fn(self, keys): users = {user.id: user for user in User.objects.filter(id__in=keys)} return Promise.resolve([users.get(user_id) for user_id in keys]) ``DataLoader`` allows you to decouple unrelated parts of your application without sacrificing the performance of batch data-loading. While the loader presents an API that loads individual values, all concurrent requests will be coalesced and presented to your batch loading function. This allows your application to safely distribute data fetching requirements throughout your application and maintain minimal outgoing data requests. Using with Graphene ------------------- DataLoader pairs nicely well with Graphene/GraphQL. GraphQL fields are designed to be stand-alone functions. Without a caching or batching mechanism, it's easy for a naive GraphQL server to issue new database requests each time a field is resolved. Consider the following GraphQL request: .. code:: { me { name bestFriend { name } friends(first: 5) { name bestFriend { name } } } } Naively, if ``me``, ``bestFriend`` and ``friends`` each need to request the backend, there could be at most 13 database requests! When using DataLoader, we could define the User type using our previous example with leaner code and at most 4 database requests, and possibly fewer if there are cache hits. .. code:: python class User(graphene.ObjectType): name = graphene.String() best_friend = graphene.Field(lambda: User) friends = graphene.List(lambda: User) def resolve_best_friend(root, info): return user_loader.load(root.best_friend_id) def resolve_friends(root, info): return user_loader.load_many(root.friend_ids) graphene-2.1.9/docs/execution/execute.rst000066400000000000000000000057271407436344100204640ustar00rootroot00000000000000.. _SchemaExecute: Executing a query ================= For executing a query a schema, you can directly call the ``execute`` method on it. .. code:: python from graphene import Schema schema = Schema(...) result = schema.execute('{ name }') ``result`` represents the result of execution. ``result.data`` is the result of executing the query, ``result.errors`` is ``None`` if no errors occurred, and is a non-empty list if an error occurred. .. _SchemaExecuteContext: Context _______ You can pass context to a query via ``context``. .. code:: python from graphene import ObjectType, String, Schema class Query(ObjectType): name = String() def resolve_name(root, info): return info.context.get('name') schema = Schema(Query) result = schema.execute('{ name }', context={'name': 'Syrus'}) assert result.data['name'] == 'Syrus' Variables _________ You can pass variables to a query via ``variables``. .. code:: python from graphene import ObjectType, Field, ID, Schema class Query(ObjectType): user = Field(User, id=ID(required=True)) def resolve_user(root, info, id): return get_user_by_id(id) schema = Schema(Query) result = schema.execute( ''' query getUser($id: ID) { user(id: $id) { id firstName lastName } } ''', variables={'id': 12}, ) Root Value __________ Value used for :ref:`ResolverParamParent` in root queries and mutations can be overridden using ``root`` parameter. .. code:: python from graphene import ObjectType, Field, Schema class Query(ObjectType): me = Field(User) def resolve_user(root, info): return {'id': root.id, 'firstName': root.name} schema = Schema(Query) user_root = User(id=12, name='bob'} result = schema.execute( ''' query getUser { user { id firstName lastName } } ''', root=user_root ) assert result.data['user']['id'] == user_root.id Operation Name ______________ If there are multiple operations defined in a query string, ``operation_name`` should be used to indicate which should be executed. .. code:: python from graphene import ObjectType, Field, Schema class Query(ObjectType): me = Field(User) def resolve_user(root, info): return get_user_by_id(12) schema = Schema(Query) query_string = ''' query getUserWithFirstName { user { id firstName lastName } } query getUserWithFullName { user { id fullName } } ''' result = schema.execute( query_string, operation_name='getUserWithFullName' ) assert result.data['user']['fullName'] graphene-2.1.9/docs/execution/index.rst000066400000000000000000000001441407436344100201150ustar00rootroot00000000000000========= Execution ========= .. toctree:: :maxdepth: 2 execute middleware dataloader graphene-2.1.9/docs/execution/middleware.rst000066400000000000000000000036031407436344100211260ustar00rootroot00000000000000Middleware ========== You can use ``middleware`` to affect the evaluation of fields in your schema. A middleware is any object or function that responds to ``resolve(next_middleware, *args)``. Inside that method, it should either: - Send ``resolve`` to the next middleware to continue the evaluation; or - Return a value to end the evaluation early. Resolve arguments ----------------- Middlewares ``resolve`` is invoked with several arguments: - ``next`` represents the execution chain. Call ``next`` to continue evaluation. - ``root`` is the root value object passed throughout the query. - ``info`` is the resolver info. - ``args`` is the dict of arguments passed to the field. Example ------- This middleware only continues evaluation if the ``field_name`` is not ``'user'`` .. code:: python class AuthorizationMiddleware(object): def resolve(next, root, info, **args): if info.field_name == 'user': return None return next(root, info, **args) And then execute it with: .. code:: python result = schema.execute('THE QUERY', middleware=[AuthorizationMiddleware()]) Functional example ------------------ Middleware can also be defined as a function. Here we define a middleware that logs the time it takes to resolve each field .. code:: python from time import time as timer def timing_middleware(next, root, info, **args): start = timer() return_value = next(root, info, **args) duration = timer() - start logger.debug("{parent_type}.{field_name}: {duration} ms".format( parent_type=root._meta.name if root and hasattr(root, '_meta') else '', field_name=info.field_name, duration=round(duration * 1000, 2) )) return return_value And then execute it with: .. code:: python result = schema.execute('THE QUERY', middleware=[timing_middleware]) graphene-2.1.9/docs/index.rst000066400000000000000000000020541407436344100161140ustar00rootroot00000000000000Graphene ======== Contents: .. toctree:: :maxdepth: 2 quickstart types/index execution/index relay/index testing/index api/index .. _Integrations: Integrations ------------ * `Graphene-Django `_ (`source `_) * Flask-Graphql (`source `_) * `Graphene-SQLAlchemy `_ (`source `_) * `Graphene-GAE `_ (`source `_) * `Graphene-Mongo `_ (`source `_) * `Starlette `_ (`source `_) * `FastAPI `_ (`source `_) graphene-2.1.9/docs/quickstart.rst000066400000000000000000000112461407436344100172020ustar00rootroot00000000000000Getting started =============== Introduction ------------ What is GraphQL? ~~~~~~~~~~~~~~~~ GraphQL is a query language for your API. It provides a standard way to: * *describe data provided by a server* in a statically typed **Schema** * *request data* in a **Query** which exactly describes your data requirements and * *receive data* in a **Response** containing only the data you requested. For an introduction to GraphQL and an overview of its concepts, please refer to `the official GraphQL documentation`_. .. _the official GraphQL documentation: http://graphql.org/learn/ What is Graphene? ~~~~~~~~~~~~~~~~~ Graphene is a library that provides tools to implement a GraphQL API in Python using a *code-first* approach. Compare Graphene's *code-first* approach to building a GraphQL API with *schema-first* approaches like `Apollo Server`_ (JavaScript) or Ariadne_ (Python). Instead of writing GraphQL **Schema Definition Langauge (SDL)**, we write Python code to describe the data provided by your server. .. _Apollo Server: https://www.apollographql.com/docs/apollo-server/ .. _Ariadne: https://ariadne.readthedocs.io Graphene is fully featured with integrations for the most popular web frameworks and ORMs. Graphene produces schemas tha are fully compliant with the GraphQL spec and provides tools and patterns for building a Relay-Compliant API as well. An example in Graphene ---------------------- Let’s build a basic GraphQL schema to say "hello" and "goodbye" in Graphene. When we send a **Query** requesting only one **Field**, ``hello``, and specify a value for the ``name`` **Argument**... .. code:: { hello(name: "friend") } ...we would expect the following Response containing only the data requested (the ``goodbye`` field is not resolved). .. code:: { "data": { "hello": "Hello friend!" } } Requirements ~~~~~~~~~~~~ - Python (2.7, 3.4, 3.5, 3.6, pypy) - Graphene (2.0) Project setup ~~~~~~~~~~~~~ .. code:: bash pip install "graphene>=2.0" Creating a basic Schema ~~~~~~~~~~~~~~~~~~~~~~~ In Graphene, we can define a simple schema using the following code: .. code:: python from graphene import ObjectType, String, Schema class Query(ObjectType): # this defines a Field `hello` in our Schema with a single Argument `name` hello = String(name=String(default_value="stranger")) goodbye = String() # our Resolver method takes the GraphQL context (root, info) as well as # Argument (name) for the Field and returns data for the query Response def resolve_hello(root, info, name): return f'Hello {name}!' def resolve_goodbye(root, info): return 'See ya!' schema = Schema(query=Query) A GraphQL **Schema** describes each **Field** in the data model provided by the server using scalar types like *String*, *Int* and *Enum* and compound types like *List* and *Object*. For more details refer to the Graphene :ref:`TypesReference`. Our schema can also define any number of **Arguments** for our **Fields**. This is a powerful way for a **Query** to describe the exact data requirements for each **Field**. For each **Field** in our **Schema**, we write a **Resolver** method to fetch data requested by a client's **Query** using the current context and **Arguments**. For more details, refer to this section on :ref:`Resolvers`. Schema Definition Language (SDL) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In the `GraphQL Schema Definition Language`_, we could describe the fields defined by our example code as show below. .. _GraphQL Schema Definition Language: https://graphql.org/learn/schema/ .. code:: type Query { hello(name: String = "stranger"): String goodbye: String } Further examples in this documentation will use SDL to describe schema created by ObjectTypes and other fields. Querying ~~~~~~~~ Then we can start querying our **Schema** by passing a GraphQL query string to ``execute``: .. code:: python # we can query for our field (with the default argument) query_string = '{ hello }' result = schema.execute(query_string) print(result.data['hello']) # "Hello stranger" # or passing the argument in the query query_with_argument = '{ hello(name: "GraphQL") }' result = schema.execute(query_with_argument) print(result.data['hello']) # "Hello GraphQL!" Next steps ~~~~~~~~~~ Congrats! You got your first Graphene schema working! Normally, we don't need to directly execute a query string against our schema as Graphene provides many useful Integrations with popular web frameworks like Flask and Django. Check out :ref:`Integrations` for more information on how to get started serving your GraphQL API. graphene-2.1.9/docs/relay/000077500000000000000000000000001407436344100153665ustar00rootroot00000000000000graphene-2.1.9/docs/relay/connection.rst000066400000000000000000000025311407436344100202600ustar00rootroot00000000000000Connection ========== A connection is a vitaminized version of a List that provides ways of slicing and paginating through it. The way you create Connection types in ``graphene`` is using ``relay.Connection`` and ``relay.ConnectionField``. Quick example ------------- If we want to create a custom Connection on a given node, we have to subclass the ``Connection`` class. In the following example, ``extra`` will be an extra field in the connection, and ``other`` an extra field in the Connection Edge. .. code:: python class ShipConnection(Connection): extra = String() class Meta: node = Ship class Edge: other = String() The ``ShipConnection`` connection class, will have automatically a ``pageInfo`` field, and a ``edges`` field (which is a list of ``ShipConnection.Edge``). This ``Edge`` will have a ``node`` field linking to the specified node (in ``ShipConnection.Meta``) and the field ``other`` that we defined in the class. Connection Field ---------------- You can create connection fields in any Connection, in case any ObjectType that implements ``Node`` will have a default Connection. .. code:: python class Faction(graphene.ObjectType): name = graphene.String() ships = relay.ConnectionField(ShipConnection) def resolve_ships(root, info): return [] graphene-2.1.9/docs/relay/index.rst000066400000000000000000000017071407436344100172340ustar00rootroot00000000000000Relay ===== Graphene has complete support for `Relay`_ and offers some utils to make integration from Python easy. .. toctree:: :maxdepth: 2 nodes connection mutations Useful links ------------ - `Getting started with Relay`_ - `Relay Global Identification Specification`_ - `Relay Cursor Connection Specification`_ - `Relay input Object Mutation`_ .. _Relay: https://facebook.github.io/relay/docs/en/graphql-server-specification.html .. _Relay specification: https://facebook.github.io/relay/graphql/objectidentification.htm#sec-Node-root-field .. _Getting started with Relay: https://facebook.github.io/relay/docs/en/quick-start-guide.html .. _Relay Global Identification Specification: https://facebook.github.io/relay/graphql/objectidentification.htm .. _Relay Cursor Connection Specification: https://facebook.github.io/relay/graphql/connections.htm .. _Relay input Object Mutation: https://facebook.github.io/relay/graphql/mutations.htm graphene-2.1.9/docs/relay/mutations.rst000066400000000000000000000032201407436344100201400ustar00rootroot00000000000000Mutations ========= Most APIs don’t just allow you to read data, they also allow you to write. In GraphQL, this is done using mutations. Just like queries, Relay puts some additional requirements on mutations, but Graphene nicely manages that for you. All you need to do is make your mutation a subclass of ``relay.ClientIDMutation``. .. code:: python class IntroduceShip(relay.ClientIDMutation): class Input: ship_name = graphene.String(required=True) faction_id = graphene.String(required=True) ship = graphene.Field(Ship) faction = graphene.Field(Faction) @classmethod def mutate_and_get_payload(cls, root, info, **input): ship_name = input.ship_name faction_id = input.faction_id ship = create_ship(ship_name, faction_id) faction = get_faction(faction_id) return IntroduceShip(ship=ship, faction=faction) Accepting Files --------------- Mutations can also accept files, that's how it will work with different integrations: .. code:: python class UploadFile(graphene.ClientIDMutation): class Input: pass # nothing needed for uploading file # your return fields success = graphene.String() @classmethod def mutate_and_get_payload(cls, root, info, **input): # When using it in Django, context will be the request files = info.context.FILES # Or, if used in Flask, context will be the flask global request # files = context.files # do something with files return UploadFile(success=True) graphene-2.1.9/docs/relay/nodes.rst000066400000000000000000000063571407436344100172430ustar00rootroot00000000000000Nodes ===== A ``Node`` is an Interface provided by ``graphene.relay`` that contains a single field ``id`` (which is a ``ID!``). Any object that inherits from it has to implement a ``get_node`` method for retrieving a ``Node`` by an *id*. Quick example ------------- Example usage (taken from the `Starwars Relay example`_): .. code:: python class Ship(graphene.ObjectType): '''A ship in the Star Wars saga''' class Meta: interfaces = (relay.Node, ) name = graphene.String(description='The name of the ship.') @classmethod def get_node(cls, info, id): return get_ship(id) The ``id`` returned by the ``Ship`` type when you query it will be a scalar which contains enough info for the server to know its type and its id. For example, the instance ``Ship(id=1)`` will return ``U2hpcDox`` as the id when you query it (which is the base64 encoding of ``Ship:1``), and which could be useful later if we want to query a node by its id. Custom Nodes ------------ You can use the predefined ``relay.Node`` or you can subclass it, defining custom ways of how a node id is encoded (using the ``to_global_id`` method in the class) or how we can retrieve a Node given a encoded id (with the ``get_node_from_global_id`` method). Example of a custom node: .. code:: python class CustomNode(Node): class Meta: name = 'Node' @staticmethod def to_global_id(type, id): return '{}:{}'.format(type, id) @staticmethod def get_node_from_global_id(info, global_id, only_type=None): type, id = global_id.split(':') if only_type: # We assure that the node type that we want to retrieve # is the same that was indicated in the field type assert type == only_type._meta.name, 'Received not compatible node.' if type == 'User': return get_user(id) elif type == 'Photo': return get_photo(id) The ``get_node_from_global_id`` method will be called when ``CustomNode.Field`` is resolved. Accessing node types -------------------- If we want to retrieve node instances from a ``global_id`` (scalar that identifies an instance by it's type name and id), we can simply do ``Node.get_node_from_global_id(info, global_id)``. In the case we want to restrict the instance retrieval to a specific type, we can do: ``Node.get_node_from_global_id(info, global_id, only_type=Ship)``. This will raise an error if the ``global_id`` doesn't correspond to a Ship type. Node Root field --------------- As is required in the `Relay specification`_, the server must implement a root field called ``node`` that returns a ``Node`` Interface. For this reason, ``graphene`` provides the field ``relay.Node.Field``, which links to any type in the Schema which implements ``Node``. Example usage: .. code:: python class Query(graphene.ObjectType): # Should be CustomNode.Field() if we want to use our custom Node node = relay.Node.Field() .. _Relay specification: https://facebook.github.io/relay/docs/graphql-relay-specification.html .. _Starwars Relay example: https://github.com/graphql-python/graphene/blob/master/examples/starwars_relay/schema.py graphene-2.1.9/docs/requirements.txt000066400000000000000000000001761407436344100175420ustar00rootroot00000000000000# Required library Sphinx==1.5.3 sphinx-autobuild==0.7.1 # Docs template http://graphene-python.org/sphinx_graphene_theme.zip graphene-2.1.9/docs/testing/000077500000000000000000000000001407436344100157275ustar00rootroot00000000000000graphene-2.1.9/docs/testing/index.rst000066400000000000000000000070511407436344100175730ustar00rootroot00000000000000=================== Testing in Graphene =================== Automated testing is an extremely useful bug-killing tool for the modern developer. You can use a collection of tests – a test suite – to solve, or avoid, a number of problems: - When you’re writing new code, you can use tests to validate your code works as expected. - When you’re refactoring or modifying old code, you can use tests to ensure your changes haven’t affected your application’s behavior unexpectedly. Testing a GraphQL application is a complex task, because a GraphQL application is made of several layers of logic – schema definition, schema validation, permissions and field resolution. With Graphene test-execution framework and assorted utilities, you can simulate GraphQL requests, execute mutations, inspect your application’s output and generally verify your code is doing what it should be doing. Testing tools ------------- Graphene provides a small set of tools that come in handy when writing tests. Test Client ~~~~~~~~~~~ The test client is a Python class that acts as a dummy GraphQL client, allowing you to test your views and interact with your Graphene-powered application programmatically. Some of the things you can do with the test client are: - Simulate Queries and Mutations and observe the response. - Test that a given query request is rendered by a given Django template, with a template context that contains certain values. Overview and a quick example ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To use the test client, instantiate ``graphene.test.Client`` and retrieve GraphQL responses: .. code:: python from graphene.test import Client def test_hey(): client = Client(my_schema) executed = client.execute('''{ hey }''') assert executed == { 'data': { 'hey': 'hello!' } } Execute parameters ~~~~~~~~~~~~~~~~~~ You can also add extra keyword arguments to the ``execute`` method, such as ``context``, ``root``, ``variables``, ...: .. code:: python from graphene.test import Client def test_hey(): client = Client(my_schema) executed = client.execute('''{ hey }''', context={'user': 'Peter'}) assert executed == { 'data': { 'hey': 'hello Peter!' } } Snapshot testing ~~~~~~~~~~~~~~~~ As our APIs evolve, we need to know when our changes introduce any breaking changes that might break some of the clients of our GraphQL app. However, writing tests and replicate the same response we expect from our GraphQL application can be tedious and repetitive task, and sometimes it's easier to skip this process. Because of that, we recommend the usage of `SnapshotTest `_. SnapshotTest let us write all this tests in a breeze, as creates automatically the ``snapshots`` for us the first time the test is executed. Here is a simple example on how our tests will look if we use ``pytest``: .. code:: python def test_hey(snapshot): client = Client(my_schema) # This will create a snapshot dir and a snapshot file # the first time the test is executed, with the response # of the execution. snapshot.assert_match(client.execute('''{ hey }''')) If we are using ``unittest``: .. code:: python from snapshottest import TestCase class APITestCase(TestCase): def test_api_me(self): """Testing the API for /me""" client = Client(my_schema) self.assertMatchSnapshot(client.execute('''{ hey }''')) graphene-2.1.9/docs/types/000077500000000000000000000000001407436344100154165ustar00rootroot00000000000000graphene-2.1.9/docs/types/abstracttypes.rst000066400000000000000000000017511407436344100210440ustar00rootroot00000000000000AbstractTypes ============= An AbstractType contains fields that can be shared among ``graphene.ObjectType``, ``graphene.Interface``, ``graphene.InputObjectType`` or other ``graphene.AbstractType``. The basics: - Each AbstractType is a Python class that inherits from ``graphene.AbstractType``. - Each attribute of the AbstractType represents a field (a ``graphene.Field`` or ``graphene.InputField`` depending on where it is mounted) Quick example ------------- In this example UserFields is an ``AbstractType`` with a name. ``User`` and ``UserInput`` are two types that have their own fields plus the ones defined in ``UserFields``. .. code:: python import graphene class UserFields(graphene.AbstractType): name = graphene.String() class User(graphene.ObjectType, UserFields): pass class UserInput(graphene.InputObjectType, UserFields): pass .. code:: type User { name: String } inputtype UserInput { name: String } graphene-2.1.9/docs/types/enums.rst000066400000000000000000000042631407436344100173040ustar00rootroot00000000000000Enums ===== An ``Enum`` is a special ``GraphQL`` type that represents a set of symbolic names (members) bound to unique, constant values. Definition ---------- You can create an ``Enum`` using classes: .. code:: python import graphene class Episode(graphene.Enum): NEWHOPE = 4 EMPIRE = 5 JEDI = 6 But also using instances of Enum: .. code:: python Episode = graphene.Enum('Episode', [('NEWHOPE', 4), ('EMPIRE', 5), ('JEDI', 6)]) Value descriptions ------------------ It's possible to add a description to an enum value, for that the enum value needs to have the ``description`` property on it. .. code:: python class Episode(graphene.Enum): NEWHOPE = 4 EMPIRE = 5 JEDI = 6 @property def description(self): if self == Episode.NEWHOPE: return 'New Hope Episode' return 'Other episode' Usage with Python Enums ----------------------- In case the Enums are already defined it's possible to reuse them using the ``Enum.from_enum`` function. .. code:: python graphene.Enum.from_enum(AlreadyExistingPyEnum) ``Enum.from_enum`` supports a ``description`` and ``deprecation_reason`` lambdas as input so you can add description etc. to your enum without changing the original: .. code:: python graphene.Enum.from_enum( AlreadyExistingPyEnum, description=lambda v: return 'foo' if v == AlreadyExistingPyEnum.Foo else 'bar') Notes ----- ``graphene.Enum`` uses |enum.Enum|_ internally (or a backport if that's not available) and can be used in a similar way, with the exception of member getters. In the Python ``Enum`` implementation you can access a member by initing the Enum. .. code:: python from enum import Enum class Color(Enum): RED = 1 GREEN = 2 BLUE = 3 assert Color(1) == Color.RED However, in Graphene ``Enum`` you need to call get to have the same effect: .. code:: python from graphene import Enum class Color(Enum): RED = 1 GREEN = 2 BLUE = 3 assert Color.get(1) == Color.RED .. |enum.Enum| replace:: ``enum.Enum`` .. _enum.Enum: https://docs.python.org/3/library/enum.html graphene-2.1.9/docs/types/index.rst000066400000000000000000000003331407436344100172560ustar00rootroot00000000000000.. _TypesReference: =============== Types Reference =============== .. toctree:: :maxdepth: 1 schema scalars list-and-nonnull objecttypes enums interfaces unions mutations abstracttypes graphene-2.1.9/docs/types/interfaces.rst000066400000000000000000000111141407436344100202710ustar00rootroot00000000000000.. _Interfaces: Interfaces ========== An *Interface* is an abstract type that defines a certain set of fields that a type must include to implement the interface. For example, you can define an Interface ``Character`` that represents any character in the Star Wars trilogy: .. code:: python import graphene class Character(graphene.Interface): id = graphene.ID(required=True) name = graphene.String(required=True) friends = graphene.List(lambda: Character) Any ObjectType that implements ``Character`` will have these exact fields, with these arguments and return types. For example, here are some types that might implement ``Character``: .. code:: python class Human(graphene.ObjectType): class Meta: interfaces = (Character, ) starships = graphene.List(Starship) home_planet = graphene.String() class Droid(graphene.ObjectType): class Meta: interfaces = (Character, ) primary_function = graphene.String() Both of these types have all of the fields from the ``Character`` interface, but also bring in extra fields, ``home_planet``, ``starships`` and ``primary_function``, that are specific to that particular type of character. The full GraphQL schema defition will look like this: .. code:: interface Character { id: ID! name: String! friends: [Character] } type Human implements Character { id: ID! name: String! friends: [Character] starships: [Starship] homePlanet: String } type Droid implements Character { id: ID! name: String! friends: [Character] primaryFunction: String } Interfaces are useful when you want to return an object or set of objects, which might be of several different types. For example, you can define a field ``hero`` that resolves to any ``Character``, depending on the episode, like this: .. code:: python class Query(graphene.ObjectType): hero = graphene.Field( Character, required=True, episode=graphene.Int(required=True) ) def resolve_hero(root, info, episode): # Luke is the hero of Episode V if episode == 5: return get_human(name='Luke Skywalker') return get_droid(name='R2-D2') schema = graphene.Schema(query=Query, types=[Human, Droid]) This allows you to directly query for fields that exist on the Character interface as well as selecting specific fields on any type that implements the interface using `inline fragments `_. For example, the following query: .. code:: query HeroForEpisode($episode: Int!) { hero(episode: $episode) { __typename name ... on Droid { primaryFunction } ... on Human { homePlanet } } } Will return the following data with variables ``{ "episode": 4 }``: .. code:: json { "data": { "hero": { "__typename": "Droid", "name": "R2-D2", "primaryFunction": "Astromech" } } } And different data with the variables ``{ "episode": 5 }``: .. code:: json { "data": { "hero": { "__typename": "Human", "name": "Luke Skywalker", "homePlanet": "Tatooine" } } } Resolving data objects to types ------------------------------- As you build out your schema in Graphene it's common for your resolvers to return objects that represent the data backing your GraphQL types rather than instances of the Graphene types (e.g. Django or SQLAlchemy models). This works well with ``ObjectType`` and ``Scalar`` fields, however when you start using Interfaces you might come across this error: .. code:: "Abstract type Character must resolve to an Object type at runtime for field Query.hero ..." This happens because Graphene doesn't have enough information to convert the data object into a Graphene type needed to resolve the ``Interface``. To solve this you can define a ``resolve_type`` class method on the ``Interface`` which maps a data object to a Graphene type: .. code:: python class Character(graphene.Interface): id = graphene.ID(required=True) name = graphene.String(required=True) @classmethod def resolve_type(cls, instance, info): if instance.type == 'DROID': return Droid return Human graphene-2.1.9/docs/types/list-and-nonnull.rst000066400000000000000000000034441407436344100213530ustar00rootroot00000000000000Lists and Non-Null ================== Object types, scalars, and enums are the only kinds of types you can define in Graphene. But when you use the types in other parts of the schema, or in your query variable declarations, you can apply additional type modifiers that affect validation of those values. NonNull ------- .. code:: python import graphene class Character(graphene.ObjectType): name = graphene.NonNull(graphene.String) Here, we're using a ``String`` type and marking it as Non-Null by wrapping it using the ``NonNull`` class. This means that our server always expects to return a non-null value for this field, and if it ends up getting a null value that will actually trigger a GraphQL execution error, letting the client know that something has gone wrong. The previous ``NonNull`` code snippet is also equivalent to: .. code:: python import graphene class Character(graphene.ObjectType): name = graphene.String(required=True) List ---- .. code:: python import graphene class Character(graphene.ObjectType): appears_in = graphene.List(graphene.String) Lists work in a similar way: We can use a type modifier to mark a type as a ``List``, which indicates that this field will return a list of that type. It works the same for arguments, where the validation step will expect a list for that value. NonNull Lists ------------- By default items in a list will be considered nullable. To define a list without any nullable items the type needs to be marked as ``NonNull``. For example: .. code:: python import graphene class Character(graphene.ObjectType): appears_in = graphene.List(graphene.NonNull(graphene.String)) The above results in the type definition: .. code:: type Character { appearsIn: [String!] } graphene-2.1.9/docs/types/mutations.rst000066400000000000000000000103001407436344100201650ustar00rootroot00000000000000Mutations ========= A Mutation is a special ObjectType that also defines an Input. Quick example ------------- This example defines a Mutation: .. code:: python import graphene class CreatePerson(graphene.Mutation): class Arguments: name = graphene.String() ok = graphene.Boolean() person = graphene.Field(lambda: Person) def mutate(root, info, name): person = Person(name=name) ok = True return CreatePerson(person=person, ok=ok) **person** and **ok** are the output fields of the Mutation when it is resolved. **Arguments** attributes are the arguments that the Mutation ``CreatePerson`` needs for resolving, in this case **name** will be the only argument for the mutation. **mutate** is the function that will be applied once the mutation is called. This method is just a special resolver that we can change data within. It takes the same arguments as the standard query :ref:`ResolverArguments`. So, we can finish our schema like this: .. code:: python # ... the Mutation Class class Person(graphene.ObjectType): name = graphene.String() age = graphene.Int() class MyMutations(graphene.ObjectType): create_person = CreatePerson.Field() # We must define a query for our schema class Query(graphene.ObjectType): person = graphene.Field(Person) schema = graphene.Schema(query=Query, mutation=MyMutations) Executing the Mutation ---------------------- Then, if we query (``schema.execute(query_str)``) the following: .. code:: mutation myFirstMutation { createPerson(name:"Peter") { person { name } ok } } We should receive: .. code:: json { "createPerson": { "person" : { "name": "Peter" }, "ok": true } } InputFields and InputObjectTypes ---------------------------------- InputFields are used in mutations to allow nested input data for mutations To use an InputField you define an InputObjectType that specifies the structure of your input data .. code:: python import graphene class PersonInput(graphene.InputObjectType): name = graphene.String(required=True) age = graphene.Int(required=True) class CreatePerson(graphene.Mutation): class Arguments: person_data = PersonInput(required=True) person = graphene.Field(Person) @staticmethod def mutate(root, info, person_data=None): person = Person( name=person_data.name, age=person_data.age ) return CreatePerson(person=person) Note that **name** and **age** are part of **person_data** now Using the above mutation your new query would look like this: .. code:: mutation myFirstMutation { createPerson(personData: {name:"Peter", age: 24}) { person { name, age } } } InputObjectTypes can also be fields of InputObjectTypes allowing you to have as complex of input data as you need .. code:: python import graphene class LatLngInput(graphene.InputObjectType): lat = graphene.Float() lng = graphene.Float() #A location has a latlng associated to it class LocationInput(graphene.InputObjectType): name = graphene.String() latlng = graphene.InputField(LatLngInput) Output type example ------------------- To return an existing ObjectType instead of a mutation-specific type, set the **Output** attribute to the desired ObjectType: .. code:: python import graphene class CreatePerson(graphene.Mutation): class Arguments: name = graphene.String() Output = Person def mutate(root, info, name): return Person(name=name) Then, if we query (``schema.execute(query_str)``) the following: .. code:: mutation myFirstMutation { createPerson(name:"Peter") { name __typename } } We should receive: .. code:: json { "createPerson": { "name": "Peter", "__typename": "Person" } } graphene-2.1.9/docs/types/objecttypes.rst000066400000000000000000000326251407436344100205130ustar00rootroot00000000000000.. _ObjectType: ObjectType ========== A Graphene *ObjectType* is the building block used to define the relationship between **Fields** in your **Schema** and how their data is retrieved. The basics: - Each ObjectType is a Python class that inherits from ``graphene.ObjectType``. - Each attribute of the ObjectType represents a ``Field``. - Each ``Field`` has a :ref:`resolver method` to fetch data (or :ref:`DefaultResolver`). Quick example ------------- This example model defines a Person, with a first and a last name: .. code:: python from graphene import ObjectType, String class Person(ObjectType): first_name = String() last_name = String() full_name = String() def resolve_full_name(parent, info): return f"{parent.first_name} {parent.last_name}" This *ObjectType* defines the field **first\_name**, **last\_name**, and **full\_name**. Each field is specified as a class attribute, and each attribute maps to a Field. Data is fetched by our ``resolve_full_name`` :ref:`resolver method` for ``full_name`` field and the :ref:`DefaultResolver` for other fields. The above ``Person`` ObjectType has the following schema representation: .. code:: type Person { firstName: String lastName: String fullName: String } .. _Resolvers: Resolvers --------- A **Resolver** is a method that helps us answer **Queries** by fetching data for a **Field** in our **Schema**. Resolvers are lazily executed, so if a field is not included in a query, its resolver will not be executed. Each field on an *ObjectType* in Graphene should have a corresponding resolver method to fetch data. This resolver method should match the field name. For example, in the ``Person`` type above, the ``full_name`` field is resolved by the method ``resolve_full_name``. Each resolver method takes the parameters: * :ref:`ResolverParamParent` for the value object use to resolve most fields * :ref:`ResolverParamInfo` for query and schema meta information and per-request context * :ref:`ResolverParamGraphQLArguments` as defined on the **Field**. .. _ResolverArguments: Resolver Parameters ~~~~~~~~~~~~~~~~~~~ .. _ResolverParamParent: Parent Value Object (*parent*) ****************************** This parameter is typically used to derive the values for most fields on an *ObjectType*. The first parameter of a resolver method (*parent*) is the value object returned from the resolver of the parent field. If there is no parent field, such as a root Query field, then the value for *parent* is set to the ``root_value`` configured while executing the query (default ``None``). See :ref:`SchemaExecute` for more details on executing queries. Resolver example ^^^^^^^^^^^^^^^^ If we have a schema with Person type and one field on the root query. .. code:: python from graphene import ObjectType, String, Field class Person(ObjectType): full_name = String() def resolve_full_name(parent, info): return f"{parent.first_name} {parent.last_name}" class Query(ObjectType): me = Field(Person) def resolve_me(parent, info): # returns an object that represents a Person return get_human(name="Luke Skywalker") When we execute a query against that schema. .. code:: python schema = Schema(query=Query) query_string = "{ me { fullName } }" result = schema.execute(query_string) assert result["data"]["me"] == {"fullName": "Luke Skywalker") Then we go through the following steps to resolve this query: * ``parent`` is set with the root_value from query execution (None). * ``Query.resolve_me`` called with ``parent`` None which returns a value object ``Person("Luke", "Skywalker")``. * This value object is then used as ``parent`` while calling ``Person.resolve_full_name`` to resolve the scalar String value "Luke Skywalker". * The scalar value is serialized and sent back in the query response. Each resolver returns the next :ref:`ResolverParamParent` to be used in executing the following resolver in the chain. If the Field is a Scalar type, that value will be serialized and sent in the **Response**. Otherwise, while resolving Compound types like *ObjectType*, the value be passed forward as the next :ref:`ResolverParamParent`. Naming convention ^^^^^^^^^^^^^^^^^ This :ref:`ResolverParamParent` is sometimes named ``obj``, ``parent``, or ``source`` in other GraphQL documentation. It can also be named after the value object being resolved (ex. ``root`` for a root Query or Mutation, and ``person`` for a Person value object). Sometimes this argument will be named ``self`` in Graphene code, but this can be misleading due to :ref:`ResolverImplicitStaticMethod` while executing queries in Graphene. .. _ResolverParamInfo: GraphQL Execution Info (*info*) ******************************* The second parameter provides two things: * reference to meta information about the execution of the current GraphQL Query (fields, schema, parsed query, etc.) * access to per-request ``context`` which can be used to store user authentication, data loader instances or anything else useful for resolving the query. Only context will be required for most applications. See :ref:`SchemaExecuteContext` for more information about setting context. .. _ResolverParamGraphQLArguments: GraphQL Arguments (*\*\*kwargs*) ******************************** Any arguments that a field defines gets passed to the resolver function as keyword arguments. For example: .. code:: python from graphene import ObjectType, Field, String class Query(ObjectType): human_by_name = Field(Human, name=String(required=True)) def resolve_human_by_name(parent, info, name): return get_human(name=name) You can then execute the following query: .. code:: query { humanByName(name: "Luke Skywalker") { firstName lastName } } Convenience Features of Graphene Resolvers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. _ResolverImplicitStaticMethod: Implicit staticmethod ********************* One surprising feature of Graphene is that all resolver methods are treated implicitly as staticmethods. This means that, unlike other methods in Python, the first argument of a resolver is *never* ``self`` while it is being executed by Graphene. Instead, the first argument is always :ref:`ResolverParamParent`. In practice, this is very convenient as, in GraphQL, we are almost always more concerned with the using the parent value object to resolve queries than attributes on the Python object itself. The two resolvers in this example are effectively the same. .. code:: python from graphene import ObjectType, String class Person(ObjectType): first_name = String() last_name = String() @staticmethod def resolve_first_name(parent, info): ''' Decorating a Python method with `staticmethod` ensures that `self` will not be provided as an argument. However, Graphene does not need this decorator for this behavior. ''' return parent.first_name def resolve_last_name(parent, info): ''' Normally the first argument for this method would be `self`, but Graphene executes this as a staticmethod implicitly. ''' return parent.last_name # ... If you prefer your code to be more explict, feel free to use ``@staticmethod`` decorators. Otherwise, your code may be cleaner without them! .. _DefaultResolver: Default Resolver **************** If a resolver method is not defined for a **Field** attribute on our *ObjectType*, Graphene supplies a default resolver. If the :ref:`ResolverParamParent` is a dictionary, the resolver will look for a dictionary key matching the field name. Otherwise, the resolver will get the attribute from the parent value object matching the field name. .. code:: python from collections import namedtuple from graphene import ObjectType, String, Field, Schema PersonValueObject = namedtuple('Person', 'first_name', 'last_name') class Person(ObjectType): first_name = String() last_name = String() class Query(ObjectType): me = Field(Person) my_best_friend = Field(Person) def resolve_me(parent, info): # always pass an object for `me` field return PersonValueObject(first_name='Luke', last_name='Skywalker') def resolve_my_best_friend(parent, info): # always pass a dictionary for `my_best_fiend_field` return {"first_name": "R2", "last_name": "D2"} schema = Schema(query=Query) result = schema.execute(''' { me { firstName lastName } myBestFriend { firstName lastName } } ''') # With default resolvers we can resolve attributes from an object.. assert result['data']['me'] == {"firstName": "Luke", "lastName": "Skywalker"} # With default resolvers, we can also resolve keys from a dictionary.. assert result['data']['my_best_friend'] == {"firstName": "R2", "lastName": "D2"} Advanced ~~~~~~~~ GraphQL Argument defaults ************************* If you define an argument for a field that is not required (and in a query execution it is not provided as an argument) it will not be passed to the resolver function at all. This is so that the developer can differenciate between a ``undefined`` value for an argument and an explicit ``null`` value. For example, given this schema: .. code:: python from graphene import ObjectType, String class Query(ObjectType): hello = String(required=True, name=String()) def resolve_hello(parent, info, name): return name if name else 'World' And this query: .. code:: query { hello } An error will be thrown: .. code:: TypeError: resolve_hello() missing 1 required positional argument: 'name' You can fix this error in serveral ways. Either by combining all keyword arguments into a dict: .. code:: python from graphene import ObjectType, String class Query(ObjectType): hello = String(required=True, name=String()) def resolve_hello(parent, info, **kwargs): name = kwargs.get('name', 'World') return f'Hello, {name}!' Or by setting a default value for the keyword argument: .. code:: python from graphene import ObjectType, String class Query(ObjectType): hello = String(required=True, name=String()) def resolve_hello(parent, info, name='World'): return f'Hello, {name}!' One can also set a default value for an Argument in the GraphQL schema itself using Graphene! .. code:: python from graphene import ObjectType, String class Query(ObjectType): hello = String( required=True, name=String(default_value='World') ) def resolve_hello(parent, info, name): return f'Hello, {name}!' Resolvers outside the class *************************** A field can use a custom resolver from outside the class: .. code:: python from graphene import ObjectType, String def resolve_full_name(person, info): return '{} {}'.format(person.first_name, person.last_name) class Person(ObjectType): first_name = String() last_name = String() full_name = String(resolver=resolve_full_name) Instances as value objects ************************** Graphene ``ObjectType``\ s can act as value objects too. So with the previous example you could use ``Person`` to capture data for each of the *ObjectType*'s fields. .. code:: python peter = Person(first_name='Peter', last_name='Griffin') peter.first_name # prints "Peter" peter.last_name # prints "Griffin" Field camelcasing ***************** Graphene automatically camelcases fields on *ObjectType* from ``field_name`` to ``fieldName`` to conform with GraphQL standards. See :ref:`SchemaAutoCamelCase` for more information. *ObjectType* Configuration - Meta class --------------------------------------- Graphene uses a Meta inner class on *ObjectType* to set different options. GraphQL type name ~~~~~~~~~~~~~~~~~ By default the type name in the GraphQL schema will be the same as the class name that defines the ``ObjectType``. This can be changed by setting the ``name`` property on the ``Meta`` class: .. code:: python from graphene import ObjectType class MyGraphQlSong(ObjectType): class Meta: name = 'Song' GraphQL Description ~~~~~~~~~~~~~~~~~~~ The schema description of an *ObjectType* can be set as a docstring on the Python object or on the Meta inner class. .. code:: python from graphene import ObjectType class MyGraphQlSong(ObjectType): ''' We can set the schema description for an Object Type here on a docstring ''' class Meta: description = 'But if we set the description in Meta, this value is used instead' Interfaces & Possible Types ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Setting ``interfaces`` in Meta inner class specifies the GraphQL Interfaces that this Object implements. Providing ``possible_types`` helps Graphene resolve ambiguous types such as interfaces or Unions. See :ref:`Interfaces` for more information. .. code:: python from graphene import ObjectType, Node Song = namedtuple('Song', ('title', 'artist')) class MyGraphQlSong(ObjectType): class Meta: interfaces = (Node, ) possible_types = (Song, ) .. _Interface: /docs/interfaces/ graphene-2.1.9/docs/types/scalars.rst000066400000000000000000000067351407436344100176130ustar00rootroot00000000000000.. _Scalars: Scalars ======= All Scalar types accept the following arguments. All are optional: ``name``: *string* Override the name of the Field. ``description``: *string* A description of the type to show in the GraphiQL browser. ``required``: *boolean* If ``True``, the server will enforce a value for this field. See `NonNull <../list-and-nonnull.html#nonnull>`_. Default is ``False``. ``deprecation_reason``: *string* Provide a deprecation reason for the Field. ``default_value``: *any* Provide a default value for the Field. Base scalars ------------ Graphene defines the following base Scalar Types: ``graphene.String`` Represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text. ``graphene.Int`` Represents non-fractional signed whole numeric values. Int is a signed 32‐bit integer per the `GraphQL spec `_ ``graphene.Float`` Represents signed double-precision fractional values as specified by `IEEE 754 `_. ``graphene.Boolean`` Represents `true` or `false`. ``graphene.ID`` Represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `"4"`) or integer (such as `4`) input value will be accepted as an ID. Graphene also provides custom scalars for Dates, Times, and JSON: ``graphene.types.datetime.Date`` Represents a Date value as specified by `iso8601 `_. ``graphene.types.datetime.DateTime`` Represents a DateTime value as specified by `iso8601 `_. ``graphene.types.datetime.Time`` Represents a Time value as specified by `iso8601 `_. ``graphene.types.json.JSONString`` Represents a JSON string. Custom scalars -------------- You can create custom scalars for your schema. The following is an example for creating a DateTime scalar: .. code:: python import datetime from graphene.types import Scalar from graphql.language import ast class DateTime(Scalar): '''DateTime Scalar Description''' @staticmethod def serialize(dt): return dt.isoformat() @staticmethod def parse_literal(node): if isinstance(node, ast.StringValue): return datetime.datetime.strptime( node.value, "%Y-%m-%dT%H:%M:%S.%f") @staticmethod def parse_value(value): return datetime.datetime.strptime(value, "%Y-%m-%dT%H:%M:%S.%f") Mounting Scalars ---------------- Scalars mounted in a ``ObjectType``, ``Interface`` or ``Mutation`` act as ``Field``\ s. .. code:: python class Person(graphene.ObjectType): name = graphene.String() # Is equivalent to: class Person(graphene.ObjectType): name = graphene.Field(graphene.String) **Note:** when using the ``Field`` constructor directly, pass the type and not an instance. Types mounted in a ``Field`` act as ``Argument``\ s. .. code:: python graphene.Field(graphene.String, to=graphene.String()) # Is equivalent to: graphene.Field(graphene.String, to=graphene.Argument(graphene.String)) graphene-2.1.9/docs/types/schema.rst000066400000000000000000000052161407436344100174140ustar00rootroot00000000000000Schema ====== A GraphQL **Schema** defines the types and relationship between **Fields** in your API. A Schema is created by supplying the root :ref:`ObjectType` of each operation, query (mandatory), mutation and subscription. Schema will collect all type definitions related to the root operations and then supplied to the validator and executor. .. code:: python my_schema = Schema( query=MyRootQuery, mutation=MyRootMutation, subscription=MyRootSubscription ) A Root Query is just a special :ref:`ObjectType` that :ref:`defines the fields ` that are the entrypoint for your API. Root Mutation and Root Subscription are similar to Root Query, but for different operation types: * Query fetches data * Mutation to changes data and retrieve the changes * Subscription to sends changes to clients in real time Review the `GraphQL documentation on Schema`_ for a brief overview of fields, schema and operations. .. _GraphQL documentation on Schema: https://graphql.org/learn/schema/ Querying -------- To query a schema, call the ``execute`` method on it. See :ref:`SchemaExecute` for more details. .. code:: python query_string = 'query whoIsMyBestFriend { myBestFriend { lastName } }' my_schema.execute(query_string) Types ----- There are some cases where the schema cannot access all of the types that we plan to have. For example, when a field returns an ``Interface``, the schema doesn't know about any of the implementations. In this case, we need to use the ``types`` argument when creating the Schema. .. code:: python my_schema = Schema( query=MyRootQuery, types=[SomeExtraObjectType, ] ) .. _SchemaAutoCamelCase: Auto CamelCase field names -------------------------- By default all field and argument names (that are not explicitly set with the ``name`` arg) will be converted from ``snake_case`` to ``camelCase`` (as the API is usually being consumed by a js/mobile client) For example with the ObjectType .. code:: python class Person(graphene.ObjectType): last_name = graphene.String() other_name = graphene.String(name='_other_Name') the ``last_name`` field name is converted to ``lastName``. In case you don't want to apply this transformation, provide a ``name`` argument to the field constructor. ``other_name`` converts to ``_other_Name`` (without further transformations). Your query should look like .. code:: { lastName _other_Name } To disable this behavior, set the ``auto_camelcase`` to ``False`` upon schema instantiation. .. code:: python my_schema = Schema( query=MyRootQuery, auto_camelcase=False, ) graphene-2.1.9/docs/types/unions.rst000066400000000000000000000027321407436344100174670ustar00rootroot00000000000000Unions ====== Union types are very similar to interfaces, but they don't get to specify any common fields between the types. The basics: - Each Union is a Python class that inherits from ``graphene.Union``. - Unions don't have any fields on it, just links to the possible objecttypes. Quick example ------------- This example model defines several ObjectTypes with their own fields. ``SearchResult`` is the implementation of ``Union`` of this object types. .. code:: python import graphene class Human(graphene.ObjectType): name = graphene.String() born_in = graphene.String() class Droid(graphene.ObjectType): name = graphene.String() primary_function = graphene.String() class Starship(graphene.ObjectType): name = graphene.String() length = graphene.Int() class SearchResult(graphene.Union): class Meta: types = (Human, Droid, Starship) Wherever we return a SearchResult type in our schema, we might get a Human, a Droid, or a Starship. Note that members of a union type need to be concrete object types; you can't create a union type out of interfaces or other unions. The above types have the following representation in a schema: .. code:: type Droid { name: String primaryFunction: String } type Human { name: String bornIn: String } type Ship { name: String length: Int } union SearchResult = Human | Droid | Starship graphene-2.1.9/examples/000077500000000000000000000000001407436344100151405ustar00rootroot00000000000000graphene-2.1.9/examples/__init__.py000066400000000000000000000000001407436344100172370ustar00rootroot00000000000000graphene-2.1.9/examples/complex_example.py000066400000000000000000000026771407436344100207100ustar00rootroot00000000000000import graphene class GeoInput(graphene.InputObjectType): lat = graphene.Float(required=True) lng = graphene.Float(required=True) @property def latlng(self): return "({},{})".format(self.lat, self.lng) class Address(graphene.ObjectType): latlng = graphene.String() class Query(graphene.ObjectType): address = graphene.Field(Address, geo=GeoInput(required=True)) def resolve_address(self, info, geo): return Address(latlng=geo.latlng) class CreateAddress(graphene.Mutation): class Arguments: geo = GeoInput(required=True) Output = Address def mutate(self, info, geo): return Address(latlng=geo.latlng) class Mutation(graphene.ObjectType): create_address = CreateAddress.Field() schema = graphene.Schema(query=Query, mutation=Mutation) query = """ query something{ address(geo: {lat:32.2, lng:12}) { latlng } } """ mutation = """ mutation addAddress{ createAddress(geo: {lat:32.2, lng:12}) { latlng } } """ def test_query(): result = schema.execute(query) assert not result.errors assert result.data == {"address": {"latlng": "(32.2,12.0)"}} def test_mutation(): result = schema.execute(mutation) assert not result.errors assert result.data == {"createAddress": {"latlng": "(32.2,12.0)"}} if __name__ == "__main__": result = schema.execute(query) print(result.data["address"]["latlng"]) graphene-2.1.9/examples/context_example.py000066400000000000000000000012721407436344100207130ustar00rootroot00000000000000import graphene class User(graphene.ObjectType): id = graphene.ID() name = graphene.String() class Query(graphene.ObjectType): me = graphene.Field(User) def resolve_me(self, info): return info.context["user"] schema = graphene.Schema(query=Query) query = """ query something{ me { id name } } """ def test_query(): result = schema.execute(query, context={"user": User(id="1", name="Syrus")}) assert not result.errors assert result.data == {"me": {"id": "1", "name": "Syrus"}} if __name__ == "__main__": result = schema.execute(query, context={"user": User(id="X", name="Console")}) print(result.data["me"]) graphene-2.1.9/examples/simple_example.py000066400000000000000000000012631407436344100205200ustar00rootroot00000000000000import graphene class Patron(graphene.ObjectType): id = graphene.ID() name = graphene.String() age = graphene.Int() class Query(graphene.ObjectType): patron = graphene.Field(Patron) def resolve_patron(self, info): return Patron(id=1, name="Syrus", age=27) schema = graphene.Schema(query=Query) query = """ query something{ patron { id name age } } """ def test_query(): result = schema.execute(query) assert not result.errors assert result.data == {"patron": {"id": "1", "name": "Syrus", "age": 27}} if __name__ == "__main__": result = schema.execute(query) print(result.data["patron"]) graphene-2.1.9/examples/starwars/000077500000000000000000000000001407436344100170065ustar00rootroot00000000000000graphene-2.1.9/examples/starwars/__init__.py000066400000000000000000000000001407436344100211050ustar00rootroot00000000000000graphene-2.1.9/examples/starwars/data.py000066400000000000000000000035311407436344100202730ustar00rootroot00000000000000human_data = {} droid_data = {} def setup(): from .schema import Human, Droid global human_data, droid_data luke = Human( id="1000", name="Luke Skywalker", friends=["1002", "1003", "2000", "2001"], appears_in=[4, 5, 6], home_planet="Tatooine", ) vader = Human( id="1001", name="Darth Vader", friends=["1004"], appears_in=[4, 5, 6], home_planet="Tatooine", ) han = Human( id="1002", name="Han Solo", friends=["1000", "1003", "2001"], appears_in=[4, 5, 6], home_planet=None, ) leia = Human( id="1003", name="Leia Organa", friends=["1000", "1002", "2000", "2001"], appears_in=[4, 5, 6], home_planet="Alderaan", ) tarkin = Human( id="1004", name="Wilhuff Tarkin", friends=["1001"], appears_in=[4], home_planet=None, ) human_data = { "1000": luke, "1001": vader, "1002": han, "1003": leia, "1004": tarkin, } c3po = Droid( id="2000", name="C-3PO", friends=["1000", "1002", "1003", "2001"], appears_in=[4, 5, 6], primary_function="Protocol", ) r2d2 = Droid( id="2001", name="R2-D2", friends=["1000", "1002", "1003"], appears_in=[4, 5, 6], primary_function="Astromech", ) droid_data = {"2000": c3po, "2001": r2d2} def get_character(id): return human_data.get(id) or droid_data.get(id) def get_friends(character): return map(get_character, character.friends) def get_hero(episode): if episode == 5: return human_data["1000"] return droid_data["2001"] def get_human(id): return human_data.get(id) def get_droid(id): return droid_data.get(id) graphene-2.1.9/examples/starwars/schema.py000066400000000000000000000022551407436344100206240ustar00rootroot00000000000000import graphene from .data import get_character, get_droid, get_hero, get_human class Episode(graphene.Enum): NEWHOPE = 4 EMPIRE = 5 JEDI = 6 class Character(graphene.Interface): id = graphene.ID() name = graphene.String() friends = graphene.List(lambda: Character) appears_in = graphene.List(Episode) def resolve_friends(self, info): # The character friends is a list of strings return [get_character(f) for f in self.friends] class Human(graphene.ObjectType): class Meta: interfaces = (Character,) home_planet = graphene.String() class Droid(graphene.ObjectType): class Meta: interfaces = (Character,) primary_function = graphene.String() class Query(graphene.ObjectType): hero = graphene.Field(Character, episode=Episode()) human = graphene.Field(Human, id=graphene.String()) droid = graphene.Field(Droid, id=graphene.String()) def resolve_hero(self, info, episode=None): return get_hero(episode) def resolve_human(self, info, id): return get_human(id) def resolve_droid(self, info, id): return get_droid(id) schema = graphene.Schema(query=Query) graphene-2.1.9/examples/starwars/tests/000077500000000000000000000000001407436344100201505ustar00rootroot00000000000000graphene-2.1.9/examples/starwars/tests/__init__.py000066400000000000000000000000001407436344100222470ustar00rootroot00000000000000graphene-2.1.9/examples/starwars/tests/snapshots/000077500000000000000000000000001407436344100221725ustar00rootroot00000000000000graphene-2.1.9/examples/starwars/tests/snapshots/__init__.py000066400000000000000000000000001407436344100242710ustar00rootroot00000000000000graphene-2.1.9/examples/starwars/tests/snapshots/snap_test_query.py000066400000000000000000000057101407436344100257740ustar00rootroot00000000000000# -*- coding: utf-8 -*- # snapshottest: v1 - https://goo.gl/zC4yUc from __future__ import unicode_literals from snapshottest import Snapshot snapshots = Snapshot() snapshots["test_hero_name_query 1"] = {"data": {"hero": {"name": "R2-D2"}}} snapshots["test_hero_name_and_friends_query 1"] = { "data": { "hero": { "id": "2001", "name": "R2-D2", "friends": [ {"name": "Luke Skywalker"}, {"name": "Han Solo"}, {"name": "Leia Organa"}, ], } } } snapshots["test_nested_query 1"] = { "data": { "hero": { "name": "R2-D2", "friends": [ { "name": "Luke Skywalker", "appearsIn": ["NEWHOPE", "EMPIRE", "JEDI"], "friends": [ {"name": "Han Solo"}, {"name": "Leia Organa"}, {"name": "C-3PO"}, {"name": "R2-D2"}, ], }, { "name": "Han Solo", "appearsIn": ["NEWHOPE", "EMPIRE", "JEDI"], "friends": [ {"name": "Luke Skywalker"}, {"name": "Leia Organa"}, {"name": "R2-D2"}, ], }, { "name": "Leia Organa", "appearsIn": ["NEWHOPE", "EMPIRE", "JEDI"], "friends": [ {"name": "Luke Skywalker"}, {"name": "Han Solo"}, {"name": "C-3PO"}, {"name": "R2-D2"}, ], }, ], } } } snapshots["test_fetch_luke_query 1"] = {"data": {"human": {"name": "Luke Skywalker"}}} snapshots["test_fetch_some_id_query 1"] = { "data": {"human": {"name": "Luke Skywalker"}} } snapshots["test_fetch_some_id_query2 1"] = {"data": {"human": {"name": "Han Solo"}}} snapshots["test_invalid_id_query 1"] = {"data": {"human": None}} snapshots["test_fetch_luke_aliased 1"] = {"data": {"luke": {"name": "Luke Skywalker"}}} snapshots["test_fetch_luke_and_leia_aliased 1"] = { "data": {"luke": {"name": "Luke Skywalker"}, "leia": {"name": "Leia Organa"}} } snapshots["test_duplicate_fields 1"] = { "data": { "luke": {"name": "Luke Skywalker", "homePlanet": "Tatooine"}, "leia": {"name": "Leia Organa", "homePlanet": "Alderaan"}, } } snapshots["test_use_fragment 1"] = { "data": { "luke": {"name": "Luke Skywalker", "homePlanet": "Tatooine"}, "leia": {"name": "Leia Organa", "homePlanet": "Alderaan"}, } } snapshots["test_check_type_of_r2 1"] = { "data": {"hero": {"__typename": "Droid", "name": "R2-D2"}} } snapshots["test_check_type_of_luke 1"] = { "data": {"hero": {"__typename": "Human", "name": "Luke Skywalker"}} } graphene-2.1.9/examples/starwars/tests/test_query.py000066400000000000000000000071711407436344100227340ustar00rootroot00000000000000from graphene.test import Client from ..data import setup from ..schema import schema setup() client = Client(schema) def test_hero_name_query(snapshot): query = """ query HeroNameQuery { hero { name } } """ snapshot.assert_match(client.execute(query)) def test_hero_name_and_friends_query(snapshot): query = """ query HeroNameAndFriendsQuery { hero { id name friends { name } } } """ snapshot.assert_match(client.execute(query)) def test_nested_query(snapshot): query = """ query NestedQuery { hero { name friends { name appearsIn friends { name } } } } """ snapshot.assert_match(client.execute(query)) def test_fetch_luke_query(snapshot): query = """ query FetchLukeQuery { human(id: "1000") { name } } """ snapshot.assert_match(client.execute(query)) def test_fetch_some_id_query(snapshot): query = """ query FetchSomeIDQuery($someId: String!) { human(id: $someId) { name } } """ params = {"someId": "1000"} snapshot.assert_match(client.execute(query, variables=params)) def test_fetch_some_id_query2(snapshot): query = """ query FetchSomeIDQuery($someId: String!) { human(id: $someId) { name } } """ params = {"someId": "1002"} snapshot.assert_match(client.execute(query, variables=params)) def test_invalid_id_query(snapshot): query = """ query humanQuery($id: String!) { human(id: $id) { name } } """ params = {"id": "not a valid id"} snapshot.assert_match(client.execute(query, variables=params)) def test_fetch_luke_aliased(snapshot): query = """ query FetchLukeAliased { luke: human(id: "1000") { name } } """ snapshot.assert_match(client.execute(query)) def test_fetch_luke_and_leia_aliased(snapshot): query = """ query FetchLukeAndLeiaAliased { luke: human(id: "1000") { name } leia: human(id: "1003") { name } } """ snapshot.assert_match(client.execute(query)) def test_duplicate_fields(snapshot): query = """ query DuplicateFields { luke: human(id: "1000") { name homePlanet } leia: human(id: "1003") { name homePlanet } } """ snapshot.assert_match(client.execute(query)) def test_use_fragment(snapshot): query = """ query UseFragment { luke: human(id: "1000") { ...HumanFragment } leia: human(id: "1003") { ...HumanFragment } } fragment HumanFragment on Human { name homePlanet } """ snapshot.assert_match(client.execute(query)) def test_check_type_of_r2(snapshot): query = """ query CheckTypeOfR2 { hero { __typename name } } """ snapshot.assert_match(client.execute(query)) def test_check_type_of_luke(snapshot): query = """ query CheckTypeOfLuke { hero(episode: EMPIRE) { __typename name } } """ snapshot.assert_match(client.execute(query)) graphene-2.1.9/examples/starwars/tests/test_schema.py000066400000000000000000000000001407436344100230070ustar00rootroot00000000000000graphene-2.1.9/examples/starwars_relay/000077500000000000000000000000001407436344100202025ustar00rootroot00000000000000graphene-2.1.9/examples/starwars_relay/__init__.py000066400000000000000000000000001407436344100223010ustar00rootroot00000000000000graphene-2.1.9/examples/starwars_relay/data.py000066400000000000000000000030701407436344100214650ustar00rootroot00000000000000data = {} def setup(): global data from .schema import Ship, Faction xwing = Ship(id="1", name="X-Wing") ywing = Ship(id="2", name="Y-Wing") awing = Ship(id="3", name="A-Wing") # Yeah, technically it's Corellian. But it flew in the service of the rebels, # so for the purposes of this demo it's a rebel ship. falcon = Ship(id="4", name="Millenium Falcon") homeOne = Ship(id="5", name="Home One") tieFighter = Ship(id="6", name="TIE Fighter") tieInterceptor = Ship(id="7", name="TIE Interceptor") executor = Ship(id="8", name="Executor") rebels = Faction( id="1", name="Alliance to Restore the Republic", ships=["1", "2", "3", "4", "5"] ) empire = Faction(id="2", name="Galactic Empire", ships=["6", "7", "8"]) data = { "Faction": {"1": rebels, "2": empire}, "Ship": { "1": xwing, "2": ywing, "3": awing, "4": falcon, "5": homeOne, "6": tieFighter, "7": tieInterceptor, "8": executor, }, } def create_ship(ship_name, faction_id): from .schema import Ship next_ship = len(data["Ship"].keys()) + 1 new_ship = Ship(id=str(next_ship), name=ship_name) data["Ship"][new_ship.id] = new_ship data["Faction"][faction_id].ships.append(new_ship.id) return new_ship def get_ship(_id): return data["Ship"][_id] def get_faction(_id): return data["Faction"][_id] def get_rebels(): return get_faction("1") def get_empire(): return get_faction("2") graphene-2.1.9/examples/starwars_relay/schema.py000066400000000000000000000036431407436344100220220ustar00rootroot00000000000000import graphene from graphene import relay from .data import create_ship, get_empire, get_faction, get_rebels, get_ship class Ship(graphene.ObjectType): """A ship in the Star Wars saga""" class Meta: interfaces = (relay.Node,) name = graphene.String(description="The name of the ship.") @classmethod def get_node(cls, info, id): return get_ship(id) class ShipConnection(relay.Connection): class Meta: node = Ship class Faction(graphene.ObjectType): """A faction in the Star Wars saga""" class Meta: interfaces = (relay.Node,) name = graphene.String(description="The name of the faction.") ships = relay.ConnectionField( ShipConnection, description="The ships used by the faction." ) def resolve_ships(self, info, **args): # Transform the instance ship_ids into real instances return [get_ship(ship_id) for ship_id in self.ships] @classmethod def get_node(cls, info, id): return get_faction(id) class IntroduceShip(relay.ClientIDMutation): class Input: ship_name = graphene.String(required=True) faction_id = graphene.String(required=True) ship = graphene.Field(Ship) faction = graphene.Field(Faction) @classmethod def mutate_and_get_payload( cls, root, info, ship_name, faction_id, client_mutation_id=None ): ship = create_ship(ship_name, faction_id) faction = get_faction(faction_id) return IntroduceShip(ship=ship, faction=faction) class Query(graphene.ObjectType): rebels = graphene.Field(Faction) empire = graphene.Field(Faction) node = relay.Node.Field() def resolve_rebels(self, info): return get_rebels() def resolve_empire(self, info): return get_empire() class Mutation(graphene.ObjectType): introduce_ship = IntroduceShip.Field() schema = graphene.Schema(query=Query, mutation=Mutation) graphene-2.1.9/examples/starwars_relay/tests/000077500000000000000000000000001407436344100213445ustar00rootroot00000000000000graphene-2.1.9/examples/starwars_relay/tests/__init__.py000066400000000000000000000000001407436344100234430ustar00rootroot00000000000000graphene-2.1.9/examples/starwars_relay/tests/snapshots/000077500000000000000000000000001407436344100233665ustar00rootroot00000000000000graphene-2.1.9/examples/starwars_relay/tests/snapshots/__init__.py000066400000000000000000000000001407436344100254650ustar00rootroot00000000000000graphene-2.1.9/examples/starwars_relay/tests/snapshots/snap_test_connections.py000066400000000000000000000013721407436344100303450ustar00rootroot00000000000000# -*- coding: utf-8 -*- # snapshottest: v1 - https://goo.gl/zC4yUc from __future__ import unicode_literals from snapshottest import Snapshot snapshots = Snapshot() snapshots["test_correct_fetch_first_ship_rebels 1"] = { "data": { "rebels": { "name": "Alliance to Restore the Republic", "ships": { "pageInfo": { "startCursor": "YXJyYXljb25uZWN0aW9uOjA=", "endCursor": "YXJyYXljb25uZWN0aW9uOjA=", "hasNextPage": True, "hasPreviousPage": False, }, "edges": [ {"cursor": "YXJyYXljb25uZWN0aW9uOjA=", "node": {"name": "X-Wing"}} ], }, } } } graphene-2.1.9/examples/starwars_relay/tests/snapshots/snap_test_mutation.py000066400000000000000000000016771407436344100276730ustar00rootroot00000000000000# -*- coding: utf-8 -*- # snapshottest: v1 - https://goo.gl/zC4yUc from __future__ import unicode_literals from snapshottest import Snapshot snapshots = Snapshot() snapshots["test_mutations 1"] = { "data": { "introduceShip": { "ship": {"id": "U2hpcDo5", "name": "Peter"}, "faction": { "name": "Alliance to Restore the Republic", "ships": { "edges": [ {"node": {"id": "U2hpcDox", "name": "X-Wing"}}, {"node": {"id": "U2hpcDoy", "name": "Y-Wing"}}, {"node": {"id": "U2hpcDoz", "name": "A-Wing"}}, {"node": {"id": "U2hpcDo0", "name": "Millenium Falcon"}}, {"node": {"id": "U2hpcDo1", "name": "Home One"}}, {"node": {"id": "U2hpcDo5", "name": "Peter"}}, ] }, }, } } } graphene-2.1.9/examples/starwars_relay/tests/snapshots/snap_test_objectidentification.py000066400000000000000000000033151407436344100322020ustar00rootroot00000000000000# -*- coding: utf-8 -*- # snapshottest: v1 - https://goo.gl/zC4yUc from __future__ import unicode_literals from snapshottest import Snapshot snapshots = Snapshot() snapshots["test_correctly_fetches_id_name_rebels 1"] = { "data": { "rebels": {"id": "RmFjdGlvbjox", "name": "Alliance to Restore the Republic"} } } snapshots["test_correctly_refetches_rebels 1"] = { "data": {"node": {"id": "RmFjdGlvbjox", "name": "Alliance to Restore the Republic"}} } snapshots["test_correctly_fetches_id_name_empire 1"] = { "data": {"empire": {"id": "RmFjdGlvbjoy", "name": "Galactic Empire"}} } snapshots["test_correctly_refetches_empire 1"] = { "data": {"node": {"id": "RmFjdGlvbjoy", "name": "Galactic Empire"}} } snapshots["test_correctly_refetches_xwing 1"] = { "data": {"node": {"id": "U2hpcDox", "name": "X-Wing"}} } snapshots[ "test_str_schema 1" ] = """schema { query: Query mutation: Mutation } type Faction implements Node { id: ID! name: String ships(before: String, after: String, first: Int, last: Int): ShipConnection } input IntroduceShipInput { shipName: String! factionId: String! clientMutationId: String } type IntroduceShipPayload { ship: Ship faction: Faction clientMutationId: String } type Mutation { introduceShip(input: IntroduceShipInput!): IntroduceShipPayload } interface Node { id: ID! } type PageInfo { hasNextPage: Boolean! hasPreviousPage: Boolean! startCursor: String endCursor: String } type Query { rebels: Faction empire: Faction node(id: ID!): Node } type Ship implements Node { id: ID! name: String } type ShipConnection { pageInfo: PageInfo! edges: [ShipEdge]! } type ShipEdge { node: Ship cursor: String! } """ graphene-2.1.9/examples/starwars_relay/tests/test_connections.py000066400000000000000000000011141407436344100252740ustar00rootroot00000000000000from graphene.test import Client from ..data import setup from ..schema import schema setup() client = Client(schema) def test_correct_fetch_first_ship_rebels(snapshot): query = """ query RebelsShipsQuery { rebels { name, ships(first: 1) { pageInfo { startCursor endCursor hasNextPage hasPreviousPage } edges { cursor node { name } } } } } """ snapshot.assert_match(client.execute(query)) graphene-2.1.9/examples/starwars_relay/tests/test_mutation.py000066400000000000000000000011201407436344100246070ustar00rootroot00000000000000from graphene.test import Client from ..data import setup from ..schema import schema setup() client = Client(schema) def test_mutations(snapshot): query = """ mutation MyMutation { introduceShip(input:{clientMutationId:"abc", shipName: "Peter", factionId: "1"}) { ship { id name } faction { name ships { edges { node { id name } } } } } } """ snapshot.assert_match(client.execute(query)) graphene-2.1.9/examples/starwars_relay/tests/test_objectidentification.py000066400000000000000000000026511407436344100271410ustar00rootroot00000000000000from graphene.test import Client from ..data import setup from ..schema import schema setup() client = Client(schema) def test_str_schema(snapshot): snapshot.assert_match(str(schema)) def test_correctly_fetches_id_name_rebels(snapshot): query = """ query RebelsQuery { rebels { id name } } """ snapshot.assert_match(client.execute(query)) def test_correctly_refetches_rebels(snapshot): query = """ query RebelsRefetchQuery { node(id: "RmFjdGlvbjox") { id ... on Faction { name } } } """ snapshot.assert_match(client.execute(query)) def test_correctly_fetches_id_name_empire(snapshot): query = """ query EmpireQuery { empire { id name } } """ snapshot.assert_match(client.execute(query)) def test_correctly_refetches_empire(snapshot): query = """ query EmpireRefetchQuery { node(id: "RmFjdGlvbjoy") { id ... on Faction { name } } } """ snapshot.assert_match(client.execute(query)) def test_correctly_refetches_xwing(snapshot): query = """ query XWingRefetchQuery { node(id: "U2hpcDox") { id ... on Ship { name } } } """ snapshot.assert_match(client.execute(query)) graphene-2.1.9/graphene/000077500000000000000000000000001407436344100151135ustar00rootroot00000000000000graphene-2.1.9/graphene/__init__.py000066400000000000000000000025301407436344100172240ustar00rootroot00000000000000from .pyutils.version import get_version from .types import ( AbstractType, ObjectType, InputObjectType, Interface, Mutation, Field, InputField, Schema, Scalar, String, ID, Int, Float, Boolean, Date, DateTime, Time, Decimal, JSONString, UUID, List, NonNull, Enum, Argument, Dynamic, Union, Context, ResolveInfo, ) from .relay import ( Node, is_node, GlobalID, ClientIDMutation, Connection, ConnectionField, PageInfo, ) from .utils.resolve_only_args import resolve_only_args from .utils.module_loading import lazy_import VERSION = (2, 1, 9, "final", 0) __version__ = get_version(VERSION) __all__ = [ "__version__", "ObjectType", "InputObjectType", "Interface", "Mutation", "Field", "InputField", "Schema", "Scalar", "String", "ID", "Int", "Float", "Enum", "Boolean", "Date", "DateTime", "Time", "Decimal", "JSONString", "UUID", "List", "NonNull", "Argument", "Dynamic", "Union", "resolve_only_args", "Node", "is_node", "GlobalID", "ClientIDMutation", "Connection", "ConnectionField", "PageInfo", "lazy_import", "Context", "ResolveInfo", # Deprecated "AbstractType", ] graphene-2.1.9/graphene/pyutils/000077500000000000000000000000001407436344100166245ustar00rootroot00000000000000graphene-2.1.9/graphene/pyutils/__init__.py000066400000000000000000000000001407436344100207230ustar00rootroot00000000000000graphene-2.1.9/graphene/pyutils/compat.py000066400000000000000000000005011407436344100204550ustar00rootroot00000000000000from __future__ import absolute_import import six from graphql.pyutils.compat import Enum try: from inspect import signature except ImportError: from .signature import signature if six.PY2: def func_name(func): return func.func_name else: def func_name(func): return func.__name__ graphene-2.1.9/graphene/pyutils/init_subclass.py000066400000000000000000000016131407436344100220410ustar00rootroot00000000000000is_init_subclass_available = hasattr(object, "__init_subclass__") if not is_init_subclass_available: class InitSubclassMeta(type): """Metaclass that implements PEP 487 protocol""" def __new__(cls, name, bases, ns, **kwargs): __init_subclass__ = ns.pop("__init_subclass__", None) if __init_subclass__: __init_subclass__ = classmethod(__init_subclass__) ns["__init_subclass__"] = __init_subclass__ return super(InitSubclassMeta, cls).__new__(cls, name, bases, ns, **kwargs) def __init__(cls, name, bases, ns, **kwargs): super(InitSubclassMeta, cls).__init__(name, bases, ns) super_class = super(cls, cls) if hasattr(super_class, "__init_subclass__"): super_class.__init_subclass__.__func__(cls, **kwargs) else: InitSubclassMeta = type # type: ignore graphene-2.1.9/graphene/pyutils/signature.py000066400000000000000000000717541407436344100212150ustar00rootroot00000000000000# Copyright 2001-2013 Python Software Foundation; All Rights Reserved """Function signature objects for callables Back port of Python 3.3's function signature tools from the inspect module, modified to be compatible with Python 2.7 and 3.2+. """ from __future__ import absolute_import, division, print_function import functools import itertools import re import types from collections import OrderedDict __version__ = "0.4" __all__ = ["BoundArguments", "Parameter", "Signature", "signature"] _WrapperDescriptor = type(type.__call__) _MethodWrapper = type(all.__call__) _NonUserDefinedCallables = ( _WrapperDescriptor, _MethodWrapper, types.BuiltinFunctionType, ) def formatannotation(annotation, base_module=None): if isinstance(annotation, type): if annotation.__module__ in ("builtins", "__builtin__", base_module): return annotation.__name__ return annotation.__module__ + "." + annotation.__name__ return repr(annotation) def _get_user_defined_method(cls, method_name, *nested): try: if cls is type: return meth = getattr(cls, method_name) for name in nested: meth = getattr(meth, name, meth) except AttributeError: return else: if not isinstance(meth, _NonUserDefinedCallables): # Once '__signature__' will be added to 'C'-level # callables, this check won't be necessary return meth def signature(obj): """Get a signature object for the passed callable.""" if not callable(obj): raise TypeError("{!r} is not a callable object".format(obj)) if isinstance(obj, types.MethodType): sig = signature(obj.__func__) if obj.__self__ is None: # Unbound method: the first parameter becomes positional-only if sig.parameters: first = sig.parameters.values()[0].replace(kind=_POSITIONAL_ONLY) return sig.replace( parameters=(first,) + tuple(sig.parameters.values())[1:] ) else: return sig else: # In this case we skip the first parameter of the underlying # function (usually `self` or `cls`). return sig.replace(parameters=tuple(sig.parameters.values())[1:]) try: sig = obj.__signature__ except AttributeError: pass else: if sig is not None: return sig try: # Was this function wrapped by a decorator? wrapped = obj.__wrapped__ except AttributeError: pass else: return signature(wrapped) if isinstance(obj, types.FunctionType): return Signature.from_function(obj) if isinstance(obj, functools.partial): sig = signature(obj.func) new_params = OrderedDict(sig.parameters.items()) partial_args = obj.args or () partial_keywords = obj.keywords or {} try: ba = sig.bind_partial(*partial_args, **partial_keywords) except TypeError as ex: msg = "partial object {!r} has incorrect arguments".format(obj) raise ValueError(msg) for arg_name, arg_value in ba.arguments.items(): param = new_params[arg_name] if arg_name in partial_keywords: # We set a new default value, because the following code # is correct: # # >>> def foo(a): print(a) # >>> print(partial(partial(foo, a=10), a=20)()) # 20 # >>> print(partial(partial(foo, a=10), a=20)(a=30)) # 30 # # So, with 'partial' objects, passing a keyword argument is # like setting a new default value for the corresponding # parameter # # We also mark this parameter with '_partial_kwarg' # flag. Later, in '_bind', the 'default' value of this # parameter will be added to 'kwargs', to simulate # the 'functools.partial' real call. new_params[arg_name] = param.replace( default=arg_value, _partial_kwarg=True ) elif ( param.kind not in (_VAR_KEYWORD, _VAR_POSITIONAL) and not param._partial_kwarg ): new_params.pop(arg_name) return sig.replace(parameters=new_params.values()) sig = None if isinstance(obj, type): # obj is a class or a metaclass # First, let's see if it has an overloaded __call__ defined # in its metaclass call = _get_user_defined_method(type(obj), "__call__") if call is not None: sig = signature(call) else: # Now we check if the 'obj' class has a '__new__' method new = _get_user_defined_method(obj, "__new__") if new is not None: sig = signature(new) else: # Finally, we should have at least __init__ implemented init = _get_user_defined_method(obj, "__init__") if init is not None: sig = signature(init) elif not isinstance(obj, _NonUserDefinedCallables): # An object with __call__ # We also check that the 'obj' is not an instance of # _WrapperDescriptor or _MethodWrapper to avoid # infinite recursion (and even potential segfault) call = _get_user_defined_method(type(obj), "__call__", "im_func") if call is not None: sig = signature(call) if sig is not None: # For classes and objects we skip the first parameter of their # __call__, __new__, or __init__ methods return sig.replace(parameters=tuple(sig.parameters.values())[1:]) if isinstance(obj, types.BuiltinFunctionType): # Raise a nicer error message for builtins msg = "no signature found for builtin function {!r}".format(obj) raise ValueError(msg) raise ValueError("callable {!r} is not supported by signature".format(obj)) class _void(object): """A private marker - used in Parameter & Signature""" class _empty(object): pass class _ParameterKind(int): def __new__(self, *args, **kwargs): obj = int.__new__(self, *args) obj._name = kwargs["name"] return obj def __str__(self): return self._name def __repr__(self): return "<_ParameterKind: {!r}>".format(self._name) _POSITIONAL_ONLY = _ParameterKind(0, name="POSITIONAL_ONLY") _POSITIONAL_OR_KEYWORD = _ParameterKind(1, name="POSITIONAL_OR_KEYWORD") _VAR_POSITIONAL = _ParameterKind(2, name="VAR_POSITIONAL") _KEYWORD_ONLY = _ParameterKind(3, name="KEYWORD_ONLY") _VAR_KEYWORD = _ParameterKind(4, name="VAR_KEYWORD") class Parameter(object): """Represents a parameter in a function signature. Has the following public attributes: * name : str The name of the parameter as a string. * default : object The default value for the parameter if specified. If the parameter has no default value, this attribute is not set. * annotation The annotation for the parameter if specified. If the parameter has no annotation, this attribute is not set. * kind : str Describes how argument values are bound to the parameter. Possible values: `Parameter.POSITIONAL_ONLY`, `Parameter.POSITIONAL_OR_KEYWORD`, `Parameter.VAR_POSITIONAL`, `Parameter.KEYWORD_ONLY`, `Parameter.VAR_KEYWORD`. """ __slots__ = ("_name", "_kind", "_default", "_annotation", "_partial_kwarg") POSITIONAL_ONLY = _POSITIONAL_ONLY POSITIONAL_OR_KEYWORD = _POSITIONAL_OR_KEYWORD VAR_POSITIONAL = _VAR_POSITIONAL KEYWORD_ONLY = _KEYWORD_ONLY VAR_KEYWORD = _VAR_KEYWORD empty = _empty def __init__( self, name, kind, default=_empty, annotation=_empty, _partial_kwarg=False ): if kind not in ( _POSITIONAL_ONLY, _POSITIONAL_OR_KEYWORD, _VAR_POSITIONAL, _KEYWORD_ONLY, _VAR_KEYWORD, ): raise ValueError("invalid value for 'Parameter.kind' attribute") self._kind = kind if default is not _empty: if kind in (_VAR_POSITIONAL, _VAR_KEYWORD): msg = "{} parameters cannot have default values".format(kind) raise ValueError(msg) self._default = default self._annotation = annotation if name is None: if kind != _POSITIONAL_ONLY: raise ValueError( "None is not a valid name for a " "non-positional-only parameter" ) self._name = name else: name = str(name) if kind != _POSITIONAL_ONLY and not re.match(r"[a-z_]\w*$", name, re.I): msg = "{!r} is not a valid parameter name".format(name) raise ValueError(msg) self._name = name self._partial_kwarg = _partial_kwarg @property def name(self): return self._name @property def default(self): return self._default @property def annotation(self): return self._annotation @property def kind(self): return self._kind def replace( self, name=_void, kind=_void, annotation=_void, default=_void, _partial_kwarg=_void, ): """Creates a customized copy of the Parameter.""" if name is _void: name = self._name if kind is _void: kind = self._kind if annotation is _void: annotation = self._annotation if default is _void: default = self._default if _partial_kwarg is _void: _partial_kwarg = self._partial_kwarg return type(self)( name, kind, default=default, annotation=annotation, _partial_kwarg=_partial_kwarg, ) def __str__(self): kind = self.kind formatted = self._name if kind == _POSITIONAL_ONLY: if formatted is None: formatted = "" formatted = "<{}>".format(formatted) # Add annotation and default value if self._annotation is not _empty: formatted = "{}:{}".format(formatted, formatannotation(self._annotation)) if self._default is not _empty: formatted = "{}={}".format(formatted, repr(self._default)) if kind == _VAR_POSITIONAL: formatted = "*" + formatted elif kind == _VAR_KEYWORD: formatted = "**" + formatted return formatted def __repr__(self): return "<{} at {:#x} {!r}>".format(self.__class__.__name__, id(self), self.name) def __hash__(self): msg = "unhashable type: '{}'".format(self.__class__.__name__) raise TypeError(msg) def __eq__(self, other): return ( issubclass(other.__class__, Parameter) and self._name == other._name and self._kind == other._kind and self._default == other._default and self._annotation == other._annotation ) def __ne__(self, other): return not self.__eq__(other) class BoundArguments(object): """Result of `Signature.bind` call. Holds the mapping of arguments to the function's parameters. Has the following public attributes: * arguments : OrderedDict An ordered mutable mapping of parameters' names to arguments' values. Does not contain arguments' default values. * signature : Signature The Signature object that created this instance. * args : tuple Tuple of positional arguments values. * kwargs : dict Dict of keyword arguments values. """ def __init__(self, signature, arguments): self.arguments = arguments self._signature = signature @property def signature(self): return self._signature @property def args(self): args = [] for param_name, param in self._signature.parameters.items(): if param.kind in (_VAR_KEYWORD, _KEYWORD_ONLY) or param._partial_kwarg: # Keyword arguments mapped by 'functools.partial' # (Parameter._partial_kwarg is True) are mapped # in 'BoundArguments.kwargs', along with VAR_KEYWORD & # KEYWORD_ONLY break try: arg = self.arguments[param_name] except KeyError: # We're done here. Other arguments # will be mapped in 'BoundArguments.kwargs' break else: if param.kind == _VAR_POSITIONAL: # *args args.extend(arg) else: # plain argument args.append(arg) return tuple(args) @property def kwargs(self): kwargs = {} kwargs_started = False for param_name, param in self._signature.parameters.items(): if not kwargs_started: if param.kind in (_VAR_KEYWORD, _KEYWORD_ONLY) or param._partial_kwarg: kwargs_started = True else: if param_name not in self.arguments: kwargs_started = True continue if not kwargs_started: continue try: arg = self.arguments[param_name] except KeyError: pass else: if param.kind == _VAR_KEYWORD: # **kwargs kwargs.update(arg) else: # plain keyword argument kwargs[param_name] = arg return kwargs def __hash__(self): msg = "unhashable type: '{}'".format(self.__class__.__name__) raise TypeError(msg) def __eq__(self, other): return ( issubclass(other.__class__, BoundArguments) and self.signature == other.signature and self.arguments == other.arguments ) def __ne__(self, other): return not self.__eq__(other) class Signature(object): """A Signature object represents the overall signature of a function. It stores a Parameter object for each parameter accepted by the function, as well as information specific to the function itself. A Signature object has the following public attributes and methods: * parameters : OrderedDict An ordered mapping of parameters' names to the corresponding Parameter objects (keyword-only arguments are in the same order as listed in `code.co_varnames`). * return_annotation : object The annotation for the return type of the function if specified. If the function has no annotation for its return type, this attribute is not set. * bind(*args, **kwargs) -> BoundArguments Creates a mapping from positional and keyword arguments to parameters. * bind_partial(*args, **kwargs) -> BoundArguments Creates a partial mapping from positional and keyword arguments to parameters (simulating 'functools.partial' behavior.) """ __slots__ = ("_return_annotation", "_parameters") _parameter_cls = Parameter _bound_arguments_cls = BoundArguments empty = _empty def __init__( self, parameters=None, return_annotation=_empty, __validate_parameters__=True ): """Constructs Signature from the given list of Parameter objects and 'return_annotation'. All arguments are optional. """ if parameters is None: params = OrderedDict() else: if __validate_parameters__: params = OrderedDict() top_kind = _POSITIONAL_ONLY for idx, param in enumerate(parameters): kind = param.kind if kind < top_kind: msg = "wrong parameter order: {0} before {1}" msg = msg.format(top_kind, param.kind) raise ValueError(msg) else: top_kind = kind name = param.name if name is None: name = str(idx) param = param.replace(name=name) if name in params: msg = "duplicate parameter name: {!r}".format(name) raise ValueError(msg) params[name] = param else: params = OrderedDict(((param.name, param) for param in parameters)) self._parameters = params self._return_annotation = return_annotation @classmethod def from_function(cls, func): """Constructs Signature for the given python function""" if not isinstance(func, types.FunctionType): raise TypeError("{!r} is not a Python function".format(func)) Parameter = cls._parameter_cls # Parameter information. func_code = func.__code__ pos_count = func_code.co_argcount arg_names = func_code.co_varnames positional = tuple(arg_names[:pos_count]) keyword_only_count = getattr(func_code, "co_kwonlyargcount", 0) keyword_only = arg_names[pos_count : (pos_count + keyword_only_count)] annotations = getattr(func, "__annotations__", {}) defaults = func.__defaults__ kwdefaults = getattr(func, "__kwdefaults__", None) if defaults: pos_default_count = len(defaults) else: pos_default_count = 0 parameters = [] # Non-keyword-only parameters w/o defaults. non_default_count = pos_count - pos_default_count for name in positional[:non_default_count]: annotation = annotations.get(name, _empty) parameters.append( Parameter(name, annotation=annotation, kind=_POSITIONAL_OR_KEYWORD) ) # ... w/ defaults. for offset, name in enumerate(positional[non_default_count:]): annotation = annotations.get(name, _empty) parameters.append( Parameter( name, annotation=annotation, kind=_POSITIONAL_OR_KEYWORD, default=defaults[offset], ) ) # *args if func_code.co_flags & 0x04: name = arg_names[pos_count + keyword_only_count] annotation = annotations.get(name, _empty) parameters.append( Parameter(name, annotation=annotation, kind=_VAR_POSITIONAL) ) # Keyword-only parameters. for name in keyword_only: default = _empty if kwdefaults is not None: default = kwdefaults.get(name, _empty) annotation = annotations.get(name, _empty) parameters.append( Parameter( name, annotation=annotation, kind=_KEYWORD_ONLY, default=default ) ) # **kwargs if func_code.co_flags & 0x08: index = pos_count + keyword_only_count if func_code.co_flags & 0x04: index += 1 name = arg_names[index] annotation = annotations.get(name, _empty) parameters.append(Parameter(name, annotation=annotation, kind=_VAR_KEYWORD)) return cls( parameters, return_annotation=annotations.get("return", _empty), __validate_parameters__=False, ) @property def parameters(self): try: return types.MappingProxyType(self._parameters) except AttributeError: return OrderedDict(self._parameters.items()) @property def return_annotation(self): return self._return_annotation def replace(self, parameters=_void, return_annotation=_void): """Creates a customized copy of the Signature. Pass 'parameters' and/or 'return_annotation' arguments to override them in the new copy. """ if parameters is _void: parameters = self.parameters.values() if return_annotation is _void: return_annotation = self._return_annotation return type(self)(parameters, return_annotation=return_annotation) def __hash__(self): msg = "unhashable type: '{}'".format(self.__class__.__name__) raise TypeError(msg) def __eq__(self, other): if ( not issubclass(type(other), Signature) or self.return_annotation != other.return_annotation or len(self.parameters) != len(other.parameters) ): return False other_positions = { param: idx for idx, param in enumerate(other.parameters.keys()) } for idx, (param_name, param) in enumerate(self.parameters.items()): if param.kind == _KEYWORD_ONLY: try: other_param = other.parameters[param_name] except KeyError: return False else: if param != other_param: return False else: try: other_idx = other_positions[param_name] except KeyError: return False else: if idx != other_idx or param != other.parameters[param_name]: return False return True def __ne__(self, other): return not self.__eq__(other) def _bind(self, args, kwargs, partial=False): """Private method. Don't use directly.""" arguments = OrderedDict() parameters = iter(self.parameters.values()) parameters_ex = () arg_vals = iter(args) if partial: # Support for binding arguments to 'functools.partial' objects. # See 'functools.partial' case in 'signature()' implementation # for details. for param_name, param in self.parameters.items(): if param._partial_kwarg and param_name not in kwargs: # Simulating 'functools.partial' behavior kwargs[param_name] = param.default while True: # Let's iterate through the positional arguments and corresponding # parameters try: arg_val = next(arg_vals) except StopIteration: # No more positional arguments try: param = next(parameters) except StopIteration: # No more parameters. That's it. Just need to check that # we have no `kwargs` after this while loop break else: if param.kind == _VAR_POSITIONAL: # That's OK, just empty *args. Let's start parsing # kwargs break elif param.name in kwargs: if param.kind == _POSITIONAL_ONLY: msg = ( "{arg!r} parameter is positional only, " "but was passed as a keyword" ) msg = msg.format(arg=param.name) raise TypeError(msg) parameters_ex = (param,) break elif param.kind == _VAR_KEYWORD or param.default is not _empty: # That's fine too - we have a default value for this # parameter. So, lets start parsing `kwargs`, starting # with the current parameter parameters_ex = (param,) break else: if partial: parameters_ex = (param,) break else: msg = "{arg!r} parameter lacking default value" msg = msg.format(arg=param.name) raise TypeError(msg) else: # We have a positional argument to process try: param = next(parameters) except StopIteration: raise TypeError("too many positional arguments") else: if param.kind in (_VAR_KEYWORD, _KEYWORD_ONLY): # Looks like we have no parameter for this positional # argument raise TypeError("too many positional arguments") if param.kind == _VAR_POSITIONAL: # We have an '*args'-like argument, let's fill it with # all positional arguments we have left and move on to # the next phase values = [arg_val] values.extend(arg_vals) arguments[param.name] = tuple(values) break if param.name in kwargs: raise TypeError( "multiple values for argument " "{arg!r}".format(arg=param.name) ) arguments[param.name] = arg_val # Now, we iterate through the remaining parameters to process # keyword arguments kwargs_param = None for param in itertools.chain(parameters_ex, parameters): if param.kind == _POSITIONAL_ONLY: # This should never happen in case of a properly built # Signature object (but let's have this check here # to ensure correct behaviour just in case) raise TypeError( "{arg!r} parameter is positional only, " "but was passed as a keyword".format(arg=param.name) ) if param.kind == _VAR_KEYWORD: # Memorize that we have a '**kwargs'-like parameter kwargs_param = param continue param_name = param.name try: arg_val = kwargs.pop(param_name) except KeyError: # We have no value for this parameter. It's fine though, # if it has a default value, or it is an '*args'-like # parameter, left alone by the processing of positional # arguments. if ( not partial and param.kind != _VAR_POSITIONAL and param.default is _empty ): raise TypeError( "{arg!r} parameter lacking default value".format(arg=param_name) ) else: arguments[param_name] = arg_val if kwargs: if kwargs_param is not None: # Process our '**kwargs'-like parameter arguments[kwargs_param.name] = kwargs else: raise TypeError("too many keyword arguments") return self._bound_arguments_cls(self, arguments) def bind(self, *args, **kwargs): """Get a BoundArguments object, that maps the passed `args` and `kwargs` to the function's signature. Raises `TypeError` if the passed arguments can not be bound. """ return self._bind(args, kwargs) def bind_partial(self, *args, **kwargs): """Get a BoundArguments object, that partially maps the passed `args` and `kwargs` to the function's signature. Raises `TypeError` if the passed arguments can not be bound. """ return self._bind(args, kwargs, partial=True) def __str__(self): result = [] render_kw_only_separator = True for idx, param in enumerate(self.parameters.values()): formatted = str(param) kind = param.kind if kind == _VAR_POSITIONAL: # OK, we have an '*args'-like parameter, so we won't need # a '*' to separate keyword-only arguments render_kw_only_separator = False elif kind == _KEYWORD_ONLY and render_kw_only_separator: # We have a keyword-only parameter to render and we haven't # rendered an '*args'-like parameter before, so add a '*' # separator to the parameters list ("foo(arg1, *, arg2)" case) result.append("*") # This condition should be only triggered once, so # reset the flag render_kw_only_separator = False result.append(formatted) rendered = "({})".format(", ".join(result)) if self.return_annotation is not _empty: anno = formatannotation(self.return_annotation) rendered += " -> {}".format(anno) return rendered graphene-2.1.9/graphene/pyutils/version.py000066400000000000000000000046661407436344100206770ustar00rootroot00000000000000from __future__ import unicode_literals import datetime import os import subprocess def get_version(version=None): "Returns a PEP 440-compliant version number from VERSION." version = get_complete_version(version) # Now build the two parts of the version number: # main = X.Y[.Z] # sub = .devN - for pre-alpha releases # | {a|b|rc}N - for alpha, beta, and rc releases main = get_main_version(version) sub = "" if version[3] == "alpha" and version[4] == 0: git_changeset = get_git_changeset() if git_changeset: sub = ".dev%s" % git_changeset else: sub = ".dev" elif version[3] != "final": mapping = {"alpha": "a", "beta": "b", "rc": "rc"} sub = mapping[version[3]] + str(version[4]) return str(main + sub) def get_main_version(version=None): "Returns main version (X.Y[.Z]) from VERSION." version = get_complete_version(version) parts = 2 if version[2] == 0 else 3 return ".".join(str(x) for x in version[:parts]) def get_complete_version(version=None): """Returns a tuple of the graphene version. If version argument is non-empty, then checks for correctness of the tuple provided. """ if version is None: from graphene import VERSION as version else: assert len(version) == 5 assert version[3] in ("alpha", "beta", "rc", "final") return version def get_docs_version(version=None): version = get_complete_version(version) if version[3] != "final": return "dev" else: return "%d.%d" % version[:2] def get_git_changeset(): """Returns a numeric identifier of the latest git changeset. The result is the UTC timestamp of the changeset in YYYYMMDDHHMMSS format. This value isn't guaranteed to be unique, but collisions are very unlikely, so it's sufficient for generating the development version numbers. """ repo_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) try: git_log = subprocess.Popen( "git log --pretty=format:%ct --quiet -1 HEAD", stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, cwd=repo_dir, universal_newlines=True, ) timestamp = git_log.communicate()[0] timestamp = datetime.datetime.utcfromtimestamp(int(timestamp)) except: return None return timestamp.strftime("%Y%m%d%H%M%S") graphene-2.1.9/graphene/relay/000077500000000000000000000000001407436344100162275ustar00rootroot00000000000000graphene-2.1.9/graphene/relay/__init__.py000066400000000000000000000004321407436344100203370ustar00rootroot00000000000000from .node import Node, is_node, GlobalID from .mutation import ClientIDMutation from .connection import Connection, ConnectionField, PageInfo __all__ = [ "Node", "is_node", "GlobalID", "ClientIDMutation", "Connection", "ConnectionField", "PageInfo", ] graphene-2.1.9/graphene/relay/connection.py000066400000000000000000000133231407436344100207420ustar00rootroot00000000000000import re from collections import OrderedDict try: from collections.abc import Iterable except ImportError: from collections import Iterable from functools import partial from graphql_relay import connection_from_list from ..types import Boolean, Enum, Int, Interface, List, NonNull, Scalar, String, Union from ..types.field import Field from ..types.objecttype import ObjectType, ObjectTypeOptions from ..utils.thenables import maybe_thenable from .node import is_node class PageInfo(ObjectType): class Meta: description = ( "The Relay compliant `PageInfo` type, containing data necessary to" " paginate this connection." ) has_next_page = Boolean( required=True, name="hasNextPage", description="When paginating forwards, are there more items?", ) has_previous_page = Boolean( required=True, name="hasPreviousPage", description="When paginating backwards, are there more items?", ) start_cursor = String( name="startCursor", description="When paginating backwards, the cursor to continue.", ) end_cursor = String( name="endCursor", description="When paginating forwards, the cursor to continue.", ) class ConnectionOptions(ObjectTypeOptions): node = None class Connection(ObjectType): class Meta: abstract = True @classmethod def __init_subclass_with_meta__(cls, node=None, name=None, **options): _meta = ConnectionOptions(cls) assert node, "You have to provide a node in {}.Meta".format(cls.__name__) assert isinstance(node, NonNull) or issubclass( node, (Scalar, Enum, ObjectType, Interface, Union, NonNull) ), ('Received incompatible node "{}" for Connection {}.').format( node, cls.__name__ ) base_name = re.sub("Connection$", "", name or cls.__name__) or node._meta.name if not name: name = "{}Connection".format(base_name) edge_class = getattr(cls, "Edge", None) _node = node class EdgeBase(object): node = Field(_node, description="The item at the end of the edge") cursor = String(required=True, description="A cursor for use in pagination") class EdgeMeta: description = "A Relay edge containing a `{}` and its cursor.".format( base_name ) edge_name = "{}Edge".format(base_name) if edge_class: edge_bases = (edge_class, EdgeBase, ObjectType) else: edge_bases = (EdgeBase, ObjectType) edge = type(edge_name, edge_bases, {"Meta": EdgeMeta}) cls.Edge = edge options["name"] = name _meta.node = node _meta.fields = OrderedDict( [ ( "page_info", Field( PageInfo, name="pageInfo", required=True, description="Pagination data for this connection.", ), ), ( "edges", Field( NonNull(List(edge)), description="Contains the nodes in this connection.", ), ), ] ) return super(Connection, cls).__init_subclass_with_meta__( _meta=_meta, **options ) class IterableConnectionField(Field): def __init__(self, type, *args, **kwargs): kwargs.setdefault("before", String()) kwargs.setdefault("after", String()) kwargs.setdefault("first", Int()) kwargs.setdefault("last", Int()) super(IterableConnectionField, self).__init__(type, *args, **kwargs) @property def type(self): type = super(IterableConnectionField, self).type connection_type = type if isinstance(type, NonNull): connection_type = type.of_type if is_node(connection_type): raise Exception( "ConnectionFields now need a explicit ConnectionType for Nodes.\n" "Read more: https://github.com/graphql-python/graphene/blob/v2.0.0/UPGRADE-v2.0.md#node-connections" ) assert issubclass(connection_type, Connection), ( '{} type have to be a subclass of Connection. Received "{}".' ).format(self.__class__.__name__, connection_type) return type @classmethod def resolve_connection(cls, connection_type, args, resolved): if isinstance(resolved, connection_type): return resolved assert isinstance(resolved, Iterable), ( "Resolved value from the connection field have to be iterable or instance of {}. " 'Received "{}"' ).format(connection_type, resolved) connection = connection_from_list( resolved, args, connection_type=connection_type, edge_type=connection_type.Edge, pageinfo_type=PageInfo, ) connection.iterable = resolved return connection @classmethod def connection_resolver(cls, resolver, connection_type, root, info, **args): resolved = resolver(root, info, **args) if isinstance(connection_type, NonNull): connection_type = connection_type.of_type on_resolve = partial(cls.resolve_connection, connection_type, args) return maybe_thenable(resolved, on_resolve) def get_resolver(self, parent_resolver): resolver = super(IterableConnectionField, self).get_resolver(parent_resolver) return partial(self.connection_resolver, resolver, self.type) ConnectionField = IterableConnectionField graphene-2.1.9/graphene/relay/mutation.py000066400000000000000000000045161407436344100204470ustar00rootroot00000000000000import re from collections import OrderedDict from ..types import Field, InputObjectType, String from ..types.mutation import Mutation from ..utils.thenables import maybe_thenable class ClientIDMutation(Mutation): class Meta: abstract = True @classmethod def __init_subclass_with_meta__( cls, output=None, input_fields=None, arguments=None, name=None, **options ): input_class = getattr(cls, "Input", None) base_name = re.sub("Payload$", "", name or cls.__name__) assert not output, "Can't specify any output" assert not arguments, "Can't specify any arguments" bases = (InputObjectType,) if input_class: bases += (input_class,) if not input_fields: input_fields = {} cls.Input = type( "{}Input".format(base_name), bases, OrderedDict( input_fields, client_mutation_id=String(name="clientMutationId") ), ) arguments = OrderedDict( input=cls.Input(required=True) # 'client_mutation_id': String(name='clientMutationId') ) mutate_and_get_payload = getattr(cls, "mutate_and_get_payload", None) if cls.mutate and cls.mutate.__func__ == ClientIDMutation.mutate.__func__: assert mutate_and_get_payload, ( "{name}.mutate_and_get_payload method is required" " in a ClientIDMutation." ).format(name=name or cls.__name__) if not name: name = "{}Payload".format(base_name) super(ClientIDMutation, cls).__init_subclass_with_meta__( output=None, arguments=arguments, name=name, **options ) cls._meta.fields["client_mutation_id"] = Field(String, name="clientMutationId") @classmethod def mutate(cls, root, info, input): def on_resolve(payload): try: payload.client_mutation_id = input.get("client_mutation_id") except Exception: raise Exception( ("Cannot set client_mutation_id in the payload object {}").format( repr(payload) ) ) return payload result = cls.mutate_and_get_payload(root, info, **input) return maybe_thenable(result, on_resolve) graphene-2.1.9/graphene/relay/node.py000066400000000000000000000071071407436344100175330ustar00rootroot00000000000000from collections import OrderedDict from functools import partial from inspect import isclass from graphql_relay import from_global_id, to_global_id from ..types import ID, Field, Interface, ObjectType from ..types.interface import InterfaceOptions from ..types.utils import get_type def is_node(objecttype): """ Check if the given objecttype has Node as an interface """ if not isclass(objecttype): return False if not issubclass(objecttype, ObjectType): return False for i in objecttype._meta.interfaces: if issubclass(i, Node): return True return False class GlobalID(Field): def __init__(self, node=None, parent_type=None, required=True, *args, **kwargs): super(GlobalID, self).__init__(ID, required=required, *args, **kwargs) self.node = node or Node self.parent_type_name = parent_type._meta.name if parent_type else None @staticmethod def id_resolver(parent_resolver, node, root, info, parent_type_name=None, **args): type_id = parent_resolver(root, info, **args) parent_type_name = parent_type_name or info.parent_type.name return node.to_global_id(parent_type_name, type_id) # root._meta.name def get_resolver(self, parent_resolver): return partial( self.id_resolver, parent_resolver, self.node, parent_type_name=self.parent_type_name, ) class NodeField(Field): def __init__(self, node, type=False, **kwargs): assert issubclass(node, Node), "NodeField can only operate in Nodes" self.node_type = node self.field_type = type super(NodeField, self).__init__( # If we don's specify a type, the field type will be the node # interface type or node, id=ID(required=True, description="The ID of the object"), **kwargs ) def get_resolver(self, parent_resolver): return partial(self.node_type.node_resolver, get_type(self.field_type)) class AbstractNode(Interface): class Meta: abstract = True @classmethod def __init_subclass_with_meta__(cls, **options): _meta = InterfaceOptions(cls) _meta.fields = OrderedDict( id=GlobalID(cls, description="The ID of the object.") ) super(AbstractNode, cls).__init_subclass_with_meta__(_meta=_meta, **options) class Node(AbstractNode): """An object with an ID""" @classmethod def Field(cls, *args, **kwargs): # noqa: N802 return NodeField(cls, *args, **kwargs) @classmethod def node_resolver(cls, only_type, root, info, id): return cls.get_node_from_global_id(info, id, only_type=only_type) @classmethod def get_node_from_global_id(cls, info, global_id, only_type=None): try: _type, _id = cls.from_global_id(global_id) graphene_type = info.schema.get_type(_type).graphene_type except Exception: return None if only_type: assert graphene_type == only_type, ("Must receive a {} id.").format( only_type._meta.name ) # We make sure the ObjectType implements the "Node" interface if cls not in graphene_type._meta.interfaces: return None get_node = getattr(graphene_type, "get_node", None) if get_node: return get_node(info, _id) @classmethod def from_global_id(cls, global_id): return from_global_id(global_id) @classmethod def to_global_id(cls, type, id): return to_global_id(type, id) graphene-2.1.9/graphene/relay/tests/000077500000000000000000000000001407436344100173715ustar00rootroot00000000000000graphene-2.1.9/graphene/relay/tests/__init__.py000066400000000000000000000000001407436344100214700ustar00rootroot00000000000000graphene-2.1.9/graphene/relay/tests/test_connection.py000066400000000000000000000120301407436344100231350ustar00rootroot00000000000000import pytest from ...types import Argument, Field, Int, List, NonNull, ObjectType, Schema, String from ..connection import Connection, ConnectionField, PageInfo from ..node import Node class MyObject(ObjectType): class Meta: interfaces = [Node] field = String() def test_connection(): class MyObjectConnection(Connection): extra = String() class Meta: node = MyObject class Edge: other = String() assert MyObjectConnection._meta.name == "MyObjectConnection" fields = MyObjectConnection._meta.fields assert list(fields.keys()) == ["page_info", "edges", "extra"] edge_field = fields["edges"] pageinfo_field = fields["page_info"] assert isinstance(edge_field, Field) assert isinstance(edge_field.type, NonNull) assert isinstance(edge_field.type.of_type, List) assert edge_field.type.of_type.of_type == MyObjectConnection.Edge assert isinstance(pageinfo_field, Field) assert isinstance(pageinfo_field.type, NonNull) assert pageinfo_field.type.of_type == PageInfo def test_connection_inherit_abstracttype(): class BaseConnection(object): extra = String() class MyObjectConnection(BaseConnection, Connection): class Meta: node = MyObject assert MyObjectConnection._meta.name == "MyObjectConnection" fields = MyObjectConnection._meta.fields assert list(fields.keys()) == ["page_info", "edges", "extra"] def test_connection_name(): custom_name = "MyObjectCustomNameConnection" class BaseConnection(object): extra = String() class MyObjectConnection(BaseConnection, Connection): class Meta: node = MyObject name = custom_name assert MyObjectConnection._meta.name == custom_name def test_edge(): class MyObjectConnection(Connection): class Meta: node = MyObject class Edge: other = String() Edge = MyObjectConnection.Edge assert Edge._meta.name == "MyObjectEdge" edge_fields = Edge._meta.fields assert list(edge_fields.keys()) == ["node", "cursor", "other"] assert isinstance(edge_fields["node"], Field) assert edge_fields["node"].type == MyObject assert isinstance(edge_fields["other"], Field) assert edge_fields["other"].type == String def test_edge_with_bases(): class BaseEdge(object): extra = String() class MyObjectConnection(Connection): class Meta: node = MyObject class Edge(BaseEdge): other = String() Edge = MyObjectConnection.Edge assert Edge._meta.name == "MyObjectEdge" edge_fields = Edge._meta.fields assert list(edge_fields.keys()) == ["node", "cursor", "extra", "other"] assert isinstance(edge_fields["node"], Field) assert edge_fields["node"].type == MyObject assert isinstance(edge_fields["other"], Field) assert edge_fields["other"].type == String def test_edge_with_nonnull_node(): class MyObjectConnection(Connection): class Meta: node = NonNull(MyObject) edge_fields = MyObjectConnection.Edge._meta.fields assert isinstance(edge_fields["node"], Field) assert isinstance(edge_fields["node"].type, NonNull) assert edge_fields["node"].type.of_type == MyObject def test_pageinfo(): assert PageInfo._meta.name == "PageInfo" fields = PageInfo._meta.fields assert list(fields.keys()) == [ "has_next_page", "has_previous_page", "start_cursor", "end_cursor", ] def test_connectionfield(): class MyObjectConnection(Connection): class Meta: node = MyObject field = ConnectionField(MyObjectConnection) assert field.args == { "before": Argument(String), "after": Argument(String), "first": Argument(Int), "last": Argument(Int), } def test_connectionfield_node_deprecated(): field = ConnectionField(MyObject) with pytest.raises(Exception) as exc_info: field.type assert "ConnectionFields now need a explicit ConnectionType for Nodes." in str( exc_info.value ) def test_connectionfield_custom_args(): class MyObjectConnection(Connection): class Meta: node = MyObject field = ConnectionField( MyObjectConnection, before=String(required=True), extra=String() ) assert field.args == { "before": Argument(NonNull(String)), "after": Argument(String), "first": Argument(Int), "last": Argument(Int), "extra": Argument(String), } def test_connectionfield_required(): class MyObjectConnection(Connection): class Meta: node = MyObject class Query(ObjectType): test_connection = ConnectionField(MyObjectConnection, required=True) def resolve_test_connection(root, info, **args): return [] schema = Schema(query=Query) executed = schema.execute("{ testConnection { edges { cursor } } }") assert not executed.errors assert executed.data == {"testConnection": {"edges": []}} graphene-2.1.9/graphene/relay/tests/test_connection_query.py000066400000000000000000000150621407436344100243720ustar00rootroot00000000000000from collections import OrderedDict from graphql_relay.utils import base64 from promise import Promise from ...types import ObjectType, Schema, String from ..connection import Connection, ConnectionField, PageInfo from ..node import Node letter_chars = ["A", "B", "C", "D", "E"] class Letter(ObjectType): class Meta: interfaces = (Node,) letter = String() class LetterConnection(Connection): class Meta: node = Letter class Query(ObjectType): letters = ConnectionField(LetterConnection) connection_letters = ConnectionField(LetterConnection) promise_letters = ConnectionField(LetterConnection) node = Node.Field() def resolve_letters(self, info, **args): return list(letters.values()) def resolve_promise_letters(self, info, **args): return Promise.resolve(list(letters.values())) def resolve_connection_letters(self, info, **args): return LetterConnection( page_info=PageInfo(has_next_page=True, has_previous_page=False), edges=[ LetterConnection.Edge(node=Letter(id=0, letter="A"), cursor="a-cursor") ], ) schema = Schema(Query) letters = OrderedDict() for i, letter in enumerate(letter_chars): letters[letter] = Letter(id=i, letter=letter) def edges(selected_letters): return [ { "node": {"id": base64("Letter:%s" % letter.id), "letter": letter.letter}, "cursor": base64("arrayconnection:%s" % letter.id), } for letter in [letters[i] for i in selected_letters] ] def cursor_for(ltr): letter = letters[ltr] return base64("arrayconnection:%s" % letter.id) def execute(args=""): if args: args = "(" + args + ")" return schema.execute( """ { letters%s { edges { node { id letter } cursor } pageInfo { hasPreviousPage hasNextPage startCursor endCursor } } } """ % args ) def check(args, letters, has_previous_page=False, has_next_page=False): result = execute(args) expected_edges = edges(letters) expected_page_info = { "hasPreviousPage": has_previous_page, "hasNextPage": has_next_page, "endCursor": expected_edges[-1]["cursor"] if expected_edges else None, "startCursor": expected_edges[0]["cursor"] if expected_edges else None, } assert not result.errors assert result.data == { "letters": {"edges": expected_edges, "pageInfo": expected_page_info} } def test_returns_all_elements_without_filters(): check("", "ABCDE") def test_respects_a_smaller_first(): check("first: 2", "AB", has_next_page=True) def test_respects_an_overly_large_first(): check("first: 10", "ABCDE") def test_respects_a_smaller_last(): check("last: 2", "DE", has_previous_page=True) def test_respects_an_overly_large_last(): check("last: 10", "ABCDE") def test_respects_first_and_after(): check('first: 2, after: "{}"'.format(cursor_for("B")), "CD", has_next_page=True) def test_respects_first_and_after_with_long_first(): check('first: 10, after: "{}"'.format(cursor_for("B")), "CDE") def test_respects_last_and_before(): check('last: 2, before: "{}"'.format(cursor_for("D")), "BC", has_previous_page=True) def test_respects_last_and_before_with_long_last(): check('last: 10, before: "{}"'.format(cursor_for("D")), "ABC") def test_respects_first_and_after_and_before_too_few(): check( 'first: 2, after: "{}", before: "{}"'.format(cursor_for("A"), cursor_for("E")), "BC", has_next_page=True, ) def test_respects_first_and_after_and_before_too_many(): check( 'first: 4, after: "{}", before: "{}"'.format(cursor_for("A"), cursor_for("E")), "BCD", ) def test_respects_first_and_after_and_before_exactly_right(): check( 'first: 3, after: "{}", before: "{}"'.format(cursor_for("A"), cursor_for("E")), "BCD", ) def test_respects_last_and_after_and_before_too_few(): check( 'last: 2, after: "{}", before: "{}"'.format(cursor_for("A"), cursor_for("E")), "CD", has_previous_page=True, ) def test_respects_last_and_after_and_before_too_many(): check( 'last: 4, after: "{}", before: "{}"'.format(cursor_for("A"), cursor_for("E")), "BCD", ) def test_respects_last_and_after_and_before_exactly_right(): check( 'last: 3, after: "{}", before: "{}"'.format(cursor_for("A"), cursor_for("E")), "BCD", ) def test_returns_no_elements_if_first_is_0(): check("first: 0", "", has_next_page=True) def test_returns_all_elements_if_cursors_are_invalid(): check('before: "invalid" after: "invalid"', "ABCDE") def test_returns_all_elements_if_cursors_are_on_the_outside(): check( 'before: "{}" after: "{}"'.format( base64("arrayconnection:%s" % 6), base64("arrayconnection:%s" % -1) ), "ABCDE", ) def test_returns_no_elements_if_cursors_cross(): check( 'before: "{}" after: "{}"'.format( base64("arrayconnection:%s" % 2), base64("arrayconnection:%s" % 4) ), "", ) def test_connection_type_nodes(): result = schema.execute( """ { connectionLetters { edges { node { id letter } cursor } pageInfo { hasPreviousPage hasNextPage } } } """ ) assert not result.errors assert result.data == { "connectionLetters": { "edges": [ {"node": {"id": "TGV0dGVyOjA=", "letter": "A"}, "cursor": "a-cursor"} ], "pageInfo": {"hasPreviousPage": False, "hasNextPage": True}, } } def test_connection_promise(): result = schema.execute( """ { promiseLetters(first:1) { edges { node { id letter } } pageInfo { hasPreviousPage hasNextPage } } } """ ) assert not result.errors assert result.data == { "promiseLetters": { "edges": [{"node": {"id": "TGV0dGVyOjA=", "letter": "A"}}], "pageInfo": {"hasPreviousPage": False, "hasNextPage": True}, } } graphene-2.1.9/graphene/relay/tests/test_global_id.py000066400000000000000000000030461407436344100227210ustar00rootroot00000000000000from graphql_relay import to_global_id from ...types import ID, NonNull, ObjectType, String from ...types.definitions import GrapheneObjectType from ..node import GlobalID, Node class CustomNode(Node): class Meta: name = "Node" class User(ObjectType): class Meta: interfaces = [CustomNode] name = String() class Info(object): def __init__(self, parent_type): self.parent_type = GrapheneObjectType( graphene_type=parent_type, name=parent_type._meta.name, description=parent_type._meta.description, fields=None, is_type_of=parent_type.is_type_of, interfaces=None, ) def test_global_id_defaults_to_required_and_node(): gid = GlobalID() assert isinstance(gid.type, NonNull) assert gid.type.of_type == ID assert gid.node == Node def test_global_id_allows_overriding_of_node_and_required(): gid = GlobalID(node=CustomNode, required=False) assert gid.type == ID assert gid.node == CustomNode def test_global_id_defaults_to_info_parent_type(): my_id = "1" gid = GlobalID() id_resolver = gid.get_resolver(lambda *_: my_id) my_global_id = id_resolver(None, Info(User)) assert my_global_id == to_global_id(User._meta.name, my_id) def test_global_id_allows_setting_customer_parent_type(): my_id = "1" gid = GlobalID(parent_type=User) id_resolver = gid.get_resolver(lambda *_: my_id) my_global_id = id_resolver(None, None) assert my_global_id == to_global_id(User._meta.name, my_id) graphene-2.1.9/graphene/relay/tests/test_mutation.py000066400000000000000000000132701407436344100226450ustar00rootroot00000000000000import pytest from promise import Promise from ...types import ( ID, Argument, Field, InputField, InputObjectType, NonNull, ObjectType, Schema, ) from ...types.scalars import String from ..mutation import ClientIDMutation class SharedFields(object): shared = String() class MyNode(ObjectType): # class Meta: # interfaces = (Node, ) id = ID() name = String() class SaySomething(ClientIDMutation): class Input: what = String() phrase = String() @staticmethod def mutate_and_get_payload(self, info, what, client_mutation_id=None): return SaySomething(phrase=str(what)) class FixedSaySomething(object): __slots__ = ("phrase",) def __init__(self, phrase): self.phrase = phrase class SaySomethingFixed(ClientIDMutation): class Input: what = String() phrase = String() @staticmethod def mutate_and_get_payload(self, info, what, client_mutation_id=None): return FixedSaySomething(phrase=str(what)) class SaySomethingPromise(ClientIDMutation): class Input: what = String() phrase = String() @staticmethod def mutate_and_get_payload(self, info, what, client_mutation_id=None): return Promise.resolve(SaySomething(phrase=str(what))) # MyEdge = MyNode.Connection.Edge class MyEdge(ObjectType): node = Field(MyNode) cursor = String() class OtherMutation(ClientIDMutation): class Input(SharedFields): additional_field = String() name = String() my_node_edge = Field(MyEdge) @staticmethod def mutate_and_get_payload( self, info, shared="", additional_field="", client_mutation_id=None ): edge_type = MyEdge return OtherMutation( name=shared + additional_field, my_node_edge=edge_type(cursor="1", node=MyNode(name="name")), ) class RootQuery(ObjectType): something = String() class Mutation(ObjectType): say = SaySomething.Field() say_fixed = SaySomethingFixed.Field() say_promise = SaySomethingPromise.Field() other = OtherMutation.Field() schema = Schema(query=RootQuery, mutation=Mutation) def test_no_mutate_and_get_payload(): with pytest.raises(AssertionError) as excinfo: class MyMutation(ClientIDMutation): pass assert ( "MyMutation.mutate_and_get_payload method is required in a ClientIDMutation." == str(excinfo.value) ) def test_mutation(): fields = SaySomething._meta.fields assert list(fields.keys()) == ["phrase", "client_mutation_id"] assert SaySomething._meta.name == "SaySomethingPayload" assert isinstance(fields["phrase"], Field) field = SaySomething.Field() assert field.type == SaySomething assert list(field.args.keys()) == ["input"] assert isinstance(field.args["input"], Argument) assert isinstance(field.args["input"].type, NonNull) assert field.args["input"].type.of_type == SaySomething.Input assert isinstance(fields["client_mutation_id"], Field) assert fields["client_mutation_id"].name == "clientMutationId" assert fields["client_mutation_id"].type == String def test_mutation_input(): Input = SaySomething.Input assert issubclass(Input, InputObjectType) fields = Input._meta.fields assert list(fields.keys()) == ["what", "client_mutation_id"] assert isinstance(fields["what"], InputField) assert fields["what"].type == String assert isinstance(fields["client_mutation_id"], InputField) assert fields["client_mutation_id"].type == String def test_subclassed_mutation(): fields = OtherMutation._meta.fields assert list(fields.keys()) == ["name", "my_node_edge", "client_mutation_id"] assert isinstance(fields["name"], Field) field = OtherMutation.Field() assert field.type == OtherMutation assert list(field.args.keys()) == ["input"] assert isinstance(field.args["input"], Argument) assert isinstance(field.args["input"].type, NonNull) assert field.args["input"].type.of_type == OtherMutation.Input def test_subclassed_mutation_input(): Input = OtherMutation.Input assert issubclass(Input, InputObjectType) fields = Input._meta.fields assert list(fields.keys()) == ["shared", "additional_field", "client_mutation_id"] assert isinstance(fields["shared"], InputField) assert fields["shared"].type == String assert isinstance(fields["additional_field"], InputField) assert fields["additional_field"].type == String assert isinstance(fields["client_mutation_id"], InputField) assert fields["client_mutation_id"].type == String def test_node_query(): executed = schema.execute( 'mutation a { say(input: {what:"hello", clientMutationId:"1"}) { phrase } }' ) assert not executed.errors assert executed.data == {"say": {"phrase": "hello"}} def test_node_query_fixed(): executed = schema.execute( 'mutation a { sayFixed(input: {what:"hello", clientMutationId:"1"}) { phrase } }' ) assert "Cannot set client_mutation_id in the payload object" in str( executed.errors[0] ) def test_node_query_promise(): executed = schema.execute( 'mutation a { sayPromise(input: {what:"hello", clientMutationId:"1"}) { phrase } }' ) assert not executed.errors assert executed.data == {"sayPromise": {"phrase": "hello"}} def test_edge_query(): executed = schema.execute( 'mutation a { other(input: {clientMutationId:"1"}) { clientMutationId, myNodeEdge { cursor node { name }} } }' ) assert not executed.errors assert dict(executed.data) == { "other": { "clientMutationId": "1", "myNodeEdge": {"cursor": "1", "node": {"name": "name"}}, } } graphene-2.1.9/graphene/relay/tests/test_node.py000066400000000000000000000107421407436344100217330ustar00rootroot00000000000000from collections import OrderedDict from graphql_relay import to_global_id from ...types import ObjectType, Schema, String from ..node import Node, is_node class SharedNodeFields(object): shared = String() something_else = String() def resolve_something_else(*_): return "----" class MyNode(ObjectType): class Meta: interfaces = (Node,) name = String() @staticmethod def get_node(info, id): return MyNode(name=str(id)) class MyOtherNode(SharedNodeFields, ObjectType): extra_field = String() class Meta: interfaces = (Node,) def resolve_extra_field(self, *_): return "extra field info." @staticmethod def get_node(info, id): return MyOtherNode(shared=str(id)) class RootQuery(ObjectType): first = String() node = Node.Field() only_node = Node.Field(MyNode) only_node_lazy = Node.Field(lambda: MyNode) schema = Schema(query=RootQuery, types=[MyNode, MyOtherNode]) def test_node_good(): assert "id" in MyNode._meta.fields assert is_node(MyNode) assert not is_node(object) def test_node_query(): executed = schema.execute( '{ node(id:"%s") { ... on MyNode { name } } }' % Node.to_global_id("MyNode", 1) ) assert not executed.errors assert executed.data == {"node": {"name": "1"}} def test_subclassed_node_query(): executed = schema.execute( '{ node(id:"%s") { ... on MyOtherNode { shared, extraField, somethingElse } } }' % to_global_id("MyOtherNode", 1) ) assert not executed.errors assert executed.data == OrderedDict( { "node": OrderedDict( [ ("shared", "1"), ("extraField", "extra field info."), ("somethingElse", "----"), ] ) } ) def test_node_requesting_non_node(): executed = schema.execute( '{ node(id:"%s") { __typename } } ' % Node.to_global_id("RootQuery", 1) ) assert executed.data == {"node": None} def test_node_query_incorrect_id(): executed = schema.execute( '{ node(id:"%s") { ... on MyNode { name } } }' % "something:2" ) assert not executed.errors assert executed.data == {"node": None} def test_node_field(): node_field = Node.Field() assert node_field.type == Node assert node_field.node_type == Node def test_node_field_custom(): node_field = Node.Field(MyNode) assert node_field.type == MyNode assert node_field.node_type == Node def test_node_field_args(): field_args = { "name": "my_custom_name", "description": "my_custom_description", "deprecation_reason": "my_custom_deprecation_reason", } node_field = Node.Field(**field_args) for field_arg, value in field_args.items(): assert getattr(node_field, field_arg) == value def test_node_field_only_type(): executed = schema.execute( '{ onlyNode(id:"%s") { __typename, name } } ' % Node.to_global_id("MyNode", 1) ) assert not executed.errors assert executed.data == {"onlyNode": {"__typename": "MyNode", "name": "1"}} def test_node_field_only_type_wrong(): executed = schema.execute( '{ onlyNode(id:"%s") { __typename, name } } ' % Node.to_global_id("MyOtherNode", 1) ) assert len(executed.errors) == 1 assert str(executed.errors[0]) == "Must receive a MyNode id." assert executed.data == {"onlyNode": None} def test_node_field_only_lazy_type(): executed = schema.execute( '{ onlyNodeLazy(id:"%s") { __typename, name } } ' % Node.to_global_id("MyNode", 1) ) assert not executed.errors assert executed.data == {"onlyNodeLazy": {"__typename": "MyNode", "name": "1"}} def test_node_field_only_lazy_type_wrong(): executed = schema.execute( '{ onlyNodeLazy(id:"%s") { __typename, name } } ' % Node.to_global_id("MyOtherNode", 1) ) assert len(executed.errors) == 1 assert str(executed.errors[0]) == "Must receive a MyNode id." assert executed.data == {"onlyNodeLazy": None} def test_str_schema(): assert ( str(schema) == """ schema { query: RootQuery } type MyNode implements Node { id: ID! name: String } type MyOtherNode implements Node { id: ID! shared: String somethingElse: String extraField: String } interface Node { id: ID! } type RootQuery { first: String node(id: ID!): Node onlyNode(id: ID!): MyNode onlyNodeLazy(id: ID!): MyNode } """.lstrip() ) graphene-2.1.9/graphene/relay/tests/test_node_custom.py000066400000000000000000000134301407436344100233220ustar00rootroot00000000000000from graphql import graphql from ...types import Interface, ObjectType, Schema from ...types.scalars import Int, String from ..node import Node class CustomNode(Node): class Meta: name = "Node" @staticmethod def to_global_id(type, id): return id @staticmethod def get_node_from_global_id(info, id, only_type=None): assert info.schema == schema if id in user_data: return user_data.get(id) else: return photo_data.get(id) class BasePhoto(Interface): width = Int() class User(ObjectType): class Meta: interfaces = [CustomNode] name = String() class Photo(ObjectType): class Meta: interfaces = [CustomNode, BasePhoto] user_data = {"1": User(id="1", name="John Doe"), "2": User(id="2", name="Jane Smith")} photo_data = {"3": Photo(id="3", width=300), "4": Photo(id="4", width=400)} class RootQuery(ObjectType): node = CustomNode.Field() schema = Schema(query=RootQuery, types=[User, Photo]) def test_str_schema_correct(): assert ( str(schema) == """schema { query: RootQuery } interface BasePhoto { width: Int } interface Node { id: ID! } type Photo implements Node, BasePhoto { id: ID! width: Int } type RootQuery { node(id: ID!): Node } type User implements Node { id: ID! name: String } """ ) def test_gets_the_correct_id_for_users(): query = """ { node(id: "1") { id } } """ expected = {"node": {"id": "1"}} result = graphql(schema, query) assert not result.errors assert result.data == expected def test_gets_the_correct_id_for_photos(): query = """ { node(id: "4") { id } } """ expected = {"node": {"id": "4"}} result = graphql(schema, query) assert not result.errors assert result.data == expected def test_gets_the_correct_name_for_users(): query = """ { node(id: "1") { id ... on User { name } } } """ expected = {"node": {"id": "1", "name": "John Doe"}} result = graphql(schema, query) assert not result.errors assert result.data == expected def test_gets_the_correct_width_for_photos(): query = """ { node(id: "4") { id ... on Photo { width } } } """ expected = {"node": {"id": "4", "width": 400}} result = graphql(schema, query) assert not result.errors assert result.data == expected def test_gets_the_correct_typename_for_users(): query = """ { node(id: "1") { id __typename } } """ expected = {"node": {"id": "1", "__typename": "User"}} result = graphql(schema, query) assert not result.errors assert result.data == expected def test_gets_the_correct_typename_for_photos(): query = """ { node(id: "4") { id __typename } } """ expected = {"node": {"id": "4", "__typename": "Photo"}} result = graphql(schema, query) assert not result.errors assert result.data == expected def test_ignores_photo_fragments_on_user(): query = """ { node(id: "1") { id ... on Photo { width } } } """ expected = {"node": {"id": "1"}} result = graphql(schema, query) assert not result.errors assert result.data == expected def test_returns_null_for_bad_ids(): query = """ { node(id: "5") { id } } """ expected = {"node": None} result = graphql(schema, query) assert not result.errors assert result.data == expected def test_have_correct_node_interface(): query = """ { __type(name: "Node") { name kind fields { name type { kind ofType { name kind } } } } } """ expected = { "__type": { "name": "Node", "kind": "INTERFACE", "fields": [ { "name": "id", "type": { "kind": "NON_NULL", "ofType": {"name": "ID", "kind": "SCALAR"}, }, } ], } } result = graphql(schema, query) assert not result.errors assert result.data == expected def test_has_correct_node_root_field(): query = """ { __schema { queryType { fields { name type { name kind } args { name type { kind ofType { name kind } } } } } } } """ expected = { "__schema": { "queryType": { "fields": [ { "name": "node", "type": {"name": "Node", "kind": "INTERFACE"}, "args": [ { "name": "id", "type": { "kind": "NON_NULL", "ofType": {"name": "ID", "kind": "SCALAR"}, }, } ], } ] } } } result = graphql(schema, query) assert not result.errors assert result.data == expected graphene-2.1.9/graphene/test/000077500000000000000000000000001407436344100160725ustar00rootroot00000000000000graphene-2.1.9/graphene/test/__init__.py000066400000000000000000000025301407436344100202030ustar00rootroot00000000000000from promise import Promise, is_thenable import six from graphql.error import format_error as format_graphql_error from graphql.error import GraphQLError from graphene.types.schema import Schema def default_format_error(error): if isinstance(error, GraphQLError): return format_graphql_error(error) return {"message": six.text_type(error)} def format_execution_result(execution_result, format_error): if execution_result: response = {} if execution_result.errors: response["errors"] = [format_error(e) for e in execution_result.errors] if not execution_result.invalid: response["data"] = execution_result.data return response class Client(object): def __init__(self, schema, format_error=None, **execute_options): assert isinstance(schema, Schema) self.schema = schema self.execute_options = execute_options self.format_error = format_error or default_format_error def format_result(self, result): return format_execution_result(result, self.format_error) def execute(self, *args, **kwargs): executed = self.schema.execute(*args, **dict(self.execute_options, **kwargs)) if is_thenable(executed): return Promise.resolve(executed).then(self.format_result) return self.format_result(executed) graphene-2.1.9/graphene/tests/000077500000000000000000000000001407436344100162555ustar00rootroot00000000000000graphene-2.1.9/graphene/tests/__init__.py000066400000000000000000000000001407436344100203540ustar00rootroot00000000000000graphene-2.1.9/graphene/tests/issues/000077500000000000000000000000001407436344100175705ustar00rootroot00000000000000graphene-2.1.9/graphene/tests/issues/__init__.py000066400000000000000000000000001407436344100216670ustar00rootroot00000000000000graphene-2.1.9/graphene/tests/issues/test_313.py000066400000000000000000000021161407436344100215070ustar00rootroot00000000000000# https://github.com/graphql-python/graphene/issues/313 import graphene class Query(graphene.ObjectType): rand = graphene.String() class Success(graphene.ObjectType): yeah = graphene.String() class Error(graphene.ObjectType): message = graphene.String() class CreatePostResult(graphene.Union): class Meta: types = [Success, Error] class CreatePost(graphene.Mutation): class Input: text = graphene.String(required=True) result = graphene.Field(CreatePostResult) def mutate(self, info, text): result = Success(yeah="yeah") return CreatePost(result=result) class Mutations(graphene.ObjectType): create_post = CreatePost.Field() # tests.py def test_create_post(): query_string = """ mutation { createPost(text: "Try this out") { result { __typename } } } """ schema = graphene.Schema(query=Query, mutation=Mutations) result = schema.execute(query_string) assert not result.errors assert result.data["createPost"]["result"]["__typename"] == "Success" graphene-2.1.9/graphene/tests/issues/test_356.py000066400000000000000000000012121407436344100215120ustar00rootroot00000000000000# https://github.com/graphql-python/graphene/issues/356 import pytest import graphene from graphene import relay class SomeTypeOne(graphene.ObjectType): pass class SomeTypeTwo(graphene.ObjectType): pass class MyUnion(graphene.Union): class Meta: types = (SomeTypeOne, SomeTypeTwo) def test_issue(): class Query(graphene.ObjectType): things = relay.ConnectionField(MyUnion) with pytest.raises(Exception) as exc_info: graphene.Schema(query=Query) assert str(exc_info.value) == ( "IterableConnectionField type have to be a subclass of Connection. " 'Received "MyUnion".' ) graphene-2.1.9/graphene/tests/issues/test_425.py000066400000000000000000000060351407436344100215170ustar00rootroot00000000000000# https://github.com/graphql-python/graphene/issues/425 # Adapted for Graphene 2.0 from graphene.types.enum import Enum, EnumOptions from graphene.types.inputobjecttype import InputObjectType from graphene.types.objecttype import ObjectType, ObjectTypeOptions # ObjectType class SpecialOptions(ObjectTypeOptions): other_attr = None class SpecialObjectType(ObjectType): @classmethod def __init_subclass_with_meta__(cls, other_attr="default", **options): _meta = SpecialOptions(cls) _meta.other_attr = other_attr super(SpecialObjectType, cls).__init_subclass_with_meta__( _meta=_meta, **options ) def test_special_objecttype_could_be_subclassed(): class MyType(SpecialObjectType): class Meta: other_attr = "yeah!" assert MyType._meta.other_attr == "yeah!" def test_special_objecttype_could_be_subclassed_default(): class MyType(SpecialObjectType): pass assert MyType._meta.other_attr == "default" def test_special_objecttype_inherit_meta_options(): class MyType(SpecialObjectType): pass assert MyType._meta.name == "MyType" assert MyType._meta.default_resolver is None assert MyType._meta.interfaces == () # InputObjectType class SpecialInputObjectTypeOptions(ObjectTypeOptions): other_attr = None class SpecialInputObjectType(InputObjectType): @classmethod def __init_subclass_with_meta__(cls, other_attr="default", **options): _meta = SpecialInputObjectTypeOptions(cls) _meta.other_attr = other_attr super(SpecialInputObjectType, cls).__init_subclass_with_meta__( _meta=_meta, **options ) def test_special_inputobjecttype_could_be_subclassed(): class MyInputObjectType(SpecialInputObjectType): class Meta: other_attr = "yeah!" assert MyInputObjectType._meta.other_attr == "yeah!" def test_special_inputobjecttype_could_be_subclassed_default(): class MyInputObjectType(SpecialInputObjectType): pass assert MyInputObjectType._meta.other_attr == "default" def test_special_inputobjecttype_inherit_meta_options(): class MyInputObjectType(SpecialInputObjectType): pass assert MyInputObjectType._meta.name == "MyInputObjectType" # Enum class SpecialEnumOptions(EnumOptions): other_attr = None class SpecialEnum(Enum): @classmethod def __init_subclass_with_meta__(cls, other_attr="default", **options): _meta = SpecialEnumOptions(cls) _meta.other_attr = other_attr super(SpecialEnum, cls).__init_subclass_with_meta__(_meta=_meta, **options) def test_special_enum_could_be_subclassed(): class MyEnum(SpecialEnum): class Meta: other_attr = "yeah!" assert MyEnum._meta.other_attr == "yeah!" def test_special_enum_could_be_subclassed_default(): class MyEnum(SpecialEnum): pass assert MyEnum._meta.other_attr == "default" def test_special_enum_inherit_meta_options(): class MyEnum(SpecialEnum): pass assert MyEnum._meta.name == "MyEnum" graphene-2.1.9/graphene/tests/issues/test_490.py000066400000000000000000000010041407436344100215100ustar00rootroot00000000000000# https://github.com/graphql-python/graphene/issues/313 import graphene class Query(graphene.ObjectType): some_field = graphene.String(from_=graphene.String(name="from")) def resolve_some_field(self, info, from_=None): return from_ def test_issue(): query_string = """ query myQuery { someField(from: "Oh") } """ schema = graphene.Schema(query=Query) result = schema.execute(query_string) assert not result.errors assert result.data["someField"] == "Oh" graphene-2.1.9/graphene/tests/issues/test_720.py000066400000000000000000000022551407436344100215150ustar00rootroot00000000000000# https://github.com/graphql-python/graphene/issues/720 # InputObjectTypes overwrite the "fields" attribute of the provided # _meta object, so even if dynamic fields are provided with a standard # InputObjectTypeOptions, they are ignored. import graphene class MyInputClass(graphene.InputObjectType): @classmethod def __init_subclass_with_meta__( cls, container=None, _meta=None, fields=None, **options ): if _meta is None: _meta = graphene.types.inputobjecttype.InputObjectTypeOptions(cls) _meta.fields = fields super(MyInputClass, cls).__init_subclass_with_meta__( container=container, _meta=_meta, **options ) class MyInput(MyInputClass): class Meta: fields = dict(x=graphene.Field(graphene.Int)) class Query(graphene.ObjectType): myField = graphene.Field(graphene.String, input=graphene.Argument(MyInput)) def resolve_myField(parent, info, input): return "ok" def test_issue(): query_string = """ query myQuery { myField(input: {x: 1}) } """ schema = graphene.Schema(query=Query) result = schema.execute(query_string) assert not result.errors graphene-2.1.9/graphene/tests/issues/test_956.py000066400000000000000000000005141407436344100215240ustar00rootroot00000000000000import graphene def test_issue(): options = {"description": "This my enum", "deprecation_reason": "For the funs"} new_enum = graphene.Enum("MyEnum", [("some", "data")], **options) assert new_enum._meta.description == options["description"] assert new_enum._meta.deprecation_reason == options["deprecation_reason"] graphene-2.1.9/graphene/types/000077500000000000000000000000001407436344100162575ustar00rootroot00000000000000graphene-2.1.9/graphene/types/__init__.py000066400000000000000000000021671407436344100203760ustar00rootroot00000000000000# flake8: noqa from graphql import ResolveInfo from .objecttype import ObjectType from .interface import Interface from .mutation import Mutation from .scalars import Scalar, String, ID, Int, Float, Boolean from .datetime import Date, DateTime, Time from .decimal import Decimal from .json import JSONString from .uuid import UUID from .schema import Schema from .structures import List, NonNull from .enum import Enum from .field import Field from .inputfield import InputField from .argument import Argument from .inputobjecttype import InputObjectType from .dynamic import Dynamic from .union import Union from .context import Context # Deprecated from .abstracttype import AbstractType __all__ = [ "ObjectType", "InputObjectType", "Interface", "Mutation", "Enum", "Field", "InputField", "Schema", "Scalar", "String", "ID", "Int", "Float", "Date", "DateTime", "Time", "Decimal", "JSONString", "UUID", "Boolean", "List", "NonNull", "Argument", "Dynamic", "Union", "Context", "ResolveInfo", # Deprecated "AbstractType", ] graphene-2.1.9/graphene/types/abstracttype.py000066400000000000000000000007601407436344100213410ustar00rootroot00000000000000from ..utils.deprecated import warn_deprecation from ..utils.subclass_with_meta import SubclassWithMeta class AbstractType(SubclassWithMeta): def __init_subclass__(cls, *args, **kwargs): warn_deprecation( "Abstract type is deprecated, please use normal object inheritance instead.\n" "See more: https://github.com/graphql-python/graphene/blob/master/UPGRADE-v2.0.md#deprecations" ) super(AbstractType, cls).__init_subclass__(*args, **kwargs) graphene-2.1.9/graphene/types/argument.py000066400000000000000000000071271407436344100204620ustar00rootroot00000000000000from collections import OrderedDict from itertools import chain from .dynamic import Dynamic from .mountedtype import MountedType from .structures import NonNull from .utils import get_type class Argument(MountedType): """ Makes an Argument available on a Field in the GraphQL schema. Arguments will be parsed and provided to resolver methods for fields as keyword arguments. All ``arg`` and ``**extra_args`` for a ``graphene.Field`` are implicitly mounted as Argument using the below parameters. .. code:: python from graphene import String, Boolean, Argument age = String( # Boolean implicitly mounted as Argument dog_years=Boolean(description="convert to dog years"), # Boolean explicitly mounted as Argument decades=Argument(Boolean, default_value=False), ) args: type (class for a graphene.UnmountedType): must be a class (not an instance) of an unmounted graphene type (ex. scalar or object) which is used for the type of this argument in the GraphQL schema. required (bool): indicates this argument as not null in the graphql scehma. Same behavior as graphene.NonNull. Default False. name (str): the name of the GraphQL argument. Defaults to parameter name. description (str): the description of the GraphQL argument in the schema. default_value (Any): The value to be provided if the user does not set this argument in the operation. """ def __init__( self, type, default_value=None, description=None, name=None, required=False, _creation_counter=None, ): super(Argument, self).__init__(_creation_counter=_creation_counter) if required: type = NonNull(type) self.name = name self._type = type self.default_value = default_value self.description = description @property def type(self): return get_type(self._type) def __eq__(self, other): return isinstance(other, Argument) and ( self.name == other.name and self.type == other.type and self.default_value == other.default_value and self.description == other.description ) def to_arguments(args, extra_args=None): from .unmountedtype import UnmountedType from .field import Field from .inputfield import InputField if extra_args: extra_args = sorted(extra_args.items(), key=lambda f: f[1]) else: extra_args = [] iter_arguments = chain(args.items(), extra_args) arguments = OrderedDict() for default_name, arg in iter_arguments: if isinstance(arg, Dynamic): arg = arg.get_type() if arg is None: # If the Dynamic type returned None # then we skip the Argument continue if isinstance(arg, UnmountedType): arg = Argument.mounted(arg) if isinstance(arg, (InputField, Field)): raise ValueError( "Expected {} to be Argument, but received {}. Try using Argument({}).".format( default_name, type(arg).__name__, arg.type ) ) if not isinstance(arg, Argument): raise ValueError('Unknown argument "{}".'.format(default_name)) arg_name = default_name or arg.name assert ( arg_name not in arguments ), 'More than one Argument have same name "{}".'.format(arg_name) arguments[arg_name] = arg return arguments graphene-2.1.9/graphene/types/base.py000066400000000000000000000025351407436344100175500ustar00rootroot00000000000000from ..utils.subclass_with_meta import SubclassWithMeta from ..utils.trim_docstring import trim_docstring import six if six.PY3: from typing import Type class BaseOptions(object): name = None # type: str description = None # type: str _frozen = False # type: bool def __init__(self, class_type): self.class_type = class_type # type: Type def freeze(self): self._frozen = True def __setattr__(self, name, value): if not self._frozen: super(BaseOptions, self).__setattr__(name, value) else: raise Exception("Can't modify frozen Options {}".format(self)) def __repr__(self): return "<{} name={}>".format(self.__class__.__name__, repr(self.name)) class BaseType(SubclassWithMeta): @classmethod def create_type(cls, class_name, **options): return type(class_name, (cls,), {"Meta": options}) @classmethod def __init_subclass_with_meta__( cls, name=None, description=None, _meta=None, **_kwargs ): assert "_meta" not in cls.__dict__, "Can't assign directly meta" if not _meta: return _meta.name = name or cls.__name__ _meta.description = description or trim_docstring(cls.__doc__) _meta.freeze() cls._meta = _meta super(BaseType, cls).__init_subclass_with_meta__() graphene-2.1.9/graphene/types/context.py000066400000000000000000000014371407436344100203220ustar00rootroot00000000000000class Context(object): """ Context can be used to make a convenient container for attributes to provide for execution for resolvers of a GraphQL operation like a query. .. code:: python from graphene import Context context = Context(loaders=build_dataloaders(), request=my_web_request) schema.execute('{ hello(name: "world") }', context=context) def resolve_hello(parent, info, name): info.context.request # value set in Context info.context.loaders # value set in Context # ... args: **params (Dict[str, Any]): values to make available on Context instance as attributes. """ def __init__(self, **params): for key, value in params.items(): setattr(self, key, value) graphene-2.1.9/graphene/types/datetime.py000066400000000000000000000052131407436344100204260ustar00rootroot00000000000000from __future__ import absolute_import import datetime from aniso8601 import parse_date, parse_datetime, parse_time from graphql.language import ast from six import string_types from .scalars import Scalar class Date(Scalar): """ The `Date` scalar type represents a Date value as specified by [iso8601](https://en.wikipedia.org/wiki/ISO_8601). """ @staticmethod def serialize(date): if isinstance(date, datetime.datetime): date = date.date() assert isinstance( date, datetime.date ), 'Received not compatible date "{}"'.format(repr(date)) return date.isoformat() @classmethod def parse_literal(cls, node): if isinstance(node, ast.StringValue): return cls.parse_value(node.value) @staticmethod def parse_value(value): try: if isinstance(value, datetime.date): return value elif isinstance(value, string_types): return parse_date(value) except ValueError: return None class DateTime(Scalar): """ The `DateTime` scalar type represents a DateTime value as specified by [iso8601](https://en.wikipedia.org/wiki/ISO_8601). """ @staticmethod def serialize(dt): assert isinstance( dt, (datetime.datetime, datetime.date) ), 'Received not compatible datetime "{}"'.format(repr(dt)) return dt.isoformat() @classmethod def parse_literal(cls, node): if isinstance(node, ast.StringValue): return cls.parse_value(node.value) @staticmethod def parse_value(value): try: if isinstance(value, datetime.datetime): return value elif isinstance(value, string_types): return parse_datetime(value) except ValueError: return None class Time(Scalar): """ The `Time` scalar type represents a Time value as specified by [iso8601](https://en.wikipedia.org/wiki/ISO_8601). """ @staticmethod def serialize(time): assert isinstance( time, datetime.time ), 'Received not compatible time "{}"'.format(repr(time)) return time.isoformat() @classmethod def parse_literal(cls, node): if isinstance(node, ast.StringValue): return cls.parse_value(node.value) @classmethod def parse_value(cls, value): try: if isinstance(value, datetime.time): return value elif isinstance(value, string_types): return parse_time(value) except ValueError: return None graphene-2.1.9/graphene/types/decimal.py000066400000000000000000000014231407436344100202270ustar00rootroot00000000000000from __future__ import absolute_import from decimal import Decimal as _Decimal from graphql.language import ast from .scalars import Scalar class Decimal(Scalar): """ The `Decimal` scalar type represents a python Decimal. """ @staticmethod def serialize(dec): if isinstance(dec, str): dec = _Decimal(dec) assert isinstance(dec, _Decimal), 'Received not compatible Decimal "{}"'.format( repr(dec) ) return str(dec) @classmethod def parse_literal(cls, node): if isinstance(node, ast.StringValue): return cls.parse_value(node.value) @staticmethod def parse_value(value): try: return _Decimal(value) except ValueError: return None graphene-2.1.9/graphene/types/definitions.py000066400000000000000000000016541407436344100211520ustar00rootroot00000000000000from graphql import ( GraphQLEnumType, GraphQLInputObjectType, GraphQLInterfaceType, GraphQLObjectType, GraphQLScalarType, GraphQLUnionType, ) class GrapheneGraphQLType(object): """ A class for extending the base GraphQLType with the related graphene_type """ def __init__(self, *args, **kwargs): self.graphene_type = kwargs.pop("graphene_type") super(GrapheneGraphQLType, self).__init__(*args, **kwargs) class GrapheneInterfaceType(GrapheneGraphQLType, GraphQLInterfaceType): pass class GrapheneUnionType(GrapheneGraphQLType, GraphQLUnionType): pass class GrapheneObjectType(GrapheneGraphQLType, GraphQLObjectType): pass class GrapheneScalarType(GrapheneGraphQLType, GraphQLScalarType): pass class GrapheneEnumType(GrapheneGraphQLType, GraphQLEnumType): pass class GrapheneInputObjectType(GrapheneGraphQLType, GraphQLInputObjectType): pass graphene-2.1.9/graphene/types/dynamic.py000066400000000000000000000012371407436344100202600ustar00rootroot00000000000000import inspect from functools import partial from .mountedtype import MountedType class Dynamic(MountedType): """ A Dynamic Type let us get the type in runtime when we generate the schema. So we can have lazy fields. """ def __init__(self, type, with_schema=False, _creation_counter=None): super(Dynamic, self).__init__(_creation_counter=_creation_counter) assert inspect.isfunction(type) or isinstance(type, partial) self.type = type self.with_schema = with_schema def get_type(self, schema=None): if schema and self.with_schema: return self.type(schema=schema) return self.type() graphene-2.1.9/graphene/types/enum.py000066400000000000000000000071251407436344100176020ustar00rootroot00000000000000from collections import OrderedDict import six from graphene.utils.subclass_with_meta import SubclassWithMeta_Meta from ..pyutils.compat import Enum as PyEnum from .base import BaseOptions, BaseType from .unmountedtype import UnmountedType def eq_enum(self, other): if isinstance(other, self.__class__): return self is other return self.value is other EnumType = type(PyEnum) class EnumOptions(BaseOptions): enum = None # type: Enum deprecation_reason = None class EnumMeta(SubclassWithMeta_Meta): def __new__(cls, name, bases, classdict, **options): enum_members = OrderedDict(classdict, __eq__=eq_enum) # We remove the Meta attribute from the class to not collide # with the enum values. enum_members.pop("Meta", None) enum = PyEnum(cls.__name__, enum_members) return SubclassWithMeta_Meta.__new__( cls, name, bases, OrderedDict(classdict, __enum__=enum), **options ) def get(cls, value): return cls._meta.enum(value) def __getitem__(cls, value): return cls._meta.enum[value] def __prepare__(name, bases, **kwargs): # noqa: N805 return OrderedDict() def __call__(cls, *args, **kwargs): # noqa: N805 if cls is Enum: description = kwargs.pop("description", None) deprecation_reason = kwargs.pop("deprecation_reason", None) return cls.from_enum( PyEnum(*args, **kwargs), description=description, deprecation_reason=deprecation_reason, ) return super(EnumMeta, cls).__call__(*args, **kwargs) # return cls._meta.enum(*args, **kwargs) def from_enum(cls, enum, description=None, deprecation_reason=None): # noqa: N805 description = description or enum.__doc__ meta_dict = { "enum": enum, "description": description, "deprecation_reason": deprecation_reason, } meta_class = type("Meta", (object,), meta_dict) return type(meta_class.enum.__name__, (Enum,), {"Meta": meta_class}) class Enum(six.with_metaclass(EnumMeta, UnmountedType, BaseType)): """ Enum type definition Defines a static set of values that can be provided as a Field, Argument or InputField. .. code:: python from graphene import Enum class NameFormat(Enum): FIRST_LAST = "first_last" LAST_FIRST = "last_first" Meta: enum (optional, Enum): Python enum to use as a base for GraphQL Enum. name (optional, str): Name of the GraphQL type (must be unique in schema). Defaults to class name. description (optional, str): Description of the GraphQL type in the schema. Defaults to class docstring. deprecation_reason (optional, str): Setting this value indicates that the enum is depreciated and may provide instruction or reason on how for clients to proceed. """ @classmethod def __init_subclass_with_meta__(cls, enum=None, _meta=None, **options): if not _meta: _meta = EnumOptions(cls) _meta.enum = enum or cls.__enum__ _meta.deprecation_reason = options.pop("deprecation_reason", None) for key, value in _meta.enum.__members__.items(): setattr(cls, key, value) super(Enum, cls).__init_subclass_with_meta__(_meta=_meta, **options) @classmethod def get_type(cls): """ This function is called when the unmounted type (Enum instance) is mounted (as a Field, InputField or Argument) """ return cls graphene-2.1.9/graphene/types/field.py000066400000000000000000000106231407436344100177160ustar00rootroot00000000000000import inspect from collections import OrderedDict try: from collections.abc import Mapping except ImportError: from collections import Mapping from functools import partial from .argument import Argument, to_arguments from .mountedtype import MountedType from .structures import NonNull from .unmountedtype import UnmountedType from .utils import get_type base_type = type def source_resolver(source, root, info, **args): resolved = getattr(root, source, None) if inspect.isfunction(resolved) or inspect.ismethod(resolved): return resolved() return resolved class Field(MountedType): """ Makes a field available on an ObjectType in the GraphQL schema. Any type can be mounted as a Field: - Object Type - Scalar Type - Enum - Interface - Union All class attributes of ``graphene.ObjectType`` are implicitly mounted as Field using the below arguments. .. code:: python class Person(ObjectType): first_name = graphene.String(required=True) # implicitly mounted as Field last_name = graphene.Field(String, description='Surname') # explicitly mounted as Field args: type (class for a graphene.UnmountedType): must be a class (not an instance) of an unmounted graphene type (ex. scalar or object) which is used for the type of this field in the GraphQL schema. args (optional, Dict[str, graphene.Argument]): arguments that can be input to the field. Prefer to use **extra_args. resolver (optional, Callable): A function to get the value for a Field from the parent value object. If not set, the default resolver method for the schema is used. source (optional, str): attribute name to resolve for this field from the parent value object. Alternative to resolver (cannot set both source and resolver). deprecation_reason (optional, str): Setting this value indicates that the field is depreciated and may provide instruction or reason on how for clients to proceed. required (optional, bool): indicates this field as not null in the graphql scehma. Same behavior as graphene.NonNull. Default False. name (optional, str): the name of the GraphQL field (must be unique in a type). Defaults to attribute name. description (optional, str): the description of the GraphQL field in the schema. default_value (optional, Any): Default value to resolve if none set from schema. **extra_args (optional, Dict[str, Union[graphene.Argument, graphene.UnmountedType]): any additional arguments to mount on the field. """ def __init__( self, type, args=None, resolver=None, source=None, deprecation_reason=None, name=None, description=None, required=False, _creation_counter=None, default_value=None, **extra_args ): super(Field, self).__init__(_creation_counter=_creation_counter) assert not args or isinstance(args, Mapping), ( 'Arguments in a field have to be a mapping, received "{}".' ).format(args) assert not ( source and resolver ), "A Field cannot have a source and a resolver in at the same time." assert not callable(default_value), ( 'The default value can not be a function but received "{}".' ).format(base_type(default_value)) if required: type = NonNull(type) # Check if name is actually an argument of the field if isinstance(name, (Argument, UnmountedType)): extra_args["name"] = name name = None # Check if source is actually an argument of the field if isinstance(source, (Argument, UnmountedType)): extra_args["source"] = source source = None self.name = name self._type = type self.args = to_arguments(args or OrderedDict(), extra_args) if source: resolver = partial(source_resolver, source) self.resolver = resolver self.deprecation_reason = deprecation_reason self.description = description self.default_value = default_value @property def type(self): return get_type(self._type) def get_resolver(self, parent_resolver): return self.resolver or parent_resolver graphene-2.1.9/graphene/types/generic.py000066400000000000000000000023711407436344100202500ustar00rootroot00000000000000from __future__ import unicode_literals from graphql.language.ast import ( BooleanValue, FloatValue, IntValue, ListValue, ObjectValue, StringValue, ) from graphene.types.scalars import MAX_INT, MIN_INT from .scalars import Scalar class GenericScalar(Scalar): """ The `GenericScalar` scalar type represents a generic GraphQL scalar value that could be: String, Boolean, Int, Float, List or Object. """ @staticmethod def identity(value): return value serialize = identity parse_value = identity @staticmethod def parse_literal(ast): if isinstance(ast, (StringValue, BooleanValue)): return ast.value elif isinstance(ast, IntValue): num = int(ast.value) if MIN_INT <= num <= MAX_INT: return num elif isinstance(ast, FloatValue): return float(ast.value) elif isinstance(ast, ListValue): return [GenericScalar.parse_literal(value) for value in ast.values] elif isinstance(ast, ObjectValue): return { field.name.value: GenericScalar.parse_literal(field.value) for field in ast.fields } else: return None graphene-2.1.9/graphene/types/inputfield.py000066400000000000000000000050211407436344100207720ustar00rootroot00000000000000from .mountedtype import MountedType from .structures import NonNull from .utils import get_type class InputField(MountedType): """ Makes a field available on an ObjectType in the GraphQL schema. Any type can be mounted as a Input Field except Interface and Union: - Object Type - Scalar Type - Enum Input object types also can't have arguments on their input fields, unlike regular ``graphene.Field``. All class attributes of ``graphene.InputObjectType`` are implicitly mounted as InputField using the below arguments. .. code:: python from graphene import InputObjectType, String, InputField class Person(InputObjectType): # implicitly mounted as Input Field first_name = String(required=True) # explicitly mounted as Input Field last_name = InputField(String, description="Surname") args: type (class for a graphene.UnmountedType): Must be a class (not an instance) of an unmounted graphene type (ex. scalar or object) which is used for the type of this field in the GraphQL schema. name (optional, str): Name of the GraphQL input field (must be unique in a type). Defaults to attribute name. default_value (optional, Any): Default value to use as input if none set in user operation ( query, mutation, etc.). deprecation_reason (optional, str): Setting this value indicates that the field is depreciated and may provide instruction or reason on how for clients to proceed. description (optional, str): Description of the GraphQL field in the schema. required (optional, bool): Indicates this input field as not null in the graphql scehma. Raises a validation error if argument not provided. Same behavior as graphene.NonNull. Default False. **extra_args (optional, Dict): Not used. """ def __init__( self, type, name=None, default_value=None, deprecation_reason=None, description=None, required=False, _creation_counter=None, **extra_args ): super(InputField, self).__init__(_creation_counter=_creation_counter) self.name = name if required: type = NonNull(type) self._type = type self.deprecation_reason = deprecation_reason self.default_value = default_value self.description = description @property def type(self): return get_type(self._type) graphene-2.1.9/graphene/types/inputobjecttype.py000066400000000000000000000061611407436344100220650ustar00rootroot00000000000000from collections import OrderedDict from .base import BaseOptions, BaseType from .inputfield import InputField from .unmountedtype import UnmountedType from .utils import yank_fields_from_attrs # For static type checking with Mypy MYPY = False if MYPY: from typing import Dict, Callable # NOQA class InputObjectTypeOptions(BaseOptions): fields = None # type: Dict[str, InputField] container = None # type: InputObjectTypeContainer class InputObjectTypeContainer(dict, BaseType): class Meta: abstract = True def __init__(self, *args, **kwargs): dict.__init__(self, *args, **kwargs) for key in self._meta.fields.keys(): setattr(self, key, self.get(key, None)) def __init_subclass__(cls, *args, **kwargs): pass class InputObjectType(UnmountedType, BaseType): """ Input Object Type Definition An input object defines a structured collection of fields which may be supplied to a field argument. Using ``graphene.NonNull`` will ensure that a input value must be provided by the query. All class attributes of ``graphene.InputObjectType`` are implicitly mounted as InputField using the below Meta class options. .. code:: python from graphene import InputObjectType, String, InputField class Person(InputObjectType): # implicitly mounted as Input Field first_name = String(required=True) # explicitly mounted as Input Field last_name = InputField(String, description="Surname") The fields on an input object type can themselves refer to input object types, but you can't mix input and output types in your schema. Meta class options (optional): name (str): the name of the GraphQL type (must be unique in schema). Defaults to class name. description (str): the description of the GraphQL type in the schema. Defaults to class docstring. container (class): A class reference for a value object that allows for attribute initialization and access. Default InputObjectTypeContainer. fields (Dict[str, graphene.InputField]): Dictionary of field name to InputField. Not recommended to use (prefer class attributes). """ @classmethod def __init_subclass_with_meta__(cls, container=None, _meta=None, **options): if not _meta: _meta = InputObjectTypeOptions(cls) fields = OrderedDict() for base in reversed(cls.__mro__): fields.update(yank_fields_from_attrs(base.__dict__, _as=InputField)) if _meta.fields: _meta.fields.update(fields) else: _meta.fields = fields if container is None: container = type(cls.__name__, (InputObjectTypeContainer, cls), {}) _meta.container = container super(InputObjectType, cls).__init_subclass_with_meta__(_meta=_meta, **options) @classmethod def get_type(cls): """ This function is called when the unmounted type (InputObjectType instance) is mounted (as a Field, InputField or Argument) """ return cls graphene-2.1.9/graphene/types/interface.py000066400000000000000000000043451407436344100205770ustar00rootroot00000000000000from collections import OrderedDict from .base import BaseOptions, BaseType from .field import Field from .utils import yank_fields_from_attrs # For static type checking with Mypy MYPY = False if MYPY: from typing import Dict # NOQA class InterfaceOptions(BaseOptions): fields = None # type: Dict[str, Field] class Interface(BaseType): """ Interface Type Definition When a field can return one of a heterogeneous set of types, a Interface type is used to describe what types are possible, what fields are in common across all types, as well as a function to determine which type is actually used when the field is resolved. .. code:: python from graphene import Interface, String class HasAddress(Interface): class Meta: description = "Address fields" address1 = String() address2 = String() If a field returns an Interface Type, the ambiguous type of the object can be determined using ``resolve_type`` on Interface and an ObjectType with ``Meta.possible_types`` or ``is_type_of``. Meta: name (str): Name of the GraphQL type (must be unique in schema). Defaults to class name. description (str): Description of the GraphQL type in the schema. Defaults to class docstring. fields (Dict[str, graphene.Field]): Dictionary of field name to Field. Not recommended to use (prefer class attributes). """ @classmethod def __init_subclass_with_meta__(cls, _meta=None, **options): if not _meta: _meta = InterfaceOptions(cls) fields = OrderedDict() for base in reversed(cls.__mro__): fields.update(yank_fields_from_attrs(base.__dict__, _as=Field)) if _meta.fields: _meta.fields.update(fields) else: _meta.fields = fields super(Interface, cls).__init_subclass_with_meta__(_meta=_meta, **options) @classmethod def resolve_type(cls, instance, info): from .objecttype import ObjectType if isinstance(instance, ObjectType): return type(instance) def __init__(self, *args, **kwargs): raise Exception("An Interface cannot be intitialized") graphene-2.1.9/graphene/types/json.py000066400000000000000000000012341407436344100176020ustar00rootroot00000000000000from __future__ import absolute_import import json from graphql.language import ast from .scalars import Scalar class JSONString(Scalar): """ Allows use of a JSON String for input / output from the GraphQL schema. Use of this type is *not recommended* as you lose the benefits of having a defined, static schema (one of the key benefits of GraphQL). """ @staticmethod def serialize(dt): return json.dumps(dt) @staticmethod def parse_literal(node): if isinstance(node, ast.StringValue): return json.loads(node.value) @staticmethod def parse_value(value): return json.loads(value) graphene-2.1.9/graphene/types/mountedtype.py000066400000000000000000000010661407436344100212110ustar00rootroot00000000000000from ..utils.orderedtype import OrderedType from .unmountedtype import UnmountedType class MountedType(OrderedType): @classmethod def mounted(cls, unmounted): # noqa: N802 """ Mount the UnmountedType instance """ assert isinstance(unmounted, UnmountedType), ("{} can't mount {}").format( cls.__name__, repr(unmounted) ) return cls( unmounted.get_type(), *unmounted.args, _creation_counter=unmounted.creation_counter, **unmounted.kwargs ) graphene-2.1.9/graphene/types/mutation.py000066400000000000000000000122561407436344100204770ustar00rootroot00000000000000from collections import OrderedDict from ..utils.deprecated import warn_deprecation from ..utils.get_unbound_function import get_unbound_function from ..utils.props import props from .field import Field from .objecttype import ObjectType, ObjectTypeOptions from .utils import yank_fields_from_attrs from .interface import Interface # For static type checking with Mypy MYPY = False if MYPY: from .argument import Argument # NOQA from typing import Dict, Type, Callable, Iterable # NOQA class MutationOptions(ObjectTypeOptions): arguments = None # type: Dict[str, Argument] output = None # type: Type[ObjectType] resolver = None # type: Callable interfaces = () # type: Iterable[Type[Interface]] class Mutation(ObjectType): """ Object Type Definition (mutation field) Mutation is a convenience type that helps us build a Field which takes Arguments and returns a mutation Output ObjectType. .. code:: python from graphene import Mutation, ObjectType, String, Boolean, Field class CreatePerson(Mutation): class Arguments: name = String() ok = Boolean() person = Field(Person) def mutate(parent, info, name): person = Person(name=name) ok = True return CreatePerson(person=person, ok=ok) class Mutation(ObjectType): create_person = CreatePerson.Field() Meta class options (optional): output (graphene.ObjectType): Or ``Output`` inner class with attributes on Mutation class. Or attributes from Mutation class. Fields which can be returned from this mutation field. resolver (Callable resolver method): Or ``mutate`` method on Mutation class. Perform data change and return output. arguments (Dict[str, graphene.Argument]): Or ``Arguments`` inner class with attributes on Mutation class. Arguments to use for the mutation Field. name (str): Name of the GraphQL type (must be unique in schema). Defaults to class name. description (str): Description of the GraphQL type in the schema. Defaults to class docstring. interfaces (Iterable[graphene.Interface]): GraphQL interfaces to extend with the payload object. All fields from interface will be included in this object's schema. fields (Dict[str, graphene.Field]): Dictionary of field name to Field. Not recommended to use (prefer class attributes or ``Meta.output``). """ @classmethod def __init_subclass_with_meta__( cls, interfaces=(), resolver=None, output=None, arguments=None, _meta=None, **options ): if not _meta: _meta = MutationOptions(cls) output = output or getattr(cls, "Output", None) fields = {} for interface in interfaces: assert issubclass(interface, Interface), ( 'All interfaces of {} must be a subclass of Interface. Received "{}".' ).format(cls.__name__, interface) fields.update(interface._meta.fields) if not output: # If output is defined, we don't need to get the fields fields = OrderedDict() for base in reversed(cls.__mro__): fields.update(yank_fields_from_attrs(base.__dict__, _as=Field)) output = cls if not arguments: input_class = getattr(cls, "Arguments", None) if not input_class: input_class = getattr(cls, "Input", None) if input_class: warn_deprecation( ( "Please use {name}.Arguments instead of {name}.Input." "Input is now only used in ClientMutationID.\n" "Read more:" " https://github.com/graphql-python/graphene/blob/v2.0.0/UPGRADE-v2.0.md#mutation-input" ).format(name=cls.__name__) ) if input_class: arguments = props(input_class) else: arguments = {} if not resolver: mutate = getattr(cls, "mutate", None) assert mutate, "All mutations must define a mutate method in it" resolver = get_unbound_function(mutate) if _meta.fields: _meta.fields.update(fields) else: _meta.fields = fields _meta.interfaces = interfaces _meta.output = output _meta.resolver = resolver _meta.arguments = arguments super(Mutation, cls).__init_subclass_with_meta__(_meta=_meta, **options) @classmethod def Field( cls, name=None, description=None, deprecation_reason=None, required=False ): """Mount instance of mutation Field.""" return Field( cls._meta.output, args=cls._meta.arguments, resolver=cls._meta.resolver, name=name, description=description or cls._meta.description, deprecation_reason=deprecation_reason, required=required, ) graphene-2.1.9/graphene/types/objecttype.py000066400000000000000000000137431407436344100210110ustar00rootroot00000000000000from collections import OrderedDict from .base import BaseOptions, BaseType from .field import Field from .interface import Interface from .utils import yank_fields_from_attrs # For static type checking with Mypy MYPY = False if MYPY: from typing import Dict, Iterable, Type # NOQA class ObjectTypeOptions(BaseOptions): fields = None # type: Dict[str, Field] interfaces = () # type: Iterable[Type[Interface]] class ObjectType(BaseType): """ Object Type Definition Almost all of the GraphQL types you define will be object types. Object types have a name, but most importantly describe their fields. The name of the type defined by an _ObjectType_ defaults to the class name. The type description defaults to the class docstring. This can be overriden by adding attributes to a Meta inner class. The class attributes of an _ObjectType_ are mounted as instances of ``graphene.Field``. Methods starting with ``resolve_`` are bound as resolvers of the matching Field name. If no resolver is provided, the default resolver is used. Ambiguous types with Interface and Union can be determined through``is_type_of`` method and ``Meta.possible_types`` attribute. .. code:: python from graphene import ObjectType, String, Field class Person(ObjectType): class Meta: description = 'A human' # implicitly mounted as Field first_name = String() # explicitly mounted as Field last_name = Field(String) def resolve_last_name(parent, info): return last_name ObjectType must be mounted using ``graphene.Field``. .. code:: python from graphene import ObjectType, Field class Query(ObjectType): person = Field(Person, description="My favorite person") Meta class options (optional): name (str): Name of the GraphQL type (must be unique in schema). Defaults to class name. description (str): Description of the GraphQL type in the schema. Defaults to class docstring. interfaces (Iterable[graphene.Interface]): GraphQL interfaces to extend with this object. all fields from interface will be included in this object's schema. possible_types (Iterable[class]): Used to test parent value object via isintance to see if this type can be used to resolve an ambigous type (interface, union). default_resolver (any Callable resolver): Override the default resolver for this type. Defaults to graphene default resolver which returns an attribute or dictionary key with the same name as the field. fields (Dict[str, graphene.Field]): Dictionary of field name to Field. Not recommended to use (prefer class attributes). An _ObjectType_ can be used as a simple value object by creating an instance of the class. .. code:: python p = Person(first_name='Bob', last_name='Roberts') assert p.first_name == 'Bob' Args: *args (List[Any]): Positional values to use for Field values of value object **kwargs (Dict[str: Any]): Keyword arguments to use for Field values of value object """ @classmethod def __init_subclass_with_meta__( cls, interfaces=(), possible_types=(), default_resolver=None, _meta=None, **options ): if not _meta: _meta = ObjectTypeOptions(cls) fields = OrderedDict() for interface in interfaces: assert issubclass(interface, Interface), ( 'All interfaces of {} must be a subclass of Interface. Received "{}".' ).format(cls.__name__, interface) fields.update(interface._meta.fields) for base in reversed(cls.__mro__): fields.update(yank_fields_from_attrs(base.__dict__, _as=Field)) assert not (possible_types and cls.is_type_of), ( "{name}.Meta.possible_types will cause type collision with {name}.is_type_of. " "Please use one or other." ).format(name=cls.__name__) if _meta.fields: _meta.fields.update(fields) else: _meta.fields = fields if not _meta.interfaces: _meta.interfaces = interfaces _meta.possible_types = possible_types _meta.default_resolver = default_resolver super(ObjectType, cls).__init_subclass_with_meta__(_meta=_meta, **options) is_type_of = None def __init__(self, *args, **kwargs): # ObjectType acting as container args_len = len(args) fields = self._meta.fields.items() if args_len > len(fields): # Daft, but matches old exception sans the err msg. raise IndexError("Number of args exceeds number of fields") fields_iter = iter(fields) if not kwargs: for val, (name, field) in zip(args, fields_iter): setattr(self, name, val) else: for val, (name, field) in zip(args, fields_iter): setattr(self, name, val) kwargs.pop(name, None) for name, field in fields_iter: try: val = kwargs.pop( name, field.default_value if isinstance(field, Field) else None ) setattr(self, name, val) except KeyError: pass if kwargs: for prop in list(kwargs): try: if isinstance( getattr(self.__class__, prop), property ) or prop.startswith("_"): setattr(self, prop, kwargs.pop(prop)) except AttributeError: pass if kwargs: raise TypeError( "'{}' is an invalid keyword argument for {}".format( list(kwargs)[0], self.__class__.__name__ ) ) graphene-2.1.9/graphene/types/resolver.py000066400000000000000000000013131407436344100204700ustar00rootroot00000000000000def attr_resolver(attname, default_value, root, info, **args): return getattr(root, attname, default_value) def dict_resolver(attname, default_value, root, info, **args): return root.get(attname, default_value) def dict_or_attr_resolver(attname, default_value, root, info, **args): resolver = attr_resolver if isinstance(root, dict): resolver = dict_resolver return resolver(attname, default_value, root, info, **args) default_resolver = dict_or_attr_resolver def set_default_resolver(resolver): global default_resolver assert callable(resolver), "Received non-callable resolver." default_resolver = resolver def get_default_resolver(): return default_resolver graphene-2.1.9/graphene/types/scalars.py000066400000000000000000000101661407436344100202650ustar00rootroot00000000000000import six from graphql.language.ast import BooleanValue, FloatValue, IntValue, StringValue from .base import BaseOptions, BaseType from .unmountedtype import UnmountedType if six.PY3: from typing import Any class ScalarOptions(BaseOptions): pass class Scalar(UnmountedType, BaseType): """ Scalar Type Definition The leaf values of any request and input values to arguments are Scalars (or Enums) and are defined with a name and a series of functions used to parse input from ast or variables and to ensure validity. """ @classmethod def __init_subclass_with_meta__(cls, **options): _meta = ScalarOptions(cls) super(Scalar, cls).__init_subclass_with_meta__(_meta=_meta, **options) serialize = None parse_value = None parse_literal = None @classmethod def get_type(cls): """ This function is called when the unmounted type (Scalar instance) is mounted (as a Field, InputField or Argument) """ return cls # As per the GraphQL Spec, Integers are only treated as valid when a valid # 32-bit signed integer, providing the broadest support across platforms. # # n.b. JavaScript's integers are safe between -(2^53 - 1) and 2^53 - 1 because # they are internally represented as IEEE 754 doubles. MAX_INT = 2147483647 MIN_INT = -2147483648 class Int(Scalar): """ The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^53 - 1) and 2^53 - 1 since represented in JSON as double-precision floating point numbers specified by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point). """ @staticmethod def coerce_int(value): try: num = int(value) except ValueError: try: num = int(float(value)) except ValueError: return None if MIN_INT <= num <= MAX_INT: return num serialize = coerce_int parse_value = coerce_int @staticmethod def parse_literal(ast): if isinstance(ast, IntValue): num = int(ast.value) if MIN_INT <= num <= MAX_INT: return num class Float(Scalar): """ The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point). """ @staticmethod def coerce_float(value): # type: (Any) -> float try: return float(value) except ValueError: return None serialize = coerce_float parse_value = coerce_float @staticmethod def parse_literal(ast): if isinstance(ast, (FloatValue, IntValue)): return float(ast.value) class String(Scalar): """ The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text. """ @staticmethod def coerce_string(value): if isinstance(value, bool): return u"true" if value else u"false" return six.text_type(value) serialize = coerce_string parse_value = coerce_string @staticmethod def parse_literal(ast): if isinstance(ast, StringValue): return ast.value class Boolean(Scalar): """ The `Boolean` scalar type represents `true` or `false`. """ serialize = bool parse_value = bool @staticmethod def parse_literal(ast): if isinstance(ast, BooleanValue): return ast.value class ID(Scalar): """ The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `"4"`) or integer (such as `4`) input value will be accepted as an ID. """ serialize = str parse_value = str @staticmethod def parse_literal(ast): if isinstance(ast, (StringValue, IntValue)): return ast.value graphene-2.1.9/graphene/types/schema.py000066400000000000000000000152361407436344100201000ustar00rootroot00000000000000import inspect from graphql import GraphQLObjectType, GraphQLSchema, graphql, is_type from graphql.type.directives import ( GraphQLDirective, GraphQLIncludeDirective, GraphQLSkipDirective, ) from graphql.type.introspection import IntrospectionSchema from graphql.utils.introspection_query import introspection_query from graphql.utils.schema_printer import print_schema from .definitions import GrapheneGraphQLType from .objecttype import ObjectType from .typemap import TypeMap, is_graphene_type def assert_valid_root_type(_type): if _type is None: return is_graphene_objecttype = inspect.isclass(_type) and issubclass(_type, ObjectType) is_graphql_objecttype = isinstance(_type, GraphQLObjectType) assert is_graphene_objecttype or is_graphql_objecttype, ( "Type {} is not a valid ObjectType." ).format(_type) class Schema(GraphQLSchema): """ Graphene Schema can execute operations (query, mutation, subscription) against the defined types. For advanced purposes, the schema can be used to lookup type definitions and answer questions about the types through introspection. Args: query (ObjectType): Root query *ObjectType*. Describes entry point for fields to *read* data in your Schema. mutation (ObjectType, optional): Root mutation *ObjectType*. Describes entry point for fields to *create, update or delete* data in your API. subscription (ObjectType, optional): Root subscription *ObjectType*. Describes entry point for fields to receive continuous updates. directives (List[GraphQLDirective], optional): List of custom directives to include in GraphQL schema. Defaults to only include directives definved by GraphQL spec (@include and @skip) [GraphQLIncludeDirective, GraphQLSkipDirective]. types (List[GraphQLType], optional): List of any types to include in schema that may not be introspected through root types. auto_camelcase (bool): Fieldnames will be transformed in Schema's TypeMap from snake_case to camelCase (preferred by GraphQL standard). Default True. """ def __init__( self, query=None, mutation=None, subscription=None, directives=None, types=None, auto_camelcase=True, ): assert_valid_root_type(query) assert_valid_root_type(mutation) assert_valid_root_type(subscription) self._query = query self._mutation = mutation self._subscription = subscription self.types = types self.auto_camelcase = auto_camelcase if directives is None: directives = [GraphQLIncludeDirective, GraphQLSkipDirective] assert all( isinstance(d, GraphQLDirective) for d in directives ), "Schema directives must be List[GraphQLDirective] if provided but got: {}.".format( directives ) self._directives = directives self.build_typemap() def get_query_type(self): return self.get_graphql_type(self._query) def get_mutation_type(self): return self.get_graphql_type(self._mutation) def get_subscription_type(self): return self.get_graphql_type(self._subscription) def __getattr__(self, type_name): """ This function let the developer select a type in a given schema by accessing its attrs. Example: using schema.Query for accessing the "Query" type in the Schema """ _type = super(Schema, self).get_type(type_name) if _type is None: raise AttributeError('Type "{}" not found in the Schema'.format(type_name)) if isinstance(_type, GrapheneGraphQLType): return _type.graphene_type return _type def get_graphql_type(self, _type): if not _type: return _type if is_type(_type): return _type if is_graphene_type(_type): graphql_type = self.get_type(_type._meta.name) assert graphql_type, "Type {} not found in this schema.".format( _type._meta.name ) assert graphql_type.graphene_type == _type return graphql_type raise Exception("{} is not a valid GraphQL type.".format(_type)) def execute(self, *args, **kwargs): """ Use the `graphql` function from `graphql-core` to provide the result for a query string. Most of the time this method will be called by one of the Graphene :ref:`Integrations` via a web request. Args: request_string (str or Document): GraphQL request (query, mutation or subscription) in string or parsed AST form from `graphql-core`. root (Any, optional): Value to use as the parent value object when resolving root types. context (Any, optional): Value to be made avaiable to all resolvers via `info.context`. Can be used to share authorization, dataloaders or other information needed to resolve an operation. variables (dict, optional): If variables are used in the request string, they can be provided in dictionary form mapping the variable name to the variable value. operation_name (str, optional): If mutiple operations are provided in the request_string, an operation name must be provided for the result to be provided. middleware (List[SupportsGraphQLMiddleware]): Supply request level middleware as defined in `graphql-core`. backend (GraphQLCoreBackend, optional): Override the default GraphQLCoreBackend. **execute_options (Any): Depends on backend selected. Default backend has several options such as: validate, allow_subscriptions, return_promise, executor. Returns: :obj:`ExecutionResult` containing any data and errors for the operation. """ return graphql(self, *args, **kwargs) def introspect(self): instrospection = self.execute(introspection_query) if instrospection.errors: raise instrospection.errors[0] return instrospection.data def __str__(self): return print_schema(self) def lazy(self, _type): return lambda: self.get_type(_type) def build_typemap(self): initial_types = [ self._query, self._mutation, self._subscription, IntrospectionSchema, ] if self.types: initial_types += self.types self._type_map = TypeMap( initial_types, auto_camelcase=self.auto_camelcase, schema=self ) graphene-2.1.9/graphene/types/structures.py000066400000000000000000000061531407436344100210610ustar00rootroot00000000000000from .unmountedtype import UnmountedType from .utils import get_type class Structure(UnmountedType): """ A structure is a GraphQL type instance that wraps a main type with certain structure. """ def __init__(self, of_type, *args, **kwargs): super(Structure, self).__init__(*args, **kwargs) if not isinstance(of_type, Structure) and isinstance(of_type, UnmountedType): cls_name = type(self).__name__ of_type_name = type(of_type).__name__ raise Exception( "{} could not have a mounted {}() as inner type. Try with {}({}).".format( cls_name, of_type_name, cls_name, of_type_name ) ) self._of_type = of_type @property def of_type(self): return get_type(self._of_type) def get_type(self): """ This function is called when the unmounted type (List or NonNull instance) is mounted (as a Field, InputField or Argument) """ return self class List(Structure): """ List Modifier A list is a kind of type marker, a wrapping type which points to another type. Lists are often created within the context of defining the fields of an object type. List indicates that many values will be returned (or input) for this field. .. code:: python from graphene import List, String field_name = List(String, description="There will be many values") """ def __str__(self): return "[{}]".format(self.of_type) def __eq__(self, other): return isinstance(other, List) and ( self.of_type == other.of_type and self.args == other.args and self.kwargs == other.kwargs ) class NonNull(Structure): """ Non-Null Modifier A non-null is a kind of type marker, a wrapping type which points to another type. Non-null types enforce that their values are never null and can ensure an error is raised if this ever occurs during a request. It is useful for fields which you can make a strong guarantee on non-nullability, for example usually the id field of a database row will never be null. Note: the enforcement of non-nullability occurs within the executor. NonNull can also be indicated on all Mounted types with the keyword argument ``required``. .. code:: python from graphene import NonNull, String field_name = NonNull(String, description='This field will not be null') another_field = String(required=True, description='This is equivalent to the above') """ def __init__(self, *args, **kwargs): super(NonNull, self).__init__(*args, **kwargs) assert not isinstance(self._of_type, NonNull), ( "Can only create NonNull of a Nullable GraphQLType but got: {}." ).format(self._of_type) def __str__(self): return "{}!".format(self.of_type) def __eq__(self, other): return isinstance(other, NonNull) and ( self.of_type == other.of_type and self.args == other.args and self.kwargs == other.kwargs ) graphene-2.1.9/graphene/types/tests/000077500000000000000000000000001407436344100174215ustar00rootroot00000000000000graphene-2.1.9/graphene/types/tests/__init__.py000066400000000000000000000000001407436344100215200ustar00rootroot00000000000000graphene-2.1.9/graphene/types/tests/test_abstracttype.py000066400000000000000000000020071407436344100235360ustar00rootroot00000000000000from .. import abstracttype from ..abstracttype import AbstractType from ..field import Field from ..objecttype import ObjectType from ..unmountedtype import UnmountedType class MyType(ObjectType): pass class MyScalar(UnmountedType): def get_type(self): return MyType def test_abstract_objecttype_warn_deprecation(mocker): mocker.patch.object(abstracttype, "warn_deprecation") class MyAbstractType(AbstractType): field1 = MyScalar() assert abstracttype.warn_deprecation.called def test_generate_objecttype_inherit_abstracttype(): class MyAbstractType(AbstractType): field1 = MyScalar() class MyObjectType(ObjectType, MyAbstractType): field2 = MyScalar() assert MyObjectType._meta.description is None assert MyObjectType._meta.interfaces == () assert MyObjectType._meta.name == "MyObjectType" assert list(MyObjectType._meta.fields.keys()) == ["field1", "field2"] assert list(map(type, MyObjectType._meta.fields.values())) == [Field, Field] graphene-2.1.9/graphene/types/tests/test_argument.py000066400000000000000000000037031407436344100226570ustar00rootroot00000000000000from functools import partial import pytest from ..argument import Argument, to_arguments from ..field import Field from ..inputfield import InputField from ..scalars import String from ..structures import NonNull def test_argument(): arg = Argument(String, default_value="a", description="desc", name="b") assert arg.type == String assert arg.default_value == "a" assert arg.description == "desc" assert arg.name == "b" def test_argument_comparasion(): arg1 = Argument(String, name="Hey", description="Desc", default_value="default") arg2 = Argument(String, name="Hey", description="Desc", default_value="default") assert arg1 == arg2 assert arg1 != String() def test_argument_required(): arg = Argument(String, required=True) assert arg.type == NonNull(String) def test_to_arguments(): args = {"arg_string": Argument(String), "unmounted_arg": String(required=True)} my_args = to_arguments(args) assert my_args == { "arg_string": Argument(String), "unmounted_arg": Argument(String, required=True), } def test_to_arguments_raises_if_field(): args = {"arg_string": Field(String)} with pytest.raises(ValueError) as exc_info: to_arguments(args) assert str(exc_info.value) == ( "Expected arg_string to be Argument, but received Field. Try using " "Argument(String)." ) def test_to_arguments_raises_if_inputfield(): args = {"arg_string": InputField(String)} with pytest.raises(ValueError) as exc_info: to_arguments(args) assert str(exc_info.value) == ( "Expected arg_string to be Argument, but received InputField. Try " "using Argument(String)." ) def test_argument_with_lazy_type(): MyType = object() arg = Argument(lambda: MyType) assert arg.type == MyType def test_argument_with_lazy_partial_type(): MyType = object() arg = Argument(partial(lambda: MyType)) assert arg.type == MyType graphene-2.1.9/graphene/types/tests/test_base.py000066400000000000000000000032301407436344100217420ustar00rootroot00000000000000from ..base import BaseOptions, BaseType class CustomOptions(BaseOptions): pass class CustomType(BaseType): @classmethod def __init_subclass_with_meta__(cls, **options): _meta = CustomOptions(cls) super(CustomType, cls).__init_subclass_with_meta__(_meta=_meta, **options) def test_basetype(): class MyBaseType(CustomType): pass assert isinstance(MyBaseType._meta, CustomOptions) assert MyBaseType._meta.name == "MyBaseType" assert MyBaseType._meta.description is None def test_basetype_nones(): class MyBaseType(CustomType): """Documentation""" class Meta: name = None description = None assert isinstance(MyBaseType._meta, CustomOptions) assert MyBaseType._meta.name == "MyBaseType" assert MyBaseType._meta.description == "Documentation" def test_basetype_custom(): class MyBaseType(CustomType): """Documentation""" class Meta: name = "Base" description = "Desc" assert isinstance(MyBaseType._meta, CustomOptions) assert MyBaseType._meta.name == "Base" assert MyBaseType._meta.description == "Desc" def test_basetype_create(): MyBaseType = CustomType.create_type("MyBaseType") assert isinstance(MyBaseType._meta, CustomOptions) assert MyBaseType._meta.name == "MyBaseType" assert MyBaseType._meta.description is None def test_basetype_create_extra(): MyBaseType = CustomType.create_type("MyBaseType", name="Base", description="Desc") assert isinstance(MyBaseType._meta, CustomOptions) assert MyBaseType._meta.name == "Base" assert MyBaseType._meta.description == "Desc" graphene-2.1.9/graphene/types/tests/test_datetime.py000066400000000000000000000131661407436344100226350ustar00rootroot00000000000000import datetime import pytz from graphql import GraphQLError import pytest from ..datetime import Date, DateTime, Time from ..objecttype import ObjectType from ..schema import Schema class Query(ObjectType): datetime = DateTime(_in=DateTime(name="in")) date = Date(_in=Date(name="in")) time = Time(_at=Time(name="at")) def resolve_datetime(self, info, _in=None): return _in def resolve_date(self, info, _in=None): return _in def resolve_time(self, info, _at=None): return _at schema = Schema(query=Query) @pytest.fixture def sample_datetime(): utc_datetime = datetime.datetime(2019, 5, 25, 5, 30, 15, 10, pytz.utc) return utc_datetime @pytest.fixture def sample_time(sample_datetime): time = datetime.time( sample_datetime.hour, sample_datetime.minute, sample_datetime.second, sample_datetime.microsecond, sample_datetime.tzinfo, ) return time @pytest.fixture def sample_date(sample_datetime): date = sample_datetime.date() return date def test_datetime_query(sample_datetime): isoformat = sample_datetime.isoformat() result = schema.execute("""{ datetime(in: "%s") }""" % isoformat) assert not result.errors assert result.data == {"datetime": isoformat} def test_date_query(sample_date): isoformat = sample_date.isoformat() result = schema.execute("""{ date(in: "%s") }""" % isoformat) assert not result.errors assert result.data == {"date": isoformat} def test_time_query(sample_time): isoformat = sample_time.isoformat() result = schema.execute("""{ time(at: "%s") }""" % isoformat) assert not result.errors assert result.data == {"time": isoformat} def test_bad_datetime_query(): not_a_date = "Some string that's not a date" result = schema.execute("""{ datetime(in: "%s") }""" % not_a_date) assert len(result.errors) == 1 assert isinstance(result.errors[0], GraphQLError) assert result.data is None def test_bad_date_query(): not_a_date = "Some string that's not a date" result = schema.execute("""{ date(in: "%s") }""" % not_a_date) assert len(result.errors) == 1 assert isinstance(result.errors[0], GraphQLError) assert result.data is None def test_bad_time_query(): not_a_date = "Some string that's not a date" result = schema.execute("""{ time(at: "%s") }""" % not_a_date) assert len(result.errors) == 1 assert isinstance(result.errors[0], GraphQLError) assert result.data is None def test_datetime_query_variable(sample_datetime): isoformat = sample_datetime.isoformat() # test datetime variable provided as Python datetime result = schema.execute( """query Test($date: DateTime){ datetime(in: $date) }""", variables={"date": sample_datetime}, ) assert not result.errors assert result.data == {"datetime": isoformat} # test datetime variable in string representation result = schema.execute( """query Test($date: DateTime){ datetime(in: $date) }""", variables={"date": isoformat}, ) assert not result.errors assert result.data == {"datetime": isoformat} def test_date_query_variable(sample_date): isoformat = sample_date.isoformat() # test date variable provided as Python date result = schema.execute( """query Test($date: Date){ date(in: $date) }""", variables={"date": sample_date}, ) assert not result.errors assert result.data == {"date": isoformat} # test date variable in string representation result = schema.execute( """query Test($date: Date){ date(in: $date) }""", variables={"date": isoformat} ) assert not result.errors assert result.data == {"date": isoformat} def test_time_query_variable(sample_time): isoformat = sample_time.isoformat() # test time variable provided as Python time result = schema.execute( """query Test($time: Time){ time(at: $time) }""", variables={"time": sample_time}, ) assert not result.errors assert result.data == {"time": isoformat} # test time variable in string representation result = schema.execute( """query Test($time: Time){ time(at: $time) }""", variables={"time": isoformat} ) assert not result.errors assert result.data == {"time": isoformat} @pytest.mark.xfail( reason="creating the error message fails when un-parsable object is not JSON serializable." ) def test_bad_variables(sample_date, sample_datetime, sample_time): def _test_bad_variables(type_, input_): result = schema.execute( """query Test($input: {}){{ {}(in: $input) }}""".format( type_, type_.lower() ), variables={"input": input_}, ) assert len(result.errors) == 1 # when `input` is not JSON serializable formatting the error message in # `graphql.utils.is_valid_value` line 79 fails with a TypeError assert isinstance(result.errors[0], GraphQLError) print(result.errors[0]) assert result.data is None not_a_date = dict() not_a_date_str = "Some string that's not a date" today = sample_date now = sample_datetime time = sample_time bad_pairs = [ ("DateTime", not_a_date), ("DateTime", not_a_date_str), ("DateTime", today), ("DateTime", time), ("Date", not_a_date), ("Date", not_a_date_str), ("Date", now), ("Date", time), ("Time", not_a_date), ("Time", not_a_date_str), ("Time", now), ("Time", today), ] for type_, input_ in bad_pairs: _test_bad_variables(type_, input_) graphene-2.1.9/graphene/types/tests/test_decimal.py000066400000000000000000000023141407436344100224300ustar00rootroot00000000000000import decimal from ..decimal import Decimal from ..objecttype import ObjectType from ..schema import Schema class Query(ObjectType): decimal = Decimal(input=Decimal()) def resolve_decimal(self, info, input): return input schema = Schema(query=Query) def test_decimal_string_query(): decimal_value = decimal.Decimal("1969.1974") result = schema.execute("""{ decimal(input: "%s") }""" % decimal_value) assert not result.errors assert result.data == {"decimal": str(decimal_value)} assert decimal.Decimal(result.data["decimal"]) == decimal_value def test_decimal_string_query_variable(): decimal_value = decimal.Decimal("1969.1974") result = schema.execute( """query Test($decimal: Decimal){ decimal(input: $decimal) }""", variables={"decimal": decimal_value}, ) assert not result.errors assert result.data == {"decimal": str(decimal_value)} assert decimal.Decimal(result.data["decimal"]) == decimal_value def test_bad_decimal_query(): not_a_decimal = "Nobody expects the Spanish Inquisition!" result = schema.execute("""{ decimal(input: "%s") }""" % not_a_decimal) assert len(result.errors) == 1 assert result.data is None graphene-2.1.9/graphene/types/tests/test_definition.py000066400000000000000000000200561407436344100231650ustar00rootroot00000000000000from ..argument import Argument from ..enum import Enum from ..field import Field from ..inputfield import InputField from ..inputobjecttype import InputObjectType from ..interface import Interface from ..objecttype import ObjectType from ..scalars import Boolean, Int, String from ..schema import Schema from ..structures import List, NonNull from ..union import Union class Image(ObjectType): url = String() width = Int() height = Int() class Author(ObjectType): id = String() name = String() pic = Field(Image, width=Int(), height=Int()) recent_article = Field(lambda: Article) class Article(ObjectType): id = String() is_published = Boolean() author = Field(Author) title = String() body = String() class Query(ObjectType): article = Field(Article, id=String()) feed = List(Article) class Mutation(ObjectType): write_article = Field(Article) class Subscription(ObjectType): article_subscribe = Field(Article, id=String()) class MyObjectType(ObjectType): pass class MyInterface(Interface): pass class MyUnion(Union): class Meta: types = (Article,) class MyEnum(Enum): foo = "foo" class MyInputObjectType(InputObjectType): pass def test_defines_a_query_only_schema(): blog_schema = Schema(Query) assert blog_schema.get_query_type().graphene_type == Query article_field = Query._meta.fields["article"] assert article_field.type == Article assert article_field.type._meta.name == "Article" article_field_type = article_field.type assert issubclass(article_field_type, ObjectType) title_field = article_field_type._meta.fields["title"] assert title_field.type == String author_field = article_field_type._meta.fields["author"] author_field_type = author_field.type assert issubclass(author_field_type, ObjectType) recent_article_field = author_field_type._meta.fields["recent_article"] assert recent_article_field.type == Article feed_field = Query._meta.fields["feed"] assert feed_field.type.of_type == Article def test_defines_a_mutation_schema(): blog_schema = Schema(Query, mutation=Mutation) assert blog_schema.get_mutation_type().graphene_type == Mutation write_mutation = Mutation._meta.fields["write_article"] assert write_mutation.type == Article assert write_mutation.type._meta.name == "Article" def test_defines_a_subscription_schema(): blog_schema = Schema(Query, subscription=Subscription) assert blog_schema.get_subscription_type().graphene_type == Subscription subscription = Subscription._meta.fields["article_subscribe"] assert subscription.type == Article assert subscription.type._meta.name == "Article" def test_includes_nested_input_objects_in_the_map(): class NestedInputObject(InputObjectType): value = String() class SomeInputObject(InputObjectType): nested = InputField(NestedInputObject) class SomeMutation(Mutation): mutate_something = Field(Article, input=Argument(SomeInputObject)) class SomeSubscription(Mutation): subscribe_to_something = Field(Article, input=Argument(SomeInputObject)) schema = Schema(query=Query, mutation=SomeMutation, subscription=SomeSubscription) assert schema.get_type_map()["NestedInputObject"].graphene_type is NestedInputObject def test_includes_interfaces_thunk_subtypes_in_the_type_map(): class SomeInterface(Interface): f = Int() class SomeSubtype(ObjectType): class Meta: interfaces = (SomeInterface,) class Query(ObjectType): iface = Field(lambda: SomeInterface) schema = Schema(query=Query, types=[SomeSubtype]) assert schema.get_type_map()["SomeSubtype"].graphene_type is SomeSubtype def test_includes_types_in_union(): class SomeType(ObjectType): a = String() class OtherType(ObjectType): b = String() class MyUnion(Union): class Meta: types = (SomeType, OtherType) class Query(ObjectType): union = Field(MyUnion) schema = Schema(query=Query) assert schema.get_type_map()["OtherType"].graphene_type is OtherType assert schema.get_type_map()["SomeType"].graphene_type is SomeType def test_maps_enum(): class SomeType(ObjectType): a = String() class OtherType(ObjectType): b = String() class MyUnion(Union): class Meta: types = (SomeType, OtherType) class Query(ObjectType): union = Field(MyUnion) schema = Schema(query=Query) assert schema.get_type_map()["OtherType"].graphene_type is OtherType assert schema.get_type_map()["SomeType"].graphene_type is SomeType def test_includes_interfaces_subtypes_in_the_type_map(): class SomeInterface(Interface): f = Int() class SomeSubtype(ObjectType): class Meta: interfaces = (SomeInterface,) class Query(ObjectType): iface = Field(SomeInterface) schema = Schema(query=Query, types=[SomeSubtype]) assert schema.get_type_map()["SomeSubtype"].graphene_type is SomeSubtype def test_stringifies_simple_types(): assert str(Int) == "Int" assert str(Article) == "Article" assert str(MyInterface) == "MyInterface" assert str(MyUnion) == "MyUnion" assert str(MyEnum) == "MyEnum" assert str(MyInputObjectType) == "MyInputObjectType" assert str(NonNull(Int)) == "Int!" assert str(List(Int)) == "[Int]" assert str(NonNull(List(Int))) == "[Int]!" assert str(List(NonNull(Int))) == "[Int!]" assert str(List(List(Int))) == "[[Int]]" # def test_identifies_input_types(): # expected = ( # (GraphQLInt, True), # (ObjectType, False), # (InterfaceType, False), # (UnionType, False), # (EnumType, True), # (InputObjectType, True) # ) # for type, answer in expected: # assert is_input_type(type) == answer # assert is_input_type(GraphQLList(type)) == answer # assert is_input_type(GraphQLNonNull(type)) == answer # def test_identifies_output_types(): # expected = ( # (GraphQLInt, True), # (ObjectType, True), # (InterfaceType, True), # (UnionType, True), # (EnumType, True), # (InputObjectType, False) # ) # for type, answer in expected: # assert is_output_type(type) == answer # assert is_output_type(GraphQLList(type)) == answer # assert is_output_type(GraphQLNonNull(type)) == answer # def test_prohibits_nesting_nonnull_inside_nonnull(): # with raises(Exception) as excinfo: # GraphQLNonNull(GraphQLNonNull(GraphQLInt)) # assert 'Can only create NonNull of a Nullable GraphQLType but got: Int!.' in str(excinfo.value) # def test_prohibits_putting_non_object_types_in_unions(): # bad_union_types = [ # GraphQLInt, # GraphQLNonNull(GraphQLInt), # GraphQLList(GraphQLInt), # InterfaceType, # UnionType, # EnumType, # InputObjectType # ] # for x in bad_union_types: # with raises(Exception) as excinfo: # GraphQLSchema( # GraphQLObjectType( # 'Root', # fields={ # 'union': GraphQLField(GraphQLUnionType('BadUnion', [x])) # } # ) # ) # assert 'BadUnion may only contain Object types, it cannot contain: ' + str(x) + '.' \ # == str(excinfo.value) def test_does_not_mutate_passed_field_definitions(): class CommonFields(object): field1 = String() field2 = String(id=String()) class TestObject1(CommonFields, ObjectType): pass class TestObject2(CommonFields, ObjectType): pass assert TestObject1._meta.fields == TestObject2._meta.fields class CommonFields(object): field1 = String() field2 = String() class TestInputObject1(CommonFields, InputObjectType): pass class TestInputObject2(CommonFields, InputObjectType): pass assert TestInputObject1._meta.fields == TestInputObject2._meta.fields graphene-2.1.9/graphene/types/tests/test_dynamic.py000066400000000000000000000017351407436344100224640ustar00rootroot00000000000000from functools import partial from ..dynamic import Dynamic from ..scalars import String from ..structures import List, NonNull def test_dynamic(): dynamic = Dynamic(lambda: String) assert dynamic.get_type() == String assert str(dynamic.get_type()) == "String" def test_nonnull(): dynamic = Dynamic(lambda: NonNull(String)) assert dynamic.get_type().of_type == String assert str(dynamic.get_type()) == "String!" def test_list(): dynamic = Dynamic(lambda: List(String)) assert dynamic.get_type().of_type == String assert str(dynamic.get_type()) == "[String]" def test_list_non_null(): dynamic = Dynamic(lambda: List(NonNull(String))) assert dynamic.get_type().of_type.of_type == String assert str(dynamic.get_type()) == "[String!]" def test_partial(): def __type(_type): return _type dynamic = Dynamic(partial(__type, String)) assert dynamic.get_type() == String assert str(dynamic.get_type()) == "String" graphene-2.1.9/graphene/types/tests/test_enum.py000066400000000000000000000130271407436344100220010ustar00rootroot00000000000000import six from ..argument import Argument from ..enum import Enum, PyEnum from ..field import Field from ..inputfield import InputField from ..schema import ObjectType, Schema def test_enum_construction(): class RGB(Enum): """Description""" RED = 1 GREEN = 2 BLUE = 3 @property def description(self): return "Description {}".format(self.name) assert RGB._meta.name == "RGB" assert RGB._meta.description == "Description" values = RGB._meta.enum.__members__.values() assert sorted([v.name for v in values]) == ["BLUE", "GREEN", "RED"] assert sorted([v.description for v in values]) == [ "Description BLUE", "Description GREEN", "Description RED", ] def test_enum_construction_meta(): class RGB(Enum): class Meta: name = "RGBEnum" description = "Description" RED = 1 GREEN = 2 BLUE = 3 assert RGB._meta.name == "RGBEnum" assert RGB._meta.description == "Description" def test_enum_instance_construction(): RGB = Enum("RGB", "RED,GREEN,BLUE") values = RGB._meta.enum.__members__.values() assert sorted([v.name for v in values]) == ["BLUE", "GREEN", "RED"] def test_enum_from_builtin_enum(): PyRGB = PyEnum("RGB", "RED,GREEN,BLUE") RGB = Enum.from_enum(PyRGB) assert RGB._meta.enum == PyRGB assert RGB.RED assert RGB.GREEN assert RGB.BLUE def test_enum_from_builtin_enum_accepts_lambda_description(): def custom_description(value): if not value: return "StarWars Episodes" return "New Hope Episode" if value == Episode.NEWHOPE else "Other" def custom_deprecation_reason(value): return "meh" if value == Episode.NEWHOPE else None PyEpisode = PyEnum("PyEpisode", "NEWHOPE,EMPIRE,JEDI") Episode = Enum.from_enum( PyEpisode, description=custom_description, deprecation_reason=custom_deprecation_reason, ) class Query(ObjectType): foo = Episode() schema = Schema(query=Query) GraphQLPyEpisode = schema._type_map["PyEpisode"].values assert schema._type_map["PyEpisode"].description == "StarWars Episodes" assert ( GraphQLPyEpisode[0].name == "NEWHOPE" and GraphQLPyEpisode[0].description == "New Hope Episode" ) assert ( GraphQLPyEpisode[1].name == "EMPIRE" and GraphQLPyEpisode[1].description == "Other" ) assert ( GraphQLPyEpisode[2].name == "JEDI" and GraphQLPyEpisode[2].description == "Other" ) assert ( GraphQLPyEpisode[0].name == "NEWHOPE" and GraphQLPyEpisode[0].deprecation_reason == "meh" ) assert ( GraphQLPyEpisode[1].name == "EMPIRE" and GraphQLPyEpisode[1].deprecation_reason is None ) assert ( GraphQLPyEpisode[2].name == "JEDI" and GraphQLPyEpisode[2].deprecation_reason is None ) def test_enum_from_python3_enum_uses_enum_doc(): if not six.PY3: return from enum import Enum as PyEnum class Color(PyEnum): """This is the description""" RED = 1 GREEN = 2 BLUE = 3 RGB = Enum.from_enum(Color) assert RGB._meta.enum == Color assert RGB._meta.description == "This is the description" assert RGB assert RGB.RED assert RGB.GREEN assert RGB.BLUE def test_enum_value_from_class(): class RGB(Enum): RED = 1 GREEN = 2 BLUE = 3 assert RGB.RED.value == 1 assert RGB.GREEN.value == 2 assert RGB.BLUE.value == 3 def test_enum_value_as_unmounted_field(): class RGB(Enum): RED = 1 GREEN = 2 BLUE = 3 unmounted = RGB() unmounted_field = unmounted.Field() assert isinstance(unmounted_field, Field) assert unmounted_field.type == RGB def test_enum_value_as_unmounted_inputfield(): class RGB(Enum): RED = 1 GREEN = 2 BLUE = 3 unmounted = RGB() unmounted_field = unmounted.InputField() assert isinstance(unmounted_field, InputField) assert unmounted_field.type == RGB def test_enum_value_as_unmounted_argument(): class RGB(Enum): RED = 1 GREEN = 2 BLUE = 3 unmounted = RGB() unmounted_field = unmounted.Argument() assert isinstance(unmounted_field, Argument) assert unmounted_field.type == RGB def test_enum_can_be_compared(): class RGB(Enum): RED = 1 GREEN = 2 BLUE = 3 assert RGB.RED == 1 assert RGB.GREEN == 2 assert RGB.BLUE == 3 def test_enum_can_be_initialzied(): class RGB(Enum): RED = 1 GREEN = 2 BLUE = 3 assert RGB.get(1) == RGB.RED assert RGB.get(2) == RGB.GREEN assert RGB.get(3) == RGB.BLUE def test_enum_can_retrieve_members(): class RGB(Enum): RED = 1 GREEN = 2 BLUE = 3 assert RGB["RED"] == RGB.RED assert RGB["GREEN"] == RGB.GREEN assert RGB["BLUE"] == RGB.BLUE def test_enum_to_enum_comparison_should_differ(): class RGB1(Enum): RED = 1 GREEN = 2 BLUE = 3 class RGB2(Enum): RED = 1 GREEN = 2 BLUE = 3 assert RGB1.RED != RGB2.RED assert RGB1.GREEN != RGB2.GREEN assert RGB1.BLUE != RGB2.BLUE def test_enum_skip_meta_from_members(): class RGB1(Enum): class Meta: name = "RGB" RED = 1 GREEN = 2 BLUE = 3 assert dict(RGB1._meta.enum.__members__) == { "RED": RGB1.RED, "GREEN": RGB1.GREEN, "BLUE": RGB1.BLUE, } graphene-2.1.9/graphene/types/tests/test_field.py000066400000000000000000000071371407436344100221250ustar00rootroot00000000000000from functools import partial import pytest from ..argument import Argument from ..field import Field from ..scalars import String from ..structures import NonNull from .utils import MyLazyType class MyInstance(object): value = "value" value_func = staticmethod(lambda: "value_func") def value_method(self): return "value_method" def test_field_basic(): MyType = object() args = {"my arg": Argument(True)} def resolver(): return None deprecation_reason = "Deprecated now" description = "My Field" my_default = "something" field = Field( MyType, name="name", args=args, resolver=resolver, description=description, deprecation_reason=deprecation_reason, default_value=my_default, ) assert field.name == "name" assert field.args == args assert field.resolver == resolver assert field.deprecation_reason == deprecation_reason assert field.description == description assert field.default_value == my_default def test_field_required(): MyType = object() field = Field(MyType, required=True) assert isinstance(field.type, NonNull) assert field.type.of_type == MyType def test_field_default_value_not_callable(): MyType = object() try: Field(MyType, default_value=lambda: True) except AssertionError as e: # substring comparison for py 2/3 compatibility assert "The default value can not be a function but received" in str(e) def test_field_source(): MyType = object() field = Field(MyType, source="value") assert field.resolver(MyInstance(), None) == MyInstance.value def test_field_with_lazy_type(): MyType = object() field = Field(lambda: MyType) assert field.type == MyType def test_field_with_lazy_partial_type(): MyType = object() field = Field(partial(lambda: MyType)) assert field.type == MyType def test_field_with_string_type(): field = Field("graphene.types.tests.utils.MyLazyType") assert field.type == MyLazyType def test_field_not_source_and_resolver(): MyType = object() with pytest.raises(Exception) as exc_info: Field(MyType, source="value", resolver=lambda: None) assert ( str(exc_info.value) == "A Field cannot have a source and a resolver in at the same time." ) def test_field_source_func(): MyType = object() field = Field(MyType, source="value_func") assert field.resolver(MyInstance(), None) == MyInstance.value_func() def test_field_source_method(): MyType = object() field = Field(MyType, source="value_method") assert field.resolver(MyInstance(), None) == MyInstance().value_method() def test_field_source_as_argument(): MyType = object() field = Field(MyType, source=String()) assert "source" in field.args assert field.args["source"].type == String def test_field_name_as_argument(): MyType = object() field = Field(MyType, name=String()) assert "name" in field.args assert field.args["name"].type == String def test_field_source_argument_as_kw(): MyType = object() field = Field(MyType, b=NonNull(True), c=Argument(None), a=NonNull(False)) assert list(field.args.keys()) == ["b", "c", "a"] assert isinstance(field.args["b"], Argument) assert isinstance(field.args["b"].type, NonNull) assert field.args["b"].type.of_type is True assert isinstance(field.args["c"], Argument) assert field.args["c"].type is None assert isinstance(field.args["a"], Argument) assert isinstance(field.args["a"].type, NonNull) assert field.args["a"].type.of_type is False graphene-2.1.9/graphene/types/tests/test_generic.py000066400000000000000000000042621407436344100224520ustar00rootroot00000000000000from ..generic import GenericScalar from ..objecttype import ObjectType from ..schema import Schema class Query(ObjectType): generic = GenericScalar(input=GenericScalar()) def resolve_generic(self, info, input=None): return input schema = Schema(query=Query) def test_generic_query_variable(): for generic_value in [ 1, 1.1, True, "str", [1, 2, 3], [1.1, 2.2, 3.3], [True, False], ["str1", "str2"], {"key_a": "a", "key_b": "b"}, { "int": 1, "float": 1.1, "boolean": True, "string": "str", "int_list": [1, 2, 3], "float_list": [1.1, 2.2, 3.3], "boolean_list": [True, False], "string_list": ["str1", "str2"], "nested_dict": {"key_a": "a", "key_b": "b"}, }, None, ]: result = schema.execute( """query Test($generic: GenericScalar){ generic(input: $generic) }""", variables={"generic": generic_value}, ) assert not result.errors assert result.data == {"generic": generic_value} def test_generic_parse_literal_query(): result = schema.execute( """ query { generic(input: { int: 1, float: 1.1 boolean: true, string: "str", int_list: [1, 2, 3], float_list: [1.1, 2.2, 3.3], boolean_list: [true, false] string_list: ["str1", "str2"], nested_dict: { key_a: "a", key_b: "b" }, empty_key: undefined }) } """ ) assert not result.errors assert result.data == { "generic": { "int": 1, "float": 1.1, "boolean": True, "string": "str", "int_list": [1, 2, 3], "float_list": [1.1, 2.2, 3.3], "boolean_list": [True, False], "string_list": ["str1", "str2"], "nested_dict": {"key_a": "a", "key_b": "b"}, "empty_key": None, } } graphene-2.1.9/graphene/types/tests/test_inputfield.py000066400000000000000000000013441407436344100231770ustar00rootroot00000000000000from functools import partial from ..inputfield import InputField from ..structures import NonNull from .utils import MyLazyType def test_inputfield_required(): MyType = object() field = InputField(MyType, required=True) assert isinstance(field.type, NonNull) assert field.type.of_type == MyType def test_inputfield_with_lazy_type(): MyType = object() field = InputField(lambda: MyType) assert field.type == MyType def test_inputfield_with_lazy_partial_type(): MyType = object() field = InputField(partial(lambda: MyType)) assert field.type == MyType def test_inputfield_with_string_type(): field = InputField("graphene.types.tests.utils.MyLazyType") assert field.type == MyLazyType graphene-2.1.9/graphene/types/tests/test_inputobjecttype.py000066400000000000000000000076411407436344100242720ustar00rootroot00000000000000from ..argument import Argument from ..field import Field from ..inputfield import InputField from ..inputobjecttype import InputObjectType from ..objecttype import ObjectType from ..scalars import Boolean, String from ..schema import Schema from ..unmountedtype import UnmountedType class MyType(object): pass class MyScalar(UnmountedType): def get_type(self): return MyType def test_generate_inputobjecttype(): class MyInputObjectType(InputObjectType): """Documentation""" assert MyInputObjectType._meta.name == "MyInputObjectType" assert MyInputObjectType._meta.description == "Documentation" assert MyInputObjectType._meta.fields == {} def test_generate_inputobjecttype_with_meta(): class MyInputObjectType(InputObjectType): class Meta: name = "MyOtherInputObjectType" description = "Documentation" assert MyInputObjectType._meta.name == "MyOtherInputObjectType" assert MyInputObjectType._meta.description == "Documentation" def test_generate_inputobjecttype_with_fields(): class MyInputObjectType(InputObjectType): field = Field(MyType) assert "field" in MyInputObjectType._meta.fields def test_ordered_fields_in_inputobjecttype(): class MyInputObjectType(InputObjectType): b = InputField(MyType) a = InputField(MyType) field = MyScalar() asa = InputField(MyType) assert list(MyInputObjectType._meta.fields.keys()) == ["b", "a", "field", "asa"] def test_generate_inputobjecttype_unmountedtype(): class MyInputObjectType(InputObjectType): field = MyScalar(MyType) assert "field" in MyInputObjectType._meta.fields assert isinstance(MyInputObjectType._meta.fields["field"], InputField) def test_generate_inputobjecttype_as_argument(): class MyInputObjectType(InputObjectType): field = MyScalar() class MyObjectType(ObjectType): field = Field(MyType, input=MyInputObjectType()) assert "field" in MyObjectType._meta.fields field = MyObjectType._meta.fields["field"] assert isinstance(field, Field) assert field.type == MyType assert "input" in field.args assert isinstance(field.args["input"], Argument) assert field.args["input"].type == MyInputObjectType def test_generate_inputobjecttype_inherit_abstracttype(): class MyAbstractType(object): field1 = MyScalar(MyType) class MyInputObjectType(InputObjectType, MyAbstractType): field2 = MyScalar(MyType) assert list(MyInputObjectType._meta.fields.keys()) == ["field1", "field2"] assert [type(x) for x in MyInputObjectType._meta.fields.values()] == [ InputField, InputField, ] def test_generate_inputobjecttype_inherit_abstracttype_reversed(): class MyAbstractType(object): field1 = MyScalar(MyType) class MyInputObjectType(MyAbstractType, InputObjectType): field2 = MyScalar(MyType) assert list(MyInputObjectType._meta.fields.keys()) == ["field1", "field2"] assert [type(x) for x in MyInputObjectType._meta.fields.values()] == [ InputField, InputField, ] def test_inputobjecttype_of_input(): class Child(InputObjectType): first_name = String() last_name = String() @property def full_name(self): return "{} {}".format(self.first_name, self.last_name) class Parent(InputObjectType): child = InputField(Child) class Query(ObjectType): is_child = Boolean(parent=Parent()) def resolve_is_child(self, info, parent): return ( isinstance(parent.child, Child) and parent.child.full_name == "Peter Griffin" ) schema = Schema(query=Query) result = schema.execute( """query basequery { isChild(parent: {child: {firstName: "Peter", lastName: "Griffin"}}) } """ ) assert not result.errors assert result.data == {"isChild": True} graphene-2.1.9/graphene/types/tests/test_interface.py000066400000000000000000000047051407436344100230000ustar00rootroot00000000000000from ..field import Field from ..interface import Interface from ..unmountedtype import UnmountedType class MyType(object): pass class MyScalar(UnmountedType): def get_type(self): return MyType def test_generate_interface(): class MyInterface(Interface): """Documentation""" assert MyInterface._meta.name == "MyInterface" assert MyInterface._meta.description == "Documentation" assert MyInterface._meta.fields == {} def test_generate_interface_with_meta(): class MyInterface(Interface): class Meta: name = "MyOtherInterface" description = "Documentation" assert MyInterface._meta.name == "MyOtherInterface" assert MyInterface._meta.description == "Documentation" def test_generate_interface_with_fields(): class MyInterface(Interface): field = Field(MyType) assert "field" in MyInterface._meta.fields def test_ordered_fields_in_interface(): class MyInterface(Interface): b = Field(MyType) a = Field(MyType) field = MyScalar() asa = Field(MyType) assert list(MyInterface._meta.fields.keys()) == ["b", "a", "field", "asa"] def test_generate_interface_unmountedtype(): class MyInterface(Interface): field = MyScalar() assert "field" in MyInterface._meta.fields assert isinstance(MyInterface._meta.fields["field"], Field) def test_generate_interface_inherit_abstracttype(): class MyAbstractType(object): field1 = MyScalar() class MyInterface(Interface, MyAbstractType): field2 = MyScalar() assert list(MyInterface._meta.fields.keys()) == ["field1", "field2"] assert [type(x) for x in MyInterface._meta.fields.values()] == [Field, Field] def test_generate_interface_inherit_interface(): class MyBaseInterface(Interface): field1 = MyScalar() class MyInterface(MyBaseInterface): field2 = MyScalar() assert MyInterface._meta.name == "MyInterface" assert list(MyInterface._meta.fields.keys()) == ["field1", "field2"] assert [type(x) for x in MyInterface._meta.fields.values()] == [Field, Field] def test_generate_interface_inherit_abstracttype_reversed(): class MyAbstractType(object): field1 = MyScalar() class MyInterface(MyAbstractType, Interface): field2 = MyScalar() assert list(MyInterface._meta.fields.keys()) == ["field1", "field2"] assert [type(x) for x in MyInterface._meta.fields.values()] == [Field, Field] graphene-2.1.9/graphene/types/tests/test_json.py000066400000000000000000000014741407436344100220110ustar00rootroot00000000000000from ..json import JSONString from ..objecttype import ObjectType from ..schema import Schema class Query(ObjectType): json = JSONString(input=JSONString()) def resolve_json(self, info, input): return input schema = Schema(query=Query) def test_jsonstring_query(): json_value = '{"key": "value"}' json_value_quoted = json_value.replace('"', '\\"') result = schema.execute("""{ json(input: "%s") }""" % json_value_quoted) assert not result.errors assert result.data == {"json": json_value} def test_jsonstring_query_variable(): json_value = '{"key": "value"}' result = schema.execute( """query Test($json: JSONString){ json(input: $json) }""", variables={"json": json_value}, ) assert not result.errors assert result.data == {"json": json_value} graphene-2.1.9/graphene/types/tests/test_mountedtype.py000066400000000000000000000012121407436344100234030ustar00rootroot00000000000000from ..field import Field from ..scalars import String class CustomField(Field): def __init__(self, *args, **kwargs): self.metadata = kwargs.pop("metadata", None) super(CustomField, self).__init__(*args, **kwargs) def test_mounted_type(): unmounted = String() mounted = Field.mounted(unmounted) assert isinstance(mounted, Field) assert mounted.type == String def test_mounted_type_custom(): unmounted = String(metadata={"hey": "yo!"}) mounted = CustomField.mounted(unmounted) assert isinstance(mounted, CustomField) assert mounted.type == String assert mounted.metadata == {"hey": "yo!"} graphene-2.1.9/graphene/types/tests/test_mutation.py000066400000000000000000000131071407436344100226740ustar00rootroot00000000000000import pytest from ..argument import Argument from ..dynamic import Dynamic from ..mutation import Mutation from ..objecttype import ObjectType from ..scalars import String from ..schema import Schema from ..structures import NonNull from ..interface import Interface class MyType(Interface): pass def test_generate_mutation_no_args(): class MyMutation(Mutation): """Documentation""" def mutate(self, info, **args): return args assert issubclass(MyMutation, ObjectType) assert MyMutation._meta.name == "MyMutation" assert MyMutation._meta.description == "Documentation" resolved = MyMutation.Field().resolver(None, None, name="Peter") assert resolved == {"name": "Peter"} def test_generate_mutation_with_meta(): class MyMutation(Mutation): class Meta: name = "MyOtherMutation" description = "Documentation" interfaces = (MyType,) def mutate(self, info, **args): return args assert MyMutation._meta.name == "MyOtherMutation" assert MyMutation._meta.description == "Documentation" assert MyMutation._meta.interfaces == (MyType,) resolved = MyMutation.Field().resolver(None, None, name="Peter") assert resolved == {"name": "Peter"} def test_mutation_raises_exception_if_no_mutate(): with pytest.raises(AssertionError) as excinfo: class MyMutation(Mutation): pass assert "All mutations must define a mutate method in it" == str(excinfo.value) def test_mutation_custom_output_type(): class User(ObjectType): name = String() class CreateUser(Mutation): class Arguments: name = String() Output = User def mutate(self, info, name): return User(name=name) field = CreateUser.Field() assert field.type == User assert field.args == {"name": Argument(String)} resolved = field.resolver(None, None, name="Peter") assert isinstance(resolved, User) assert resolved.name == "Peter" def test_mutation_execution(): class CreateUser(Mutation): class Arguments: name = String() dynamic = Dynamic(lambda: String()) dynamic_none = Dynamic(lambda: None) name = String() dynamic = Dynamic(lambda: String()) def mutate(self, info, name, dynamic): return CreateUser(name=name, dynamic=dynamic) class Query(ObjectType): a = String() class MyMutation(ObjectType): create_user = CreateUser.Field() schema = Schema(query=Query, mutation=MyMutation) result = schema.execute( """ mutation mymutation { createUser(name:"Peter", dynamic: "dynamic") { name dynamic } } """ ) assert not result.errors assert result.data == {"createUser": {"name": "Peter", "dynamic": "dynamic"}} def test_mutation_no_fields_output(): class CreateUser(Mutation): name = String() def mutate(self, info): return CreateUser() class Query(ObjectType): a = String() class MyMutation(ObjectType): create_user = CreateUser.Field() schema = Schema(query=Query, mutation=MyMutation) result = schema.execute( """ mutation mymutation { createUser { name } } """ ) assert not result.errors assert result.data == {"createUser": {"name": None}} def test_mutation_allow_to_have_custom_args(): class CreateUser(Mutation): class Arguments: name = String() name = String() def mutate(self, info, name): return CreateUser(name=name) class MyMutation(ObjectType): create_user = CreateUser.Field( name="createUser", description="Create a user", deprecation_reason="Is deprecated", required=True, ) field = MyMutation._meta.fields["create_user"] assert field.name == "createUser" assert field.description == "Create a user" assert field.deprecation_reason == "Is deprecated" assert field.type == NonNull(CreateUser) def test_mutation_default_args_output(): class CreateUser(Mutation): """Description.""" class Arguments: name = String() name = String() def mutate(self, info, name): return CreateUser(name=name) class MyMutation(ObjectType): create_user = CreateUser.Field() field = MyMutation._meta.fields["create_user"] assert field.name is None assert field.description == "Description." assert field.deprecation_reason is None assert field.type == CreateUser def test_mutation_as_subclass(): class BaseCreateUser(Mutation): class Arguments: name = String() name = String() def mutate(self, info, **args): return args class CreateUserWithPlanet(BaseCreateUser): class Arguments(BaseCreateUser.Arguments): planet = String() planet = String() def mutate(self, info, **args): return CreateUserWithPlanet(**args) class MyMutation(ObjectType): create_user_with_planet = CreateUserWithPlanet.Field() class Query(ObjectType): a = String() schema = Schema(query=Query, mutation=MyMutation) result = schema.execute( """ mutation mymutation { createUserWithPlanet(name:"Peter", planet: "earth") { name planet } } """ ) assert not result.errors assert result.data == {"createUserWithPlanet": {"name": "Peter", "planet": "earth"}} graphene-2.1.9/graphene/types/tests/test_objecttype.py000066400000000000000000000156651407436344100232170ustar00rootroot00000000000000import pytest from ..field import Field from ..interface import Interface from ..objecttype import ObjectType from ..scalars import String from ..schema import Schema from ..structures import NonNull from ..unmountedtype import UnmountedType class MyType(Interface): pass class Container(ObjectType): field1 = Field(MyType) field2 = Field(MyType) class MyInterface(Interface): ifield = Field(MyType) class ContainerWithInterface(ObjectType): class Meta: interfaces = (MyInterface,) field1 = Field(MyType) field2 = Field(MyType) class MyScalar(UnmountedType): def get_type(self): return MyType def test_generate_objecttype(): class MyObjectType(ObjectType): """Documentation""" assert MyObjectType._meta.name == "MyObjectType" assert MyObjectType._meta.description == "Documentation" assert MyObjectType._meta.interfaces == tuple() assert MyObjectType._meta.fields == {} assert ( repr(MyObjectType) == ">" ) def test_generate_objecttype_with_meta(): class MyObjectType(ObjectType): class Meta: name = "MyOtherObjectType" description = "Documentation" interfaces = (MyType,) assert MyObjectType._meta.name == "MyOtherObjectType" assert MyObjectType._meta.description == "Documentation" assert MyObjectType._meta.interfaces == (MyType,) def test_generate_lazy_objecttype(): class MyObjectType(ObjectType): example = Field(lambda: InnerObjectType, required=True) class InnerObjectType(ObjectType): field = Field(MyType) assert MyObjectType._meta.name == "MyObjectType" example_field = MyObjectType._meta.fields["example"] assert isinstance(example_field.type, NonNull) assert example_field.type.of_type == InnerObjectType def test_generate_objecttype_with_fields(): class MyObjectType(ObjectType): field = Field(MyType) assert "field" in MyObjectType._meta.fields def test_generate_objecttype_with_private_attributes(): class MyObjectType(ObjectType): _private_state = None assert "_private_state" not in MyObjectType._meta.fields assert hasattr(MyObjectType, "_private_state") m = MyObjectType(_private_state="custom") assert m._private_state == "custom" with pytest.raises(TypeError): MyObjectType(_other_private_state="Wrong") def test_ordered_fields_in_objecttype(): class MyObjectType(ObjectType): b = Field(MyType) a = Field(MyType) field = MyScalar() asa = Field(MyType) assert list(MyObjectType._meta.fields.keys()) == ["b", "a", "field", "asa"] def test_generate_objecttype_inherit_abstracttype(): class MyAbstractType(object): field1 = MyScalar() class MyObjectType(ObjectType, MyAbstractType): field2 = MyScalar() assert MyObjectType._meta.description is None assert MyObjectType._meta.interfaces == () assert MyObjectType._meta.name == "MyObjectType" assert list(MyObjectType._meta.fields.keys()) == ["field1", "field2"] assert list(map(type, MyObjectType._meta.fields.values())) == [Field, Field] def test_generate_objecttype_inherit_abstracttype_reversed(): class MyAbstractType(object): field1 = MyScalar() class MyObjectType(MyAbstractType, ObjectType): field2 = MyScalar() assert MyObjectType._meta.description is None assert MyObjectType._meta.interfaces == () assert MyObjectType._meta.name == "MyObjectType" assert list(MyObjectType._meta.fields.keys()) == ["field1", "field2"] assert list(map(type, MyObjectType._meta.fields.values())) == [Field, Field] def test_generate_objecttype_unmountedtype(): class MyObjectType(ObjectType): field = MyScalar() assert "field" in MyObjectType._meta.fields assert isinstance(MyObjectType._meta.fields["field"], Field) def test_parent_container_get_fields(): assert list(Container._meta.fields.keys()) == ["field1", "field2"] def test_parent_container_interface_get_fields(): assert list(ContainerWithInterface._meta.fields.keys()) == [ "ifield", "field1", "field2", ] def test_objecttype_as_container_only_args(): container = Container("1", "2") assert container.field1 == "1" assert container.field2 == "2" def test_objecttype_as_container_args_kwargs(): container = Container("1", field2="2") assert container.field1 == "1" assert container.field2 == "2" def test_objecttype_as_container_few_kwargs(): container = Container(field2="2") assert container.field2 == "2" def test_objecttype_as_container_all_kwargs(): container = Container(field1="1", field2="2") assert container.field1 == "1" assert container.field2 == "2" def test_objecttype_as_container_extra_args(): with pytest.raises(IndexError) as excinfo: Container("1", "2", "3") assert "Number of args exceeds number of fields" == str(excinfo.value) def test_objecttype_as_container_invalid_kwargs(): with pytest.raises(TypeError) as excinfo: Container(unexisting_field="3") assert "'unexisting_field' is an invalid keyword argument for Container" == str( excinfo.value ) def test_objecttype_container_benchmark(benchmark): @benchmark def create_objecttype(): Container(field1="field1", field2="field2") def test_generate_objecttype_description(): class MyObjectType(ObjectType): """ Documentation Documentation line 2 """ assert MyObjectType._meta.description == "Documentation\n\nDocumentation line 2" def test_objecttype_with_possible_types(): class MyObjectType(ObjectType): class Meta: possible_types = (dict,) assert MyObjectType._meta.possible_types == (dict,) def test_objecttype_with_possible_types_and_is_type_of_should_raise(): with pytest.raises(AssertionError) as excinfo: class MyObjectType(ObjectType): class Meta: possible_types = (dict,) @classmethod def is_type_of(cls, root, context, info): return False assert str(excinfo.value) == ( "MyObjectType.Meta.possible_types will cause type collision with " "MyObjectType.is_type_of. Please use one or other." ) def test_objecttype_no_fields_output(): class User(ObjectType): name = String() class Query(ObjectType): user = Field(User) def resolve_user(self, info): return User() schema = Schema(query=Query) result = schema.execute( """ query basequery { user { name } } """ ) assert not result.errors assert result.data == {"user": {"name": None}} def test_abstract_objecttype_can_str(): class MyObjectType(ObjectType): class Meta: abstract = True field = MyScalar() assert str(MyObjectType) == "MyObjectType" graphene-2.1.9/graphene/types/tests/test_query.py000066400000000000000000000321631407436344100222040ustar00rootroot00000000000000import json from functools import partial from graphql import GraphQLError, ResolveInfo, Source, execute, parse from ..context import Context from ..dynamic import Dynamic from ..field import Field from ..inputfield import InputField from ..inputobjecttype import InputObjectType from ..interface import Interface from ..objecttype import ObjectType from ..scalars import Boolean, Int, String from ..schema import Schema from ..structures import List, NonNull from ..union import Union def test_query(): class Query(ObjectType): hello = String(resolver=lambda *_: "World") hello_schema = Schema(Query) executed = hello_schema.execute("{ hello }") assert not executed.errors assert executed.data == {"hello": "World"} def test_query_source(): class Root(object): _hello = "World" def hello(self): return self._hello class Query(ObjectType): hello = String(source="hello") hello_schema = Schema(Query) executed = hello_schema.execute("{ hello }", Root()) assert not executed.errors assert executed.data == {"hello": "World"} def test_query_union(): class one_object(object): pass class two_object(object): pass class One(ObjectType): one = String() @classmethod def is_type_of(cls, root, info): return isinstance(root, one_object) class Two(ObjectType): two = String() @classmethod def is_type_of(cls, root, info): return isinstance(root, two_object) class MyUnion(Union): class Meta: types = (One, Two) class Query(ObjectType): unions = List(MyUnion) def resolve_unions(self, info): return [one_object(), two_object()] hello_schema = Schema(Query) executed = hello_schema.execute("{ unions { __typename } }") assert not executed.errors assert executed.data == {"unions": [{"__typename": "One"}, {"__typename": "Two"}]} def test_query_interface(): class one_object(object): pass class two_object(object): pass class MyInterface(Interface): base = String() class One(ObjectType): class Meta: interfaces = (MyInterface,) one = String() @classmethod def is_type_of(cls, root, info): return isinstance(root, one_object) class Two(ObjectType): class Meta: interfaces = (MyInterface,) two = String() @classmethod def is_type_of(cls, root, info): return isinstance(root, two_object) class Query(ObjectType): interfaces = List(MyInterface) def resolve_interfaces(self, info): return [one_object(), two_object()] hello_schema = Schema(Query, types=[One, Two]) executed = hello_schema.execute("{ interfaces { __typename } }") assert not executed.errors assert executed.data == { "interfaces": [{"__typename": "One"}, {"__typename": "Two"}] } def test_query_dynamic(): class Query(ObjectType): hello = Dynamic(lambda: String(resolver=lambda *_: "World")) hellos = Dynamic(lambda: List(String, resolver=lambda *_: ["Worlds"])) hello_field = Dynamic(lambda: Field(String, resolver=lambda *_: "Field World")) hello_schema = Schema(Query) executed = hello_schema.execute("{ hello hellos helloField }") assert not executed.errors assert executed.data == { "hello": "World", "hellos": ["Worlds"], "helloField": "Field World", } def test_query_default_value(): class MyType(ObjectType): field = String() class Query(ObjectType): hello = Field(MyType, default_value=MyType(field="something else!")) hello_schema = Schema(Query) executed = hello_schema.execute("{ hello { field } }") assert not executed.errors assert executed.data == {"hello": {"field": "something else!"}} def test_query_wrong_default_value(): class MyType(ObjectType): field = String() @classmethod def is_type_of(cls, root, info): return isinstance(root, MyType) class Query(ObjectType): hello = Field(MyType, default_value="hello") hello_schema = Schema(Query) executed = hello_schema.execute("{ hello { field } }") assert len(executed.errors) == 1 assert ( executed.errors[0].message == GraphQLError('Expected value of type "MyType" but got: str.').message ) assert executed.data == {"hello": None} def test_query_default_value_ignored_by_resolver(): class MyType(ObjectType): field = String() class Query(ObjectType): hello = Field( MyType, default_value="hello", resolver=lambda *_: MyType(field="no default."), ) hello_schema = Schema(Query) executed = hello_schema.execute("{ hello { field } }") assert not executed.errors assert executed.data == {"hello": {"field": "no default."}} def test_query_resolve_function(): class Query(ObjectType): hello = String() def resolve_hello(self, info): return "World" hello_schema = Schema(Query) executed = hello_schema.execute("{ hello }") assert not executed.errors assert executed.data == {"hello": "World"} def test_query_arguments(): class Query(ObjectType): test = String(a_str=String(), a_int=Int()) def resolve_test(self, info, **args): return json.dumps([self, args], separators=(",", ":")) test_schema = Schema(Query) result = test_schema.execute("{ test }", None) assert not result.errors assert result.data == {"test": "[null,{}]"} result = test_schema.execute('{ test(aStr: "String!") }', "Source!") assert not result.errors assert result.data == {"test": '["Source!",{"a_str":"String!"}]'} result = test_schema.execute('{ test(aInt: -123, aStr: "String!") }', "Source!") assert not result.errors assert result.data in [ {"test": '["Source!",{"a_str":"String!","a_int":-123}]'}, {"test": '["Source!",{"a_int":-123,"a_str":"String!"}]'}, ] def test_query_input_field(): class Input(InputObjectType): a_field = String() recursive_field = InputField(lambda: Input) class Query(ObjectType): test = String(a_input=Input()) def resolve_test(self, info, **args): return json.dumps([self, args], separators=(",", ":")) test_schema = Schema(Query) result = test_schema.execute("{ test }", None) assert not result.errors assert result.data == {"test": "[null,{}]"} result = test_schema.execute('{ test(aInput: {aField: "String!"} ) }', "Source!") assert not result.errors assert result.data == {"test": '["Source!",{"a_input":{"a_field":"String!"}}]'} result = test_schema.execute( '{ test(aInput: {recursiveField: {aField: "String!"}}) }', "Source!" ) assert not result.errors assert result.data == { "test": '["Source!",{"a_input":{"recursive_field":{"a_field":"String!"}}}]' } def test_query_middlewares(): class Query(ObjectType): hello = String() other = String() def resolve_hello(self, info): return "World" def resolve_other(self, info): return "other" def reversed_middleware(next, *args, **kwargs): p = next(*args, **kwargs) return p.then(lambda x: x[::-1]) hello_schema = Schema(Query) executed = hello_schema.execute( "{ hello, other }", middleware=[reversed_middleware] ) assert not executed.errors assert executed.data == {"hello": "dlroW", "other": "rehto"} def test_objecttype_on_instances(): class Ship: def __init__(self, name): self.name = name class ShipType(ObjectType): name = String(description="Ship name", required=True) def resolve_name(self, info): # Here self will be the Ship instance returned in resolve_ship return self.name class Query(ObjectType): ship = Field(ShipType) def resolve_ship(self, info): return Ship(name="xwing") schema = Schema(query=Query) executed = schema.execute("{ ship { name } }") assert not executed.errors assert executed.data == {"ship": {"name": "xwing"}} def test_big_list_query_benchmark(benchmark): big_list = range(10000) class Query(ObjectType): all_ints = List(Int) def resolve_all_ints(self, info): return big_list hello_schema = Schema(Query) big_list_query = partial(hello_schema.execute, "{ allInts }") result = benchmark(big_list_query) assert not result.errors assert result.data == {"allInts": list(big_list)} def test_big_list_query_compiled_query_benchmark(benchmark): big_list = range(100000) class Query(ObjectType): all_ints = List(Int) def resolve_all_ints(self, info): return big_list hello_schema = Schema(Query) source = Source("{ allInts }") query_ast = parse(source) big_list_query = partial(execute, hello_schema, query_ast) result = benchmark(big_list_query) assert not result.errors assert result.data == {"allInts": list(big_list)} def test_big_list_of_containers_query_benchmark(benchmark): class Container(ObjectType): x = Int() big_container_list = [Container(x=x) for x in range(1000)] class Query(ObjectType): all_containers = List(Container) def resolve_all_containers(self, info): return big_container_list hello_schema = Schema(Query) big_list_query = partial(hello_schema.execute, "{ allContainers { x } }") result = benchmark(big_list_query) assert not result.errors assert result.data == {"allContainers": [{"x": c.x} for c in big_container_list]} def test_big_list_of_containers_multiple_fields_query_benchmark(benchmark): class Container(ObjectType): x = Int() y = Int() z = Int() o = Int() big_container_list = [Container(x=x, y=x, z=x, o=x) for x in range(1000)] class Query(ObjectType): all_containers = List(Container) def resolve_all_containers(self, info): return big_container_list hello_schema = Schema(Query) big_list_query = partial(hello_schema.execute, "{ allContainers { x, y, z, o } }") result = benchmark(big_list_query) assert not result.errors assert result.data == { "allContainers": [ {"x": c.x, "y": c.y, "z": c.z, "o": c.o} for c in big_container_list ] } def test_big_list_of_containers_multiple_fields_custom_resolvers_query_benchmark( benchmark, ): class Container(ObjectType): x = Int() y = Int() z = Int() o = Int() def resolve_x(self, info): return self.x def resolve_y(self, info): return self.y def resolve_z(self, info): return self.z def resolve_o(self, info): return self.o big_container_list = [Container(x=x, y=x, z=x, o=x) for x in range(1000)] class Query(ObjectType): all_containers = List(Container) def resolve_all_containers(self, info): return big_container_list hello_schema = Schema(Query) big_list_query = partial(hello_schema.execute, "{ allContainers { x, y, z, o } }") result = benchmark(big_list_query) assert not result.errors assert result.data == { "allContainers": [ {"x": c.x, "y": c.y, "z": c.z, "o": c.o} for c in big_container_list ] } def test_query_annotated_resolvers(): context = Context(key="context") class Query(ObjectType): annotated = String(id=String()) context = String() info = String() def resolve_annotated(self, info, id): return "{}-{}".format(self, id) def resolve_context(self, info): assert isinstance(info.context, Context) return "{}-{}".format(self, info.context.key) def resolve_info(self, info): assert isinstance(info, ResolveInfo) return "{}-{}".format(self, info.field_name) test_schema = Schema(Query) result = test_schema.execute('{ annotated(id:"self") }', "base") assert not result.errors assert result.data == {"annotated": "base-self"} result = test_schema.execute("{ context }", "base", context=context) assert not result.errors assert result.data == {"context": "base-context"} result = test_schema.execute("{ info }", "base") assert not result.errors assert result.data == {"info": "base-info"} def test_default_as_kwarg_to_NonNull(): # Related to https://github.com/graphql-python/graphene/issues/702 class User(ObjectType): name = String() is_admin = NonNull(Boolean, default_value=False) class Query(ObjectType): user = Field(User) def resolve_user(self, *args, **kwargs): return User(name="foo") schema = Schema(query=Query) expected = {"user": {"name": "foo", "isAdmin": False}} result = schema.execute("{ user { name isAdmin } }") assert not result.errors assert result.data == expected graphene-2.1.9/graphene/types/tests/test_resolver.py000066400000000000000000000025601407436344100226760ustar00rootroot00000000000000from ..resolver import ( attr_resolver, dict_resolver, dict_or_attr_resolver, get_default_resolver, set_default_resolver, ) args = {} context = None info = None demo_dict = {"attr": "value"} class demo_obj(object): attr = "value" def test_attr_resolver(): resolved = attr_resolver("attr", None, demo_obj, info, **args) assert resolved == "value" def test_attr_resolver_default_value(): resolved = attr_resolver("attr2", "default", demo_obj, info, **args) assert resolved == "default" def test_dict_resolver(): resolved = dict_resolver("attr", None, demo_dict, info, **args) assert resolved == "value" def test_dict_resolver_default_value(): resolved = dict_resolver("attr2", "default", demo_dict, info, **args) assert resolved == "default" def test_dict_or_attr_resolver(): resolved = dict_or_attr_resolver("attr", None, demo_dict, info, **args) assert resolved == "value" resolved = dict_or_attr_resolver("attr", None, demo_obj, info, **args) assert resolved == "value" def test_get_default_resolver_is_attr_resolver(): assert get_default_resolver() == dict_or_attr_resolver def test_set_default_resolver_workd(): default_resolver = get_default_resolver() set_default_resolver(dict_resolver) assert get_default_resolver() == dict_resolver set_default_resolver(default_resolver) graphene-2.1.9/graphene/types/tests/test_scalar.py000066400000000000000000000003311407436344100222740ustar00rootroot00000000000000from ..scalars import Scalar def test_scalar(): class JSONScalar(Scalar): """Documentation""" assert JSONScalar._meta.name == "JSONScalar" assert JSONScalar._meta.description == "Documentation" graphene-2.1.9/graphene/types/tests/test_scalars_serialization.py000066400000000000000000000032551407436344100254240ustar00rootroot00000000000000from ..scalars import Boolean, Float, Int, String def test_serializes_output_int(): assert Int.serialize(1) == 1 assert Int.serialize(0) == 0 assert Int.serialize(-1) == -1 assert Int.serialize(0.1) == 0 assert Int.serialize(1.1) == 1 assert Int.serialize(-1.1) == -1 assert Int.serialize(1e5) == 100000 assert Int.serialize(9876504321) is None assert Int.serialize(-9876504321) is None assert Int.serialize(1e100) is None assert Int.serialize(-1e100) is None assert Int.serialize("-1.1") == -1 assert Int.serialize("one") is None assert Int.serialize(False) == 0 assert Int.serialize(True) == 1 def test_serializes_output_float(): assert Float.serialize(1) == 1.0 assert Float.serialize(0) == 0.0 assert Float.serialize(-1) == -1.0 assert Float.serialize(0.1) == 0.1 assert Float.serialize(1.1) == 1.1 assert Float.serialize(-1.1) == -1.1 assert Float.serialize("-1.1") == -1.1 assert Float.serialize("one") is None assert Float.serialize(False) == 0 assert Float.serialize(True) == 1 def test_serializes_output_string(): assert String.serialize("string") == "string" assert String.serialize(1) == "1" assert String.serialize(-1.1) == "-1.1" assert String.serialize(True) == "true" assert String.serialize(False) == "false" assert String.serialize(u"\U0001F601") == u"\U0001F601" def test_serializes_output_boolean(): assert Boolean.serialize("string") is True assert Boolean.serialize("") is False assert Boolean.serialize(1) is True assert Boolean.serialize(0) is False assert Boolean.serialize(True) is True assert Boolean.serialize(False) is False graphene-2.1.9/graphene/types/tests/test_schema.py000066400000000000000000000017661407436344100223040ustar00rootroot00000000000000import pytest from ..field import Field from ..objecttype import ObjectType from ..scalars import String from ..schema import Schema class MyOtherType(ObjectType): field = String() class Query(ObjectType): inner = Field(MyOtherType) def test_schema(): schema = Schema(Query) assert schema.get_query_type() == schema.get_graphql_type(Query) def test_schema_get_type(): schema = Schema(Query) assert schema.Query == Query assert schema.MyOtherType == MyOtherType def test_schema_get_type_error(): schema = Schema(Query) with pytest.raises(AttributeError) as exc_info: schema.X assert str(exc_info.value) == 'Type "X" not found in the Schema' def test_schema_str(): schema = Schema(Query) assert ( str(schema) == """schema { query: Query } type MyOtherType { field: String } type Query { inner: MyOtherType } """ ) def test_schema_introspect(): schema = Schema(Query) assert "__schema" in schema.introspect() graphene-2.1.9/graphene/types/tests/test_structures.py000066400000000000000000000061711407436344100232620ustar00rootroot00000000000000from functools import partial import pytest from ..scalars import String from ..structures import List, NonNull from .utils import MyLazyType def test_list(): _list = List(String) assert _list.of_type == String assert str(_list) == "[String]" def test_list_with_unmounted_type(): with pytest.raises(Exception) as exc_info: List(String()) assert ( str(exc_info.value) == "List could not have a mounted String() as inner type. Try with List(String)." ) def test_list_with_lazy_type(): MyType = object() field = List(lambda: MyType) assert field.of_type == MyType def test_list_with_lazy_partial_type(): MyType = object() field = List(partial(lambda: MyType)) assert field.of_type == MyType def test_list_with_string_type(): field = List("graphene.types.tests.utils.MyLazyType") assert field.of_type == MyLazyType def test_list_inherited_works_list(): _list = List(List(String)) assert isinstance(_list.of_type, List) assert _list.of_type.of_type == String def test_list_inherited_works_nonnull(): _list = List(NonNull(String)) assert isinstance(_list.of_type, NonNull) assert _list.of_type.of_type == String def test_nonnull(): nonnull = NonNull(String) assert nonnull.of_type == String assert str(nonnull) == "String!" def test_nonnull_with_lazy_type(): MyType = object() field = NonNull(lambda: MyType) assert field.of_type == MyType def test_nonnull_with_lazy_partial_type(): MyType = object() field = NonNull(partial(lambda: MyType)) assert field.of_type == MyType def test_nonnull_with_string_type(): field = NonNull("graphene.types.tests.utils.MyLazyType") assert field.of_type == MyLazyType def test_nonnull_inherited_works_list(): _list = NonNull(List(String)) assert isinstance(_list.of_type, List) assert _list.of_type.of_type == String def test_nonnull_inherited_dont_work_nonnull(): with pytest.raises(Exception) as exc_info: NonNull(NonNull(String)) assert ( str(exc_info.value) == "Can only create NonNull of a Nullable GraphQLType but got: String!." ) def test_nonnull_with_unmounted_type(): with pytest.raises(Exception) as exc_info: NonNull(String()) assert ( str(exc_info.value) == "NonNull could not have a mounted String() as inner type. Try with NonNull(String)." ) def test_list_comparasion(): list1 = List(String) list2 = List(String) list3 = List(None) list1_argskwargs = List(String, None, b=True) list2_argskwargs = List(String, None, b=True) assert list1 == list2 assert list1 != list3 assert list1_argskwargs == list2_argskwargs assert list1 != list1_argskwargs def test_nonnull_comparasion(): nonnull1 = NonNull(String) nonnull2 = NonNull(String) nonnull3 = NonNull(None) nonnull1_argskwargs = NonNull(String, None, b=True) nonnull2_argskwargs = NonNull(String, None, b=True) assert nonnull1 == nonnull2 assert nonnull1 != nonnull3 assert nonnull1_argskwargs == nonnull2_argskwargs assert nonnull1 != nonnull1_argskwargs graphene-2.1.9/graphene/types/tests/test_typemap.py000066400000000000000000000211671407436344100225200ustar00rootroot00000000000000import pytest from graphql.type import ( GraphQLArgument, GraphQLEnumType, GraphQLEnumValue, GraphQLField, GraphQLInputObjectField, GraphQLInputObjectType, GraphQLInterfaceType, GraphQLObjectType, GraphQLString, ) from ..dynamic import Dynamic from ..enum import Enum from ..field import Field from ..inputfield import InputField from ..inputobjecttype import InputObjectType from ..interface import Interface from ..objecttype import ObjectType from ..scalars import Int, String from ..structures import List, NonNull from ..typemap import TypeMap, resolve_type def test_enum(): class MyEnum(Enum): """Description""" foo = 1 bar = 2 @property def description(self): return "Description {}={}".format(self.name, self.value) @property def deprecation_reason(self): if self == MyEnum.foo: return "Is deprecated" typemap = TypeMap([MyEnum]) assert "MyEnum" in typemap graphql_enum = typemap["MyEnum"] assert isinstance(graphql_enum, GraphQLEnumType) assert graphql_enum.name == "MyEnum" assert graphql_enum.description == "Description" values = graphql_enum.values assert values == [ GraphQLEnumValue( name="foo", value=1, description="Description foo=1", deprecation_reason="Is deprecated", ), GraphQLEnumValue(name="bar", value=2, description="Description bar=2"), ] def test_objecttype(): class MyObjectType(ObjectType): """Description""" foo = String( bar=String(description="Argument description", default_value="x"), description="Field description", ) bar = String(name="gizmo") def resolve_foo(self, bar): return bar typemap = TypeMap([MyObjectType]) assert "MyObjectType" in typemap graphql_type = typemap["MyObjectType"] assert isinstance(graphql_type, GraphQLObjectType) assert graphql_type.name == "MyObjectType" assert graphql_type.description == "Description" fields = graphql_type.fields assert list(fields.keys()) == ["foo", "gizmo"] foo_field = fields["foo"] assert isinstance(foo_field, GraphQLField) assert foo_field.description == "Field description" assert foo_field.args == { "bar": GraphQLArgument( GraphQLString, description="Argument description", default_value="x", out_name="bar", ) } def test_dynamic_objecttype(): class MyObjectType(ObjectType): """Description""" bar = Dynamic(lambda: Field(String)) own = Field(lambda: MyObjectType) typemap = TypeMap([MyObjectType]) assert "MyObjectType" in typemap assert list(MyObjectType._meta.fields.keys()) == ["bar", "own"] graphql_type = typemap["MyObjectType"] fields = graphql_type.fields assert list(fields.keys()) == ["bar", "own"] assert fields["bar"].type == GraphQLString assert fields["own"].type == graphql_type def test_interface(): class MyInterface(Interface): """Description""" foo = String( bar=String(description="Argument description", default_value="x"), description="Field description", ) bar = String(name="gizmo", first_arg=String(), other_arg=String(name="oth_arg")) own = Field(lambda: MyInterface) def resolve_foo(self, args, info): return args.get("bar") typemap = TypeMap([MyInterface]) assert "MyInterface" in typemap graphql_type = typemap["MyInterface"] assert isinstance(graphql_type, GraphQLInterfaceType) assert graphql_type.name == "MyInterface" assert graphql_type.description == "Description" fields = graphql_type.fields assert list(fields.keys()) == ["foo", "gizmo", "own"] assert fields["own"].type == graphql_type assert list(fields["gizmo"].args.keys()) == ["firstArg", "oth_arg"] foo_field = fields["foo"] assert isinstance(foo_field, GraphQLField) assert foo_field.description == "Field description" assert not foo_field.resolver # Resolver not attached in interfaces assert foo_field.args == { "bar": GraphQLArgument( GraphQLString, description="Argument description", default_value="x", out_name="bar", ) } def test_inputobject(): class OtherObjectType(InputObjectType): thingy = NonNull(Int) class MyInnerObjectType(InputObjectType): some_field = String() some_other_field = List(OtherObjectType) class MyInputObjectType(InputObjectType): """Description""" foo_bar = String(description="Field description") bar = String(name="gizmo") baz = NonNull(MyInnerObjectType) own = InputField(lambda: MyInputObjectType) def resolve_foo_bar(self, args, info): return args.get("bar") typemap = TypeMap([MyInputObjectType]) assert "MyInputObjectType" in typemap graphql_type = typemap["MyInputObjectType"] assert isinstance(graphql_type, GraphQLInputObjectType) assert graphql_type.name == "MyInputObjectType" assert graphql_type.description == "Description" other_graphql_type = typemap["OtherObjectType"] inner_graphql_type = typemap["MyInnerObjectType"] container = graphql_type.create_container( { "bar": "oh!", "baz": inner_graphql_type.create_container( { "some_other_field": [ other_graphql_type.create_container({"thingy": 1}), other_graphql_type.create_container({"thingy": 2}), ] } ), } ) assert isinstance(container, MyInputObjectType) assert "bar" in container assert container.bar == "oh!" assert "foo_bar" not in container assert container.foo_bar is None assert container.baz.some_field is None assert container.baz.some_other_field[0].thingy == 1 assert container.baz.some_other_field[1].thingy == 2 fields = graphql_type.fields assert list(fields.keys()) == ["fooBar", "gizmo", "baz", "own"] own_field = fields["own"] assert own_field.type == graphql_type foo_field = fields["fooBar"] assert isinstance(foo_field, GraphQLInputObjectField) assert foo_field.description == "Field description" def test_objecttype_camelcase(): class MyObjectType(ObjectType): """Description""" foo_bar = String(bar_foo=String()) typemap = TypeMap([MyObjectType]) assert "MyObjectType" in typemap graphql_type = typemap["MyObjectType"] assert isinstance(graphql_type, GraphQLObjectType) assert graphql_type.name == "MyObjectType" assert graphql_type.description == "Description" fields = graphql_type.fields assert list(fields.keys()) == ["fooBar"] foo_field = fields["fooBar"] assert isinstance(foo_field, GraphQLField) assert foo_field.args == { "barFoo": GraphQLArgument(GraphQLString, out_name="bar_foo") } def test_objecttype_camelcase_disabled(): class MyObjectType(ObjectType): """Description""" foo_bar = String(bar_foo=String()) typemap = TypeMap([MyObjectType], auto_camelcase=False) assert "MyObjectType" in typemap graphql_type = typemap["MyObjectType"] assert isinstance(graphql_type, GraphQLObjectType) assert graphql_type.name == "MyObjectType" assert graphql_type.description == "Description" fields = graphql_type.fields assert list(fields.keys()) == ["foo_bar"] foo_field = fields["foo_bar"] assert isinstance(foo_field, GraphQLField) assert foo_field.args == { "bar_foo": GraphQLArgument(GraphQLString, out_name="bar_foo") } def test_objecttype_with_possible_types(): class MyObjectType(ObjectType): """Description""" class Meta: possible_types = (dict,) foo_bar = String() typemap = TypeMap([MyObjectType]) graphql_type = typemap["MyObjectType"] assert graphql_type.is_type_of assert graphql_type.is_type_of({}, None) is True assert graphql_type.is_type_of(MyObjectType(), None) is False def test_resolve_type_with_missing_type(): class MyObjectType(ObjectType): foo_bar = String() class MyOtherObjectType(ObjectType): fizz_buzz = String() def resolve_type_func(root, info): return MyOtherObjectType typemap = TypeMap([MyObjectType]) with pytest.raises(AssertionError) as excinfo: resolve_type(resolve_type_func, typemap, "MyOtherObjectType", {}, {}) assert "MyOtherObjectTyp" in str(excinfo.value) graphene-2.1.9/graphene/types/tests/test_union.py000066400000000000000000000026501407436344100221650ustar00rootroot00000000000000import pytest from ..field import Field from ..objecttype import ObjectType from ..union import Union from ..unmountedtype import UnmountedType class MyObjectType1(ObjectType): pass class MyObjectType2(ObjectType): pass def test_generate_union(): class MyUnion(Union): """Documentation""" class Meta: types = (MyObjectType1, MyObjectType2) assert MyUnion._meta.name == "MyUnion" assert MyUnion._meta.description == "Documentation" assert MyUnion._meta.types == (MyObjectType1, MyObjectType2) def test_generate_union_with_meta(): class MyUnion(Union): class Meta: name = "MyOtherUnion" description = "Documentation" types = (MyObjectType1, MyObjectType2) assert MyUnion._meta.name == "MyOtherUnion" assert MyUnion._meta.description == "Documentation" def test_generate_union_with_no_types(): with pytest.raises(Exception) as exc_info: class MyUnion(Union): pass assert str(exc_info.value) == "Must provide types for Union MyUnion." def test_union_can_be_mounted(): class MyUnion(Union): class Meta: types = (MyObjectType1, MyObjectType2) my_union_instance = MyUnion() assert isinstance(my_union_instance, UnmountedType) my_union_field = my_union_instance.mount_as(Field) assert isinstance(my_union_field, Field) assert my_union_field.type == MyUnion graphene-2.1.9/graphene/types/tests/test_uuid.py000066400000000000000000000014151407436344100220010ustar00rootroot00000000000000from ..objecttype import ObjectType from ..schema import Schema from ..uuid import UUID class Query(ObjectType): uuid = UUID(input=UUID()) def resolve_uuid(self, info, input): return input schema = Schema(query=Query) def test_uuidstring_query(): uuid_value = "dfeb3bcf-70fd-11e7-a61a-6003088f8204" result = schema.execute("""{ uuid(input: "%s") }""" % uuid_value) assert not result.errors assert result.data == {"uuid": uuid_value} def test_uuidstring_query_variable(): uuid_value = "dfeb3bcf-70fd-11e7-a61a-6003088f8204" result = schema.execute( """query Test($uuid: UUID){ uuid(input: $uuid) }""", variables={"uuid": uuid_value}, ) assert not result.errors assert result.data == {"uuid": uuid_value} graphene-2.1.9/graphene/types/tests/utils.py000066400000000000000000000000261407436344100211310ustar00rootroot00000000000000MyLazyType = object() graphene-2.1.9/graphene/types/typemap.py000066400000000000000000000300751407436344100203150ustar00rootroot00000000000000import inspect from collections import OrderedDict from functools import partial from graphql import ( GraphQLArgument, GraphQLBoolean, GraphQLField, GraphQLFloat, GraphQLID, GraphQLInputObjectField, GraphQLInt, GraphQLList, GraphQLNonNull, GraphQLString, ) from graphql.execution.executor import get_default_resolve_type_fn from graphql.type import GraphQLEnumValue from graphql.type.typemap import GraphQLTypeMap from ..utils.get_unbound_function import get_unbound_function from ..utils.str_converters import to_camel_case from .definitions import ( GrapheneEnumType, GrapheneGraphQLType, GrapheneInputObjectType, GrapheneInterfaceType, GrapheneObjectType, GrapheneScalarType, GrapheneUnionType, ) from .dynamic import Dynamic from .enum import Enum from .field import Field from .inputobjecttype import InputObjectType from .interface import Interface from .objecttype import ObjectType from .resolver import get_default_resolver from .scalars import ID, Boolean, Float, Int, Scalar, String from .structures import List, NonNull from .union import Union from .utils import get_field_as def is_graphene_type(_type): if isinstance(_type, (List, NonNull)): return True if inspect.isclass(_type) and issubclass( _type, (ObjectType, InputObjectType, Scalar, Interface, Union, Enum) ): return True def resolve_type(resolve_type_func, map, type_name, root, info): _type = resolve_type_func(root, info) if not _type: return_type = map[type_name] return get_default_resolve_type_fn(root, info, return_type) if inspect.isclass(_type) and issubclass(_type, ObjectType): graphql_type = map.get(_type._meta.name) assert graphql_type, "Can't find type {} in schema".format(_type._meta.name) assert graphql_type.graphene_type == _type, ( "The type {} does not match with the associated graphene type {}." ).format(_type, graphql_type.graphene_type) return graphql_type return _type def is_type_of_from_possible_types(possible_types, root, info): return isinstance(root, possible_types) class TypeMap(GraphQLTypeMap): def __init__(self, types, auto_camelcase=True, schema=None): self.auto_camelcase = auto_camelcase self.schema = schema super(TypeMap, self).__init__(types) def reducer(self, map, type): if not type: return map if inspect.isfunction(type): type = type() if is_graphene_type(type): return self.graphene_reducer(map, type) return GraphQLTypeMap.reducer(map, type) def graphene_reducer(self, map, type): if isinstance(type, (List, NonNull)): return self.reducer(map, type.of_type) if type._meta.name in map: _type = map[type._meta.name] if isinstance(_type, GrapheneGraphQLType): assert _type.graphene_type == type, ( "Found different types with the same name in the schema: {}, {}." ).format(_type.graphene_type, type) return map if issubclass(type, ObjectType): internal_type = self.construct_objecttype(map, type) elif issubclass(type, InputObjectType): internal_type = self.construct_inputobjecttype(map, type) elif issubclass(type, Interface): internal_type = self.construct_interface(map, type) elif issubclass(type, Scalar): internal_type = self.construct_scalar(map, type) elif issubclass(type, Enum): internal_type = self.construct_enum(map, type) elif issubclass(type, Union): internal_type = self.construct_union(map, type) else: raise Exception("Expected Graphene type, but received: {}.".format(type)) return GraphQLTypeMap.reducer(map, internal_type) def construct_scalar(self, map, type): # We have a mapping to the original GraphQL types # so there are no collisions. _scalars = { String: GraphQLString, Int: GraphQLInt, Float: GraphQLFloat, Boolean: GraphQLBoolean, ID: GraphQLID, } if type in _scalars: return _scalars[type] return GrapheneScalarType( graphene_type=type, name=type._meta.name, description=type._meta.description, serialize=getattr(type, "serialize", None), parse_value=getattr(type, "parse_value", None), parse_literal=getattr(type, "parse_literal", None), ) def construct_enum(self, map, type): values = OrderedDict() for name, value in type._meta.enum.__members__.items(): description = getattr(value, "description", None) deprecation_reason = getattr(value, "deprecation_reason", None) if not description and callable(type._meta.description): description = type._meta.description(value) if not deprecation_reason and callable(type._meta.deprecation_reason): deprecation_reason = type._meta.deprecation_reason(value) values[name] = GraphQLEnumValue( name=name, value=value.value, description=description, deprecation_reason=deprecation_reason, ) type_description = ( type._meta.description(None) if callable(type._meta.description) else type._meta.description ) return GrapheneEnumType( graphene_type=type, values=values, name=type._meta.name, description=type_description, ) def construct_objecttype(self, map, type): if type._meta.name in map: _type = map[type._meta.name] if isinstance(_type, GrapheneGraphQLType): assert _type.graphene_type == type, ( "Found different types with the same name in the schema: {}, {}." ).format(_type.graphene_type, type) return _type def interfaces(): interfaces = [] for interface in type._meta.interfaces: self.graphene_reducer(map, interface) internal_type = map[interface._meta.name] assert internal_type.graphene_type == interface interfaces.append(internal_type) return interfaces if type._meta.possible_types: is_type_of = partial( is_type_of_from_possible_types, type._meta.possible_types ) else: is_type_of = type.is_type_of return GrapheneObjectType( graphene_type=type, name=type._meta.name, description=type._meta.description, fields=partial(self.construct_fields_for_type, map, type), is_type_of=is_type_of, interfaces=interfaces, ) def construct_interface(self, map, type): if type._meta.name in map: _type = map[type._meta.name] if isinstance(_type, GrapheneInterfaceType): assert _type.graphene_type == type, ( "Found different types with the same name in the schema: {}, {}." ).format(_type.graphene_type, type) return _type _resolve_type = None if type.resolve_type: _resolve_type = partial( resolve_type, type.resolve_type, map, type._meta.name ) return GrapheneInterfaceType( graphene_type=type, name=type._meta.name, description=type._meta.description, fields=partial(self.construct_fields_for_type, map, type), resolve_type=_resolve_type, ) def construct_inputobjecttype(self, map, type): return GrapheneInputObjectType( graphene_type=type, name=type._meta.name, description=type._meta.description, container_type=type._meta.container, fields=partial( self.construct_fields_for_type, map, type, is_input_type=True ), ) def construct_union(self, map, type): _resolve_type = None if type.resolve_type: _resolve_type = partial( resolve_type, type.resolve_type, map, type._meta.name ) def types(): union_types = [] for objecttype in type._meta.types: self.graphene_reducer(map, objecttype) internal_type = map[objecttype._meta.name] assert internal_type.graphene_type == objecttype union_types.append(internal_type) return union_types return GrapheneUnionType( graphene_type=type, name=type._meta.name, description=type._meta.description, types=types, resolve_type=_resolve_type, ) def get_name(self, name): if self.auto_camelcase: return to_camel_case(name) return name def construct_fields_for_type(self, map, type, is_input_type=False): fields = OrderedDict() for name, field in type._meta.fields.items(): if isinstance(field, Dynamic): field = get_field_as(field.get_type(self.schema), _as=Field) if not field: continue map = self.reducer(map, field.type) field_type = self.get_field_type(map, field.type) if is_input_type: _field = GraphQLInputObjectField( field_type, default_value=field.default_value, out_name=name, description=field.description, ) else: args = OrderedDict() for arg_name, arg in field.args.items(): map = self.reducer(map, arg.type) arg_type = self.get_field_type(map, arg.type) processed_arg_name = arg.name or self.get_name(arg_name) args[processed_arg_name] = GraphQLArgument( arg_type, out_name=arg_name, description=arg.description, default_value=arg.default_value, ) _field = GraphQLField( field_type, args=args, resolver=field.get_resolver( self.get_resolver_for_type(type, name, field.default_value) ), deprecation_reason=field.deprecation_reason, description=field.description, ) field_name = field.name or self.get_name(name) fields[field_name] = _field return fields def get_resolver_for_type(self, type, name, default_value): if not issubclass(type, ObjectType): return resolver = getattr(type, "resolve_{}".format(name), None) if not resolver: # If we don't find the resolver in the ObjectType class, then try to # find it in each of the interfaces interface_resolver = None for interface in type._meta.interfaces: if name not in interface._meta.fields: continue interface_resolver = getattr(interface, "resolve_{}".format(name), None) if interface_resolver: break resolver = interface_resolver # Only if is not decorated with classmethod if resolver: return get_unbound_function(resolver) default_resolver = type._meta.default_resolver or get_default_resolver() return partial(default_resolver, name, default_value) def get_field_type(self, map, type): if isinstance(type, List): return GraphQLList(self.get_field_type(map, type.of_type)) if isinstance(type, NonNull): return GraphQLNonNull(self.get_field_type(map, type.of_type)) return map.get(type._meta.name) graphene-2.1.9/graphene/types/union.py000066400000000000000000000047321407436344100177670ustar00rootroot00000000000000from .base import BaseOptions, BaseType from .unmountedtype import UnmountedType # For static type checking with Mypy MYPY = False if MYPY: from .objecttype import ObjectType # NOQA from typing import Iterable, Type # NOQA class UnionOptions(BaseOptions): types = () # type: Iterable[Type[ObjectType]] class Union(UnmountedType, BaseType): """ Union Type Definition When a field can return one of a heterogeneous set of types, a Union type is used to describe what types are possible as well as providing a function to determine which type is actually used when the field is resolved. The schema in this example can take a search text and return any of the GraphQL object types indicated: Human, Droid or Startship. Ambigous return types can be resolved on each ObjectType through ``Meta.possible_types`` attribute or ``is_type_of`` method. Or by implementing ``resolve_type`` class method on the Union. .. code:: python from graphene import Union, ObjectType, List class SearchResult(Union): class Meta: types = (Human, Droid, Starship) class Query(ObjectType): search = List(SearchResult.Field( search_text=String(description='Value to search for')) ) Meta: types (Iterable[graphene.ObjectType]): Required. Collection of types that may be returned by this Union for the graphQL schema. name (optional, str): the name of the GraphQL type (must be unique in schema). Defaults to class name. description (optional, str): the description of the GraphQL type in the schema. Defaults to class docstring. """ @classmethod def __init_subclass_with_meta__(cls, types=None, **options): assert ( isinstance(types, (list, tuple)) and len(types) > 0 ), "Must provide types for Union {name}.".format(name=cls.__name__) _meta = UnionOptions(cls) _meta.types = types super(Union, cls).__init_subclass_with_meta__(_meta=_meta, **options) @classmethod def get_type(cls): """ This function is called when the unmounted type (Union instance) is mounted (as a Field, InputField or Argument) """ return cls @classmethod def resolve_type(cls, instance, info): from .objecttype import ObjectType # NOQA if isinstance(instance, ObjectType): return type(instance) graphene-2.1.9/graphene/types/unmountedtype.py000066400000000000000000000044601407436344100215550ustar00rootroot00000000000000from ..utils.orderedtype import OrderedType class UnmountedType(OrderedType): """ This class acts a proxy for a Graphene Type, so it can be mounted dynamically as Field, InputField or Argument. Instead of writing: .. code:: python from graphene import ObjectType, Field, String class MyObjectType(ObjectType): my_field = Field(String, description='Description here') It lets you write: .. code:: python from graphene import ObjectType, String class MyObjectType(ObjectType): my_field = String(description='Description here') It is not used directly, but is inherited by other types and streamlines their use in different context: - Object Type - Scalar Type - Enum - Interface - Union An unmounted type will accept arguments based upon its context (ObjectType, Field or InputObjectType) and pass it on to the appropriate MountedType (Field, Argument or InputField). See each Mounted type reference for more information about valid parameters. """ def __init__(self, *args, **kwargs): super(UnmountedType, self).__init__() self.args = args self.kwargs = kwargs def get_type(self): """ This function is called when the UnmountedType instance is mounted (as a Field, InputField or Argument) """ raise NotImplementedError("get_type not implemented in {}".format(self)) def mount_as(self, _as): return _as.mounted(self) def Field(self): # noqa: N802 """ Mount the UnmountedType as Field """ from .field import Field return self.mount_as(Field) def InputField(self): # noqa: N802 """ Mount the UnmountedType as InputField """ from .inputfield import InputField return self.mount_as(InputField) def Argument(self): # noqa: N802 """ Mount the UnmountedType as Argument """ from .argument import Argument return self.mount_as(Argument) def __eq__(self, other): return self is other or ( isinstance(other, UnmountedType) and self.get_type() == other.get_type() and self.args == other.args and self.kwargs == other.kwargs ) graphene-2.1.9/graphene/types/utils.py000066400000000000000000000022551407436344100177750ustar00rootroot00000000000000import inspect from collections import OrderedDict from functools import partial from six import string_types from ..utils.module_loading import import_string from .mountedtype import MountedType from .unmountedtype import UnmountedType def get_field_as(value, _as=None): """ Get type mounted """ if isinstance(value, MountedType): return value elif isinstance(value, UnmountedType): if _as is None: return value return _as.mounted(value) def yank_fields_from_attrs(attrs, _as=None, sort=True): """ Extract all the fields in given attributes (dict) and return them ordered """ fields_with_names = [] for attname, value in list(attrs.items()): field = get_field_as(value, _as) if not field: continue fields_with_names.append((attname, field)) if sort: fields_with_names = sorted(fields_with_names, key=lambda f: f[1]) return OrderedDict(fields_with_names) def get_type(_type): if isinstance(_type, string_types): return import_string(_type) if inspect.isfunction(_type) or isinstance(_type, partial): return _type() return _type graphene-2.1.9/graphene/types/uuid.py000066400000000000000000000014211407436344100175750ustar00rootroot00000000000000from __future__ import absolute_import import six from uuid import UUID as _UUID from graphql.language import ast from .scalars import Scalar class UUID(Scalar): """ Leverages the internal Python implmeentation of UUID (uuid.UUID) to provide native UUID objects in fields, resolvers and input. """ @staticmethod def serialize(uuid): if isinstance(uuid, six.string_types): uuid = _UUID(uuid) assert isinstance(uuid, _UUID), "Expected UUID instance, received {}".format( uuid ) return str(uuid) @staticmethod def parse_literal(node): if isinstance(node, ast.StringValue): return _UUID(node.value) @staticmethod def parse_value(value): return _UUID(value) graphene-2.1.9/graphene/utils/000077500000000000000000000000001407436344100162535ustar00rootroot00000000000000graphene-2.1.9/graphene/utils/__init__.py000066400000000000000000000000001407436344100203520ustar00rootroot00000000000000graphene-2.1.9/graphene/utils/annotate.py000066400000000000000000000020611407436344100204350ustar00rootroot00000000000000import six from ..pyutils.compat import func_name, signature from .deprecated import warn_deprecation def annotate(_func=None, _trigger_warning=True, **annotations): if not six.PY2 and _trigger_warning: warn_deprecation( "annotate is intended for use in Python 2 only, as you can use type annotations Python 3.\n" "Read more in https://docs.python.org/3/library/typing.html" ) if not _func: def _func(f): return annotate(f, **annotations) return _func func_signature = signature(_func) # We make sure the annotations are valid for key, value in annotations.items(): assert key in func_signature.parameters, ( 'The key {key} is not a function parameter in the function "{func_name}".' ).format(key=key, func_name=func_name(_func)) func_annotations = getattr(_func, "__annotations__", None) if func_annotations is None: _func.__annotations__ = annotations else: _func.__annotations__.update(annotations) return _func graphene-2.1.9/graphene/utils/crunch.py000066400000000000000000000014661407436344100201160ustar00rootroot00000000000000import json try: from collections.abc import Mapping except ImportError: from collections import Mapping def to_key(value): return json.dumps(value) def insert(value, index, values): key = to_key(value) if key not in index: index[key] = len(values) values.append(value) return len(values) - 1 return index.get(key) def flatten(data, index, values): if isinstance(data, (list, tuple)): flattened = [flatten(child, index, values) for child in data] elif isinstance(data, Mapping): flattened = {key: flatten(child, index, values) for key, child in data.items()} else: flattened = data return insert(flattened, index, values) def crunch(data): index = {} values = [] flatten(data, index, values) return values graphene-2.1.9/graphene/utils/deduplicator.py000066400000000000000000000020551407436344100213060ustar00rootroot00000000000000from collections import OrderedDict try: from collections.abc import Mapping except ImportError: from collections import Mapping def deflate(node, index=None, path=None): if index is None: index = {} if path is None: path = [] if node and "id" in node and "__typename" in node: route = ",".join(path) cache_key = ":".join([route, str(node["__typename"]), str(node["id"])]) if index.get(cache_key) is True: return {"__typename": node["__typename"], "id": node["id"]} else: index[cache_key] = True field_names = node.keys() result = OrderedDict() for field_name in field_names: value = node[field_name] new_path = path + [field_name] if isinstance(value, (list, tuple)): result[field_name] = [deflate(child, index, new_path) for child in value] elif isinstance(value, Mapping): result[field_name] = deflate(value, index, new_path) else: result[field_name] = value return result graphene-2.1.9/graphene/utils/deprecated.py000066400000000000000000000034531407436344100207320ustar00rootroot00000000000000import functools import inspect import warnings string_types = (type(b""), type(u"")) def warn_deprecation(text): warnings.warn(text, category=DeprecationWarning, stacklevel=2) def deprecated(reason): """ This is a decorator which can be used to mark functions as deprecated. It will result in a warning being emitted when the function is used. """ if isinstance(reason, string_types): # The @deprecated is used with a 'reason'. # # .. code-block:: python # # @deprecated("please, use another function") # def old_function(x, y): # pass def decorator(func1): if inspect.isclass(func1): fmt1 = "Call to deprecated class {name} ({reason})." else: fmt1 = "Call to deprecated function {name} ({reason})." @functools.wraps(func1) def new_func1(*args, **kwargs): warn_deprecation(fmt1.format(name=func1.__name__, reason=reason)) return func1(*args, **kwargs) return new_func1 return decorator elif inspect.isclass(reason) or inspect.isfunction(reason): # The @deprecated is used without any 'reason'. # # .. code-block:: python # # @deprecated # def old_function(x, y): # pass func2 = reason if inspect.isclass(func2): fmt2 = "Call to deprecated class {name}." else: fmt2 = "Call to deprecated function {name}." @functools.wraps(func2) def new_func2(*args, **kwargs): warn_deprecation(fmt2.format(name=func2.__name__)) return func2(*args, **kwargs) return new_func2 else: raise TypeError(repr(type(reason))) graphene-2.1.9/graphene/utils/get_unbound_function.py000066400000000000000000000001711407436344100230420ustar00rootroot00000000000000def get_unbound_function(func): if not getattr(func, "__self__", True): return func.__func__ return func graphene-2.1.9/graphene/utils/module_loading.py000066400000000000000000000031551407436344100216130ustar00rootroot00000000000000from functools import partial from importlib import import_module def import_string(dotted_path, dotted_attributes=None): """ Import a dotted module path and return the attribute/class designated by the last name in the path. When a dotted attribute path is also provided, the dotted attribute path would be applied to the attribute/class retrieved from the first step, and return the corresponding value designated by the attribute path. Raise ImportError if the import failed. """ try: module_path, class_name = dotted_path.rsplit(".", 1) except ValueError: raise ImportError("%s doesn't look like a module path" % dotted_path) module = import_module(module_path) try: result = getattr(module, class_name) except AttributeError: raise ImportError( 'Module "%s" does not define a "%s" attribute/class' % (module_path, class_name) ) if not dotted_attributes: return result else: attributes = dotted_attributes.split(".") traveled_attributes = [] try: for attribute in attributes: traveled_attributes.append(attribute) result = getattr(result, attribute) return result except AttributeError: raise ImportError( 'Module "%s" does not define a "%s" attribute inside attribute/class "%s"' % (module_path, ".".join(traveled_attributes), class_name) ) def lazy_import(dotted_path, dotted_attributes=None): return partial(import_string, dotted_path, dotted_attributes) graphene-2.1.9/graphene/utils/orderedtype.py000066400000000000000000000023171407436344100211560ustar00rootroot00000000000000from functools import total_ordering @total_ordering class OrderedType(object): creation_counter = 1 def __init__(self, _creation_counter=None): self.creation_counter = _creation_counter or self.gen_counter() @staticmethod def gen_counter(): counter = OrderedType.creation_counter OrderedType.creation_counter += 1 return counter def reset_counter(self): self.creation_counter = self.gen_counter() def __eq__(self, other): # Needed for @total_ordering if isinstance(self, type(other)): return self.creation_counter == other.creation_counter return NotImplemented def __lt__(self, other): # This is needed because bisect does not take a comparison function. if isinstance(other, OrderedType): return self.creation_counter < other.creation_counter return NotImplemented def __gt__(self, other): # This is needed because bisect does not take a comparison function. if isinstance(other, OrderedType): return self.creation_counter > other.creation_counter return NotImplemented def __hash__(self): return hash((self.creation_counter)) graphene-2.1.9/graphene/utils/props.py000066400000000000000000000003551407436344100177730ustar00rootroot00000000000000class _OldClass: pass class _NewClass(object): pass _all_vars = set(dir(_OldClass) + dir(_NewClass)) def props(x): return { key: vars(x).get(key, getattr(x, key)) for key in dir(x) if key not in _all_vars } graphene-2.1.9/graphene/utils/resolve_only_args.py000066400000000000000000000004001407436344100223530ustar00rootroot00000000000000from functools import wraps from .deprecated import deprecated @deprecated("This function is deprecated") def resolve_only_args(func): @wraps(func) def wrapped_func(root, info, **args): return func(root, **args) return wrapped_func graphene-2.1.9/graphene/utils/str_converters.py000066400000000000000000000012761407436344100217150ustar00rootroot00000000000000import re # Adapted from this response in Stackoverflow # http://stackoverflow.com/a/19053800/1072990 def to_camel_case(snake_str): components = snake_str.split("_") # We capitalize the first letter of each component except the first one # with the 'capitalize' method and join them together. return components[0] + "".join(x.capitalize() if x else "_" for x in components[1:]) # From this response in Stackoverflow # http://stackoverflow.com/a/1176023/1072990 def to_snake_case(name): s1 = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", name) return re.sub("([a-z0-9])([A-Z])", r"\1_\2", s1).lower() def to_const(string): return re.sub(r"[\W|^]+", "_", string).upper() # noqa graphene-2.1.9/graphene/utils/subclass_with_meta.py000066400000000000000000000034771407436344100225200ustar00rootroot00000000000000from inspect import isclass import six from ..pyutils.init_subclass import InitSubclassMeta from .props import props class SubclassWithMeta_Meta(InitSubclassMeta): _meta = None def __str__(cls): if cls._meta: return cls._meta.name return cls.__name__ def __repr__(cls): return "<{} meta={}>".format(cls.__name__, repr(cls._meta)) class SubclassWithMeta(six.with_metaclass(SubclassWithMeta_Meta)): """This class improves __init_subclass__ to receive automatically the options from meta""" # We will only have the metaclass in Python 2 def __init_subclass__(cls, **meta_options): """This method just terminates the super() chain""" _Meta = getattr(cls, "Meta", None) _meta_props = {} if _Meta: if isinstance(_Meta, dict): _meta_props = _Meta elif isclass(_Meta): _meta_props = props(_Meta) else: raise Exception( "Meta have to be either a class or a dict. Received {}".format( _Meta ) ) delattr(cls, "Meta") options = dict(meta_options, **_meta_props) abstract = options.pop("abstract", False) if abstract: assert not options, ( "Abstract types can only contain the abstract attribute. " "Received: abstract, {option_keys}" ).format(option_keys=", ".join(options.keys())) else: super_class = super(cls, cls) if hasattr(super_class, "__init_subclass_with_meta__"): super_class.__init_subclass_with_meta__(**options) @classmethod def __init_subclass_with_meta__(cls, **meta_options): """This method just terminates the super() chain""" graphene-2.1.9/graphene/utils/tests/000077500000000000000000000000001407436344100174155ustar00rootroot00000000000000graphene-2.1.9/graphene/utils/tests/__init__.py000066400000000000000000000000001407436344100215140ustar00rootroot00000000000000graphene-2.1.9/graphene/utils/tests/test_annotate.py000066400000000000000000000014731407436344100226440ustar00rootroot00000000000000import pytest from ..annotate import annotate def func(a, b, *c, **d): pass annotations = {"a": int, "b": str, "c": list, "d": dict} def func_with_annotations(a, b, *c, **d): pass func_with_annotations.__annotations__ = annotations def test_annotate_with_no_params(): annotated_func = annotate(func, _trigger_warning=False) assert annotated_func.__annotations__ == {} def test_annotate_with_params(): annotated_func = annotate(_trigger_warning=False, **annotations)(func) assert annotated_func.__annotations__ == annotations def test_annotate_with_wront_params(): with pytest.raises(Exception) as exc_info: annotate(p=int, _trigger_warning=False)(func) assert ( str(exc_info.value) == 'The key p is not a function parameter in the function "func".' ) graphene-2.1.9/graphene/utils/tests/test_crunch.py000066400000000000000000000037131407436344100223140ustar00rootroot00000000000000import pytest from collections import OrderedDict from ..crunch import crunch @pytest.mark.parametrize( "description,uncrunched,crunched", [ ["number primitive", 0, [0]], ["boolean primitive", True, [True]], ["string primitive", "string", ["string"]], ["empty array", [], [[]]], ["single-item array", [None], [None, [0]]], [ "multi-primitive all distinct array", [None, 0, True, "string"], [None, 0, True, "string", [0, 1, 2, 3]], ], [ "multi-primitive repeated array", [True, True, True, True], [True, [0, 0, 0, 0]], ], ["one-level nested array", [[1, 2, 3]], [1, 2, 3, [0, 1, 2], [3]]], ["two-level nested array", [[[1, 2, 3]]], [1, 2, 3, [0, 1, 2], [3], [4]]], ["empty object", {}, [{}]], ["single-item object", {"a": None}, [None, {"a": 0}]], [ "multi-item all distinct object", OrderedDict([("a", None), ("b", 0), ("c", True), ("d", "string")]), [None, 0, True, "string", {"a": 0, "b": 1, "c": 2, "d": 3}], ], [ "multi-item repeated object", OrderedDict([("a", True), ("b", True), ("c", True), ("d", True)]), [True, {"a": 0, "b": 0, "c": 0, "d": 0}], ], [ "complex array", [OrderedDict([("a", True), ("b", [1, 2, 3])]), [1, 2, 3]], [True, 1, 2, 3, [1, 2, 3], {"a": 0, "b": 4}, [5, 4]], ], [ "complex object", OrderedDict( [ ("a", True), ("b", [1, 2, 3]), ("c", OrderedDict([("a", True), ("b", [1, 2, 3])])), ] ), [True, 1, 2, 3, [1, 2, 3], {"a": 0, "b": 4}, {"a": 0, "b": 4, "c": 5}], ], ], ) def test_crunch(description, uncrunched, crunched): assert crunch(uncrunched) == crunched graphene-2.1.9/graphene/utils/tests/test_deduplicator.py000066400000000000000000000116451407436344100235140ustar00rootroot00000000000000import datetime import graphene from graphene import relay from graphene.types.resolver import dict_resolver from ..deduplicator import deflate def test_does_not_modify_object_without_typename_and_id(): response = {"foo": "bar"} deflated_response = deflate(response) assert deflated_response == {"foo": "bar"} def test_does_not_modify_first_instance_of_an_object(): response = { "data": [ {"__typename": "foo", "id": 1, "name": "foo"}, {"__typename": "foo", "id": 1, "name": "foo"}, ] } deflated_response = deflate(response) assert deflated_response == { "data": [ {"__typename": "foo", "id": 1, "name": "foo"}, {"__typename": "foo", "id": 1}, ] } def test_does_not_modify_first_instance_of_an_object_nested(): response = { "data": [ { "__typename": "foo", "bar1": {"__typename": "bar", "id": 1, "name": "bar"}, "bar2": {"__typename": "bar", "id": 1, "name": "bar"}, "id": 1, }, { "__typename": "foo", "bar1": {"__typename": "bar", "id": 1, "name": "bar"}, "bar2": {"__typename": "bar", "id": 1, "name": "bar"}, "id": 2, }, ] } deflated_response = deflate(response) assert deflated_response == { "data": [ { "__typename": "foo", "bar1": {"__typename": "bar", "id": 1, "name": "bar"}, "bar2": {"__typename": "bar", "id": 1, "name": "bar"}, "id": 1, }, { "__typename": "foo", "bar1": {"__typename": "bar", "id": 1}, "bar2": {"__typename": "bar", "id": 1}, "id": 2, }, ] } def test_does_not_modify_input(): response = { "data": [ {"__typename": "foo", "id": 1, "name": "foo"}, {"__typename": "foo", "id": 1, "name": "foo"}, ] } deflate(response) assert response == { "data": [ {"__typename": "foo", "id": 1, "name": "foo"}, {"__typename": "foo", "id": 1, "name": "foo"}, ] } TEST_DATA = { "events": [ {"id": "568", "date": datetime.date(2017, 5, 19), "movie": "1198359"}, {"id": "234", "date": datetime.date(2017, 5, 20), "movie": "1198359"}, ], "movies": { "1198359": { "name": "King Arthur: Legend of the Sword", "synopsis": ( "When the child Arthur's father is murdered, Vortigern, " "Arthur's uncle, seizes the crown. Robbed of his birthright and " "with no idea who he truly is..." ), } }, } def test_example_end_to_end(): class Movie(graphene.ObjectType): class Meta: interfaces = (relay.Node,) default_resolver = dict_resolver name = graphene.String(required=True) synopsis = graphene.String(required=True) class Event(graphene.ObjectType): class Meta: interfaces = (relay.Node,) default_resolver = dict_resolver movie = graphene.Field(Movie, required=True) date = graphene.types.datetime.Date(required=True) def resolve_movie(event, info): return TEST_DATA["movies"][event["movie"]] class Query(graphene.ObjectType): events = graphene.List(graphene.NonNull(Event), required=True) def resolve_events(_, info): return TEST_DATA["events"] schema = graphene.Schema(query=Query) query = """\ { events { __typename id date movie { __typename id name synopsis } } } """ result = schema.execute(query) assert not result.errors result.data = deflate(result.data) assert result.data == { "events": [ { "__typename": "Event", "id": "RXZlbnQ6NTY4", "date": "2017-05-19", "movie": { "__typename": "Movie", "id": "TW92aWU6Tm9uZQ==", "name": "King Arthur: Legend of the Sword", "synopsis": ( "When the child Arthur's father is murdered, Vortigern, " "Arthur's uncle, seizes the crown. Robbed of his birthright and " "with no idea who he truly is..." ), }, }, { "__typename": "Event", "id": "RXZlbnQ6MjM0", "date": "2017-05-20", "movie": {"__typename": "Movie", "id": "TW92aWU6Tm9uZQ=="}, }, ] } graphene-2.1.9/graphene/utils/tests/test_deprecated.py000066400000000000000000000034011407436344100231240ustar00rootroot00000000000000import pytest from .. import deprecated from ..deprecated import deprecated as deprecated_decorator from ..deprecated import warn_deprecation def test_warn_deprecation(mocker): mocker.patch.object(deprecated.warnings, "warn") warn_deprecation("OH!") deprecated.warnings.warn.assert_called_with( "OH!", stacklevel=2, category=DeprecationWarning ) def test_deprecated_decorator(mocker): mocker.patch.object(deprecated, "warn_deprecation") @deprecated_decorator def my_func(): return True result = my_func() assert result deprecated.warn_deprecation.assert_called_with( "Call to deprecated function my_func." ) def test_deprecated_class(mocker): mocker.patch.object(deprecated, "warn_deprecation") @deprecated_decorator class X: pass result = X() assert result deprecated.warn_deprecation.assert_called_with("Call to deprecated class X.") def test_deprecated_decorator_text(mocker): mocker.patch.object(deprecated, "warn_deprecation") @deprecated_decorator("Deprecation text") def my_func(): return True result = my_func() assert result deprecated.warn_deprecation.assert_called_with( "Call to deprecated function my_func (Deprecation text)." ) def test_deprecated_class_text(mocker): mocker.patch.object(deprecated, "warn_deprecation") @deprecated_decorator("Deprecation text") class X: pass result = X() assert result deprecated.warn_deprecation.assert_called_with( "Call to deprecated class X (Deprecation text)." ) def test_deprecated_other_object(mocker): mocker.patch.object(deprecated, "warn_deprecation") with pytest.raises(TypeError): deprecated_decorator({}) graphene-2.1.9/graphene/utils/tests/test_module_loading.py000066400000000000000000000035161407436344100240150ustar00rootroot00000000000000from pytest import raises from graphene import ObjectType, String from ..module_loading import import_string, lazy_import def test_import_string(): MyString = import_string("graphene.String") assert MyString == String MyObjectTypeMeta = import_string("graphene.ObjectType", "__doc__") assert MyObjectTypeMeta == ObjectType.__doc__ def test_import_string_module(): with raises(Exception) as exc_info: import_string("graphenea") assert str(exc_info.value) == "graphenea doesn't look like a module path" def test_import_string_class(): with raises(Exception) as exc_info: import_string("graphene.Stringa") assert ( str(exc_info.value) == 'Module "graphene" does not define a "Stringa" attribute/class' ) def test_import_string_attributes(): with raises(Exception) as exc_info: import_string("graphene.String", "length") assert ( str(exc_info.value) == 'Module "graphene" does not define a "length" attribute inside attribute/class ' '"String"' ) with raises(Exception) as exc_info: import_string("graphene.ObjectType", "__class__.length") assert ( str(exc_info.value) == 'Module "graphene" does not define a "__class__.length" attribute inside ' 'attribute/class "ObjectType"' ) with raises(Exception) as exc_info: import_string("graphene.ObjectType", "__classa__.__base__") assert ( str(exc_info.value) == 'Module "graphene" does not define a "__classa__" attribute inside attribute/class ' '"ObjectType"' ) def test_lazy_import(): f = lazy_import("graphene.String") MyString = f() assert MyString == String f = lazy_import("graphene.ObjectType", "__doc__") MyObjectTypeMeta = f() assert MyObjectTypeMeta == ObjectType.__doc__ graphene-2.1.9/graphene/utils/tests/test_orderedtype.py000066400000000000000000000013571407436344100233620ustar00rootroot00000000000000from ..orderedtype import OrderedType def test_orderedtype(): one = OrderedType() two = OrderedType() three = OrderedType() assert one < two < three def test_orderedtype_eq(): one = OrderedType() two = OrderedType() assert one == one assert one != two def test_orderedtype_hash(): one = OrderedType() two = OrderedType() assert hash(one) == hash(one) assert hash(one) != hash(two) def test_orderedtype_resetcounter(): one = OrderedType() two = OrderedType() one.reset_counter() assert one > two def test_orderedtype_non_orderabletypes(): one = OrderedType() assert one.__lt__(1) == NotImplemented assert one.__gt__(1) == NotImplemented assert not one == 1 graphene-2.1.9/graphene/utils/tests/test_resolve_only_args.py000066400000000000000000000006231407436344100245630ustar00rootroot00000000000000from .. import deprecated from ..resolve_only_args import resolve_only_args def test_resolve_only_args(mocker): mocker.patch.object(deprecated, "warn_deprecation") def resolver(root, **args): return root, args wrapped_resolver = resolve_only_args(resolver) assert deprecated.warn_deprecation.called result = wrapped_resolver(1, 2, a=3) assert result == (1, {"a": 3}) graphene-2.1.9/graphene/utils/tests/test_resolver_from_annotations.py000066400000000000000000000000001407436344100263150ustar00rootroot00000000000000graphene-2.1.9/graphene/utils/tests/test_str_converters.py000066400000000000000000000016701407436344100241140ustar00rootroot00000000000000# coding: utf-8 from ..str_converters import to_camel_case, to_const, to_snake_case def test_snake_case(): assert to_snake_case("snakesOnAPlane") == "snakes_on_a_plane" assert to_snake_case("SnakesOnAPlane") == "snakes_on_a_plane" assert to_snake_case("SnakesOnA_Plane") == "snakes_on_a__plane" assert to_snake_case("snakes_on_a_plane") == "snakes_on_a_plane" assert to_snake_case("snakes_on_a__plane") == "snakes_on_a__plane" assert to_snake_case("IPhoneHysteria") == "i_phone_hysteria" assert to_snake_case("iPhoneHysteria") == "i_phone_hysteria" def test_camel_case(): assert to_camel_case("snakes_on_a_plane") == "snakesOnAPlane" assert to_camel_case("snakes_on_a__plane") == "snakesOnA_Plane" assert to_camel_case("i_phone_hysteria") == "iPhoneHysteria" assert to_camel_case("field_i18n") == "fieldI18n" def test_to_const(): assert to_const('snakes $1. on a "#plane') == "SNAKES_1_ON_A_PLANE" graphene-2.1.9/graphene/utils/tests/test_trim_docstring.py000066400000000000000000000011101407436344100240460ustar00rootroot00000000000000from ..trim_docstring import trim_docstring def test_trim_docstring(): class WellDocumentedObject(object): """ This object is very well-documented. It has multiple lines in its description. Multiple paragraphs too """ assert ( trim_docstring(WellDocumentedObject.__doc__) == "This object is very well-documented. It has multiple lines in its\n" "description.\n\nMultiple paragraphs too" ) class UndocumentedObject(object): pass assert trim_docstring(UndocumentedObject.__doc__) is None graphene-2.1.9/graphene/utils/thenables.py000066400000000000000000000020331407436344100205700ustar00rootroot00000000000000""" This file is used mainly as a bridge for thenable abstractions. This includes: - Promises - Asyncio Coroutines """ try: from promise import Promise, is_thenable # type: ignore except ImportError: class Promise(object): # type: ignore pass def is_thenable(obj): # type: ignore return False try: from inspect import isawaitable from .thenables_asyncio import await_and_execute except ImportError: def isawaitable(obj): # type: ignore return False def maybe_thenable(obj, on_resolve): """ Execute a on_resolve function once the thenable is resolved, returning the same type of object inputed. If the object is not thenable, it should return on_resolve(obj) """ if isawaitable(obj) and not isinstance(obj, Promise): return await_and_execute(obj, on_resolve) if is_thenable(obj): return Promise.resolve(obj).then(on_resolve) # If it's not awaitable not a Promise, return # the function executed over the object return on_resolve(obj) graphene-2.1.9/graphene/utils/thenables_asyncio.py000066400000000000000000000002241407436344100223150ustar00rootroot00000000000000def await_and_execute(obj, on_resolve): async def build_resolve_async(): return on_resolve(await obj) return build_resolve_async() graphene-2.1.9/graphene/utils/trim_docstring.py000066400000000000000000000004511407436344100216540ustar00rootroot00000000000000import inspect def trim_docstring(docstring): # Cleans up whitespaces from an indented docstring # # See https://www.python.org/dev/peps/pep-0257/ # and https://docs.python.org/2/library/inspect.html#inspect.cleandoc return inspect.cleandoc(docstring) if docstring else None graphene-2.1.9/mypy.ini000066400000000000000000000004451407436344100150240ustar00rootroot00000000000000[mypy] ignore_missing_imports = True [mypy-graphene.pyutils.*] ignore_errors = True [mypy-graphene.types.scalars] ignore_errors = True [mypy-graphene.types.generic] ignore_errors = True [mypy-graphene.types.tests.*] ignore_errors = True [mypy-graphene.relay.tests.*] ignore_errors = True graphene-2.1.9/setup.cfg000066400000000000000000000003541407436344100151450ustar00rootroot00000000000000[flake8] exclude = setup.py,docs/*,*/examples/*,graphene/pyutils/*,tests max-line-length = 120 [coverage:run] omit = graphene/pyutils/*,*/tests/*,graphene/types/scalars.py [isort] known_first_party=graphene [bdist_wheel] universal=1 graphene-2.1.9/setup.py000066400000000000000000000053721407436344100150430ustar00rootroot00000000000000import ast import codecs import re import sys from setuptools import find_packages, setup from setuptools.command.test import test as TestCommand _version_re = re.compile(r"VERSION\s+=\s+(.*)") with open("graphene/__init__.py", "rb") as f: version = ast.literal_eval(_version_re.search(f.read().decode("utf-8")).group(1)) path_copy = sys.path[:] sys.path.append("graphene") try: from pyutils.version import get_version version = get_version(version) except Exception: version = ".".join([str(v) for v in version]) sys.path[:] = path_copy class PyTest(TestCommand): user_options = [("pytest-args=", "a", "Arguments to pass to py.test")] def initialize_options(self): TestCommand.initialize_options(self) self.pytest_args = [] def finalize_options(self): TestCommand.finalize_options(self) self.test_args = [] self.test_suite = True def run_tests(self): # import here, cause outside the eggs aren't loaded import pytest errno = pytest.main(self.pytest_args) sys.exit(errno) tests_require = [ "pytest", "pytest-benchmark", "pytest-cov", "pytest-mock", # pinning fastdiff dep (required by snapshottest) because later versions # require wasmer 1.0.0 which is not compatible with Python 2.7 "fastdiff==0.2.0", "snapshottest", "coveralls", "promise", "six", "mock", "pytz", "iso8601", ] setup( name="graphene", version=version, description="GraphQL Framework for Python", long_description=codecs.open( "README.rst", "r", encoding="ascii", errors="replace" ).read(), url="https://github.com/graphql-python/graphene", author="Syrus Akbary", author_email="me@syrusakbary.com", license="MIT", classifiers=[ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "Topic :: Software Development :: Libraries", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: Implementation :: PyPy", ], keywords="api graphql protocol rest relay graphene", packages=find_packages(exclude=["tests", "tests.*", "examples"]), install_requires=[ "six>=1.10.0,<2", "graphql-core>=2.1,<3", "graphql-relay>=2,<3", "aniso8601>=3,<=7", ], tests_require=tests_require, extras_require={ "test": tests_require, "django": ["graphene-django"], "sqlalchemy": ["graphene-sqlalchemy"], }, cmdclass={"test": PyTest}, ) graphene-2.1.9/tests_asyncio/000077500000000000000000000000001407436344100162115ustar00rootroot00000000000000graphene-2.1.9/tests_asyncio/test_relay_connection.py000066400000000000000000000056051407436344100231630ustar00rootroot00000000000000import pytest from collections import OrderedDict from graphql.execution.executors.asyncio import AsyncioExecutor from graphql_relay.utils import base64 from graphene.types import ObjectType, Schema, String from graphene.relay.connection import Connection, ConnectionField, PageInfo from graphene.relay.node import Node letter_chars = ["A", "B", "C", "D", "E"] class Letter(ObjectType): class Meta: interfaces = (Node,) letter = String() class LetterConnection(Connection): class Meta: node = Letter class Query(ObjectType): letters = ConnectionField(LetterConnection) connection_letters = ConnectionField(LetterConnection) promise_letters = ConnectionField(LetterConnection) node = Node.Field() def resolve_letters(self, info, **args): return list(letters.values()) async def resolve_promise_letters(self, info, **args): return list(letters.values()) def resolve_connection_letters(self, info, **args): return LetterConnection( page_info=PageInfo(has_next_page=True, has_previous_page=False), edges=[ LetterConnection.Edge(node=Letter(id=0, letter="A"), cursor="a-cursor") ], ) schema = Schema(Query) letters = OrderedDict() for i, letter in enumerate(letter_chars): letters[letter] = Letter(id=i, letter=letter) def edges(selected_letters): return [ { "node": {"id": base64("Letter:%s" % letter.id), "letter": letter.letter}, "cursor": base64("arrayconnection:%s" % letter.id), } for letter in [letters[i] for i in selected_letters] ] def cursor_for(ltr): letter = letters[ltr] return base64("arrayconnection:%s" % letter.id) def execute(args=""): if args: args = "(" + args + ")" return schema.execute( """ { letters%s { edges { node { id letter } cursor } pageInfo { hasPreviousPage hasNextPage startCursor endCursor } } } """ % args ) @pytest.mark.asyncio async def test_connection_promise(): result = await schema.execute( """ { promiseLetters(first:1) { edges { node { id letter } } pageInfo { hasPreviousPage hasNextPage } } } """, executor=AsyncioExecutor(), return_promise=True, ) assert not result.errors assert result.data == { "promiseLetters": { "edges": [{"node": {"id": "TGV0dGVyOjA=", "letter": "A"}}], "pageInfo": {"hasPreviousPage": False, "hasNextPage": True}, } } graphene-2.1.9/tests_asyncio/test_relay_mutation.py000066400000000000000000000044031407436344100226570ustar00rootroot00000000000000import pytest from graphql.execution.executors.asyncio import AsyncioExecutor from graphene.types import ID, Field, ObjectType, Schema from graphene.types.scalars import String from graphene.relay.mutation import ClientIDMutation class SharedFields(object): shared = String() class MyNode(ObjectType): # class Meta: # interfaces = (Node, ) id = ID() name = String() class SaySomethingAsync(ClientIDMutation): class Input: what = String() phrase = String() @staticmethod async def mutate_and_get_payload(self, info, what, client_mutation_id=None): return SaySomethingAsync(phrase=str(what)) # MyEdge = MyNode.Connection.Edge class MyEdge(ObjectType): node = Field(MyNode) cursor = String() class OtherMutation(ClientIDMutation): class Input(SharedFields): additional_field = String() name = String() my_node_edge = Field(MyEdge) @staticmethod def mutate_and_get_payload( self, info, shared="", additional_field="", client_mutation_id=None ): edge_type = MyEdge return OtherMutation( name=shared + additional_field, my_node_edge=edge_type(cursor="1", node=MyNode(name="name")), ) class RootQuery(ObjectType): something = String() class Mutation(ObjectType): say_promise = SaySomethingAsync.Field() other = OtherMutation.Field() schema = Schema(query=RootQuery, mutation=Mutation) @pytest.mark.asyncio async def test_node_query_promise(): executed = await schema.execute( 'mutation a { sayPromise(input: {what:"hello", clientMutationId:"1"}) { phrase } }', executor=AsyncioExecutor(), return_promise=True, ) assert not executed.errors assert executed.data == {"sayPromise": {"phrase": "hello"}} @pytest.mark.asyncio async def test_edge_query(): executed = await schema.execute( 'mutation a { other(input: {clientMutationId:"1"}) { clientMutationId, myNodeEdge { cursor node { name }} } }', executor=AsyncioExecutor(), return_promise=True, ) assert not executed.errors assert dict(executed.data) == { "other": { "clientMutationId": "1", "myNodeEdge": {"cursor": "1", "node": {"name": "name"}}, } } graphene-2.1.9/tests_py36/000077500000000000000000000000001407436344100153455ustar00rootroot00000000000000graphene-2.1.9/tests_py36/test_objecttype.py000066400000000000000000000005141407436344100211260ustar00rootroot00000000000000from graphene import Schema, ObjectType, String def test_objecttype_meta_with_annotations(): class Query(ObjectType): class Meta: name: str = "oops" hello = String() def resolve_hello(self, info): return "Hello" schema = Schema(query=Query) assert schema is not None graphene-2.1.9/tox.ini000066400000000000000000000014221407436344100146340ustar00rootroot00000000000000[tox] envlist = py{27,36,37,38,39,310},flake8,pre-commit,mypy [gh-actions] python = 2.7: py27 3.6: py36 3.7: py37 3.8: py38 3.9: py39 3.10-dev: py310 [testenv] passenv = * usedevelop = True deps = -e.[test] py{36,37,38,39,310}: pytest-asyncio setenv = PYTHONPATH = .:{envdir} commands = py{27}: py.test --cov=graphene graphene examples {posargs} py{36,37,38,39,310}: py.test --cov=graphene graphene examples tests_asyncio tests_py36 {posargs} [testenv:pre-commit] deps = pre-commit>0.12.0 setenv = LC_CTYPE=en_US.UTF-8 commands = pre-commit {posargs:run --all-files} [testenv:mypy] deps = mypy types-six commands = mypy graphene [testenv:flake8] deps = flake8 commands = pip install -e . flake8 graphene