diff --git a/.bumpversion.cfg b/.bumpversion.cfg index b7e42d6d..edad61bc 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 3.2.2 +current_version = 3.2.6 commit = False tag = False diff --git a/.coveragerc b/.coveragerc index 8bdacb69..537f4767 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,6 +1,8 @@ [run] branch = True -source = src +source = + src + tests omit = */conftest.py */test_*_fuzz.py @@ -15,6 +17,7 @@ exclude_lines = pragma: no cover except ImportError: \# Python < + sys\.version_info < raise NotImplementedError raise TypeError\(f?"Unexpected assert False, @@ -23,4 +26,5 @@ exclude_lines = if TYPE_CHECKING: ^\s+\.\.\.$ ^\s+pass$ + \: \.\.\.$ ignore_errors = True diff --git a/.flake8 b/.flake8 index ccded588..0be28606 100644 --- a/.flake8 +++ b/.flake8 @@ -1,4 +1,4 @@ [flake8] -ignore = E203,W503 +ignore = E203,E704,W503 exclude = .git,.mypy_cache,.pytest_cache,.tox,.venv,__pycache__,build,dist,docs max-line-length = 88 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 626f94c2..2ed9eeb4 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -7,12 +7,12 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - - name: Set up Python 3.9 - uses: actions/setup-python@v2 + - name: Set up Python 3.12 + uses: actions/setup-python@v5 with: - python-version: 3.9 + python-version: 3.12 - name: Install dependencies run: | diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index fc166745..440eb22d 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -10,16 +10,16 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - - name: Set up Python 3.9 - uses: actions/setup-python@v2 + - name: Set up Python 3.12 + uses: actions/setup-python@v5 with: - python-version: 3.9 + python-version: 3.12 - name: Build wheel and source tarball run: | - pip install wheel + pip install setuptools wheel python setup.py sdist bdist_wheel - name: Publish a Python distribution to PyPI diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 09278c14..50b9873c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,25 +3,48 @@ name: Tests on: [push, pull_request] jobs: - build: + tests: runs-on: ubuntu-latest strategy: matrix: - python: ['3.6', '3.7', '3.8', '3.9', '3.10', 'pypy3'] + python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', 'pypy3.9', 'pypy3.10'] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip - pip install tox tox-gh-actions + pip install "tox>=4.24,<5" "tox-gh-actions>=3.2,<4" - name: Run unit tests with tox run: tox + + tests-old: + runs-on: ubuntu-20.04 + + strategy: + matrix: + python-version: ['3.6', '3.7', '3.8'] + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install "tox>=3.28,<5" "tox-gh-actions>=2.12,<4" + + - name: Run unit tests with tox + run: tox \ No newline at end of file diff --git a/.gitignore b/.gitignore index 6b51313b..a15cbec4 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ .tox/ .venv*/ .vs/ +.vscode/ build/ dist/ diff --git a/README.md b/README.md index 910d0144..84c5f7f4 100644 --- a/README.md +++ b/README.md @@ -10,13 +10,18 @@ a query language for APIs created by Facebook. ![Lint Status](https://github.com/graphql-python/graphql-core/actions/workflows/lint.yml/badge.svg) [![Code Style](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black) -The current version 3.2.2 of GraphQL-core is up-to-date with GraphQL.js version 16.4.0. +The current version 3.2.6 of GraphQL-core is up-to-date with GraphQL.js version 16.8.2. -An extensive test suite with over 2300 unit tests and 100% coverage comprises a +An extensive test suite with over 2500 unit tests and 100% coverage comprises a replication of the complete test suite of GraphQL.js, making sure this port is reliable and compatible with GraphQL.js. -Note that for various reasons, GraphQL-core does not use SemVer like GraphQL.js. Increases in the major version of GraphQL.js are reflected in the minor version of GraphQL-core instead. This means there can be breaking changes in the API when the minor version changes, and only patch releases are fully backward compatible. Therefore, we recommend something like `=~ 3.2.0` as version specifier when including GraphQL-core as a dependency. +Note that for various reasons, GraphQL-core does not use SemVer like GraphQL.js. +Changes in the major version of GraphQL.js are reflected in the minor version of +GraphQL-core instead. This means there can be breaking changes in the API +when the minor version changes, and only patch releases are fully backward compatible. +Therefore, we recommend using something like `~= 3.2.0` as the version specifier +when including GraphQL-core as a dependency. ## Documentation @@ -48,8 +53,8 @@ GraphQL-core 3 can be installed from PyPI using the built-in pip command: python -m pip install graphql-core -You can also use [poetry](https://github.com/python-poetry/poetry) for installation in a -virtual environment: +You can also use [poetry](https://github.com/python-poetry/poetry) for installation +in a virtual environment: poetry install @@ -196,6 +201,10 @@ Some restrictions (mostly in line with the design goals): * supports asynchronous operations only via async.io (does not support the additional executors in GraphQL-core) +Note that meanwhile we are using the amazing [ruff](https://docs.astral.sh/ruff/) tool +to both format and check the code of GraphQL-core 3, +in addition to using [mypy](https://mypy-lang.org/) as type checker. + ## Integration with other libraries and roadmap @@ -205,14 +214,12 @@ Some restrictions (mostly in line with the design goals): also been created by Syrus Akbary, who meanwhile has handed over the maintenance and future development to members of the GraphQL-Python community. - The current version 2 of Graphene is using Graphql-core 2 as core library for much of - the heavy lifting. Note that Graphene 2 is not compatible with GraphQL-core 3. - The new version 3 of Graphene will use GraphQL-core 3 instead of GraphQL-core 2. + Graphene 3 is now using Graphql-core 3 as core library for much of the heavy lifting. * [Ariadne](https://github.com/mirumee/ariadne) is a Python library for implementing GraphQL servers using schema-first approach created by Mirumee Software. - Ariadne is already using GraphQL-core 3 as its GraphQL implementation. + Ariadne is also using GraphQL-core 3 as its GraphQL implementation. * [Strawberry](https://github.com/strawberry-graphql/strawberry), created by Patrick Arminio, is a new GraphQL library for Python 3, inspired by dataclasses, @@ -228,6 +235,7 @@ Changes are tracked as ## Credits and history The GraphQL-core 3 library + * has been created and is maintained by Christoph Zwerschke * uses ideas and code from GraphQL-core 2, a prior work by Syrus Akbary * is a Python port of GraphQL.js which has been developed by Lee Byron and others diff --git a/docs/conf.py b/docs/conf.py index d5eca20e..1ee3514f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -30,29 +30,29 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', + "sphinx.ext.autodoc", ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +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' +source_suffix = ".rst" # The encoding of source files. # # source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = 'GraphQL-core 3' -copyright = '2022, Christoph Zwerschke' -author = 'Christoph Zwerschke' +project = "GraphQL-core 3" +copyright = "2025, Christoph Zwerschke" +author = "Christoph Zwerschke" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -61,14 +61,14 @@ # The short X.Y version. # version = '3.2' # The full version, including alpha/beta/rc tags. -version = release = '3.2.2' +version = release = "3.2.6" # 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 +language = "en" # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: @@ -82,23 +82,23 @@ # 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'] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] # AutoDoc configuration autoclass_content = "class" autodoc_default_options = { - 'members': True, - 'inherited-members': True, - 'undoc-members': True, - 'show-inheritance': True + "members": True, + "inherited-members": True, + "undoc-members": True, + "show-inheritance": True, } autosummary_generate = True autodoc_type_aliases = { - 'AwaitableOrValue': 'graphql.pyutils.AwaitableOrValue', - 'FormattedSourceLocation': 'graphql.language.FormattedSourceLocation', - 'Middleware': 'graphql.execution.Middleware', - 'TypeMap': 'graphql.schema.TypeMap' + "AwaitableOrValue": "graphql.pyutils.AwaitableOrValue", + "FormattedSourceLocation": "graphql.language.FormattedSourceLocation", + "Middleware": "graphql.execution.Middleware", + "TypeMap": "graphql.schema.TypeMap", } # GraphQL-core top level modules with submodules that can be omitted. @@ -106,30 +106,38 @@ # qualified form, but the documentation has the shorter form. # We need to give autodoc a little help in this cases. graphql_modules = { - 'error': ['graphql_error'], - 'execution': ['execute', 'middleware'], - 'language': ['ast', 'directive_locations', 'location', - 'source', 'token_kind', 'visitor'], - 'pyutils': ['simple_pub_sub', 'frozen_list', 'path'], - 'type': ['definition', 'directives', 'schema'], - 'utilities': ['find_breaking_changes', 'type_info'], - 'validation': ['rules', 'validation_context']} + "error": ["graphql_error"], + "execution": ["execute", "middleware"], + "language": [ + "ast", + "directive_locations", + "location", + "source", + "token_kind", + "visitor", + ], + "pyutils": ["simple_pub_sub", "frozen_list", "path"], + "type": ["definition", "directives", "schema"], + "utilities": ["find_breaking_changes", "type_info"], + "validation": ["rules", "validation_context"], +} # GraphQL-core classes that autodoc sometimes cannot find # (e.g. where specified as string in type hints). # We need to give autodoc a little help in this cases, too: graphql_classes = { - 'GraphQLAbstractType': 'type', - 'GraphQLObjectType': 'type', - 'GraphQLOutputType': 'type', - 'GraphQLTypeResolver': 'type', - 'Node': 'language', - 'Source': 'language', - 'SourceLocation': 'language' + "GraphQLAbstractType": "type", + "GraphQLObjectType": "type", + "GraphQLOutputType": "type", + "GraphQLTypeResolver": "type", + "Node": "language", + "Source": "language", + "SourceLocation": "language", } # ignore the following undocumented or internal references: -ignore_references = set(''' +ignore_references = set( + """ GNT GT KT T VT enum.Enum traceback @@ -137,6 +145,7 @@ EnterLeaveVisitor FormattedSourceLocation GraphQLAbstractType +GraphQLErrorExtensions GraphQLOutputType asyncio.events.AbstractEventLoop graphql.execution.map_async_iterator.MapAsyncIterator @@ -146,77 +155,79 @@ graphql.validation.validation_context.VariableUsage graphql.validation.rules.known_argument_names.KnownArgumentNamesOnDirectivesRule graphql.validation.rules.provided_required_arguments.ProvidedRequiredArgumentsOnDirectivesRule -'''.split()) +""".split() +) ignore_references.update(__builtins__.keys()) def on_missing_reference(app, env, node, contnode): """Fix or skip any missing references.""" - if node.get('refdomain') != 'py': + if node.get("refdomain") != "py": return None - target = node.get('reftarget') + target = node.get("reftarget") if not target: return None - if target in ignore_references or target.endswith('Kwargs'): + if target in ignore_references or target.endswith("Kwargs"): return contnode - typ = node.get('reftype') - name = target.rsplit('.', 1)[-1] - if name in ('GT', 'GNT', 'KT', 'T', 'VT'): + typ = node.get("reftype") + name = target.rsplit(".", 1)[-1] + if name in ("GT", "GNT", "KT", "T", "VT"): return contnode - if typ == 'obj': - if target.startswith('typing.'): - if name in ('Any', 'Optional', 'Union'): + if typ == "obj": + if target.startswith("typing."): + if name in ("Any", "Optional", "Union"): return contnode - if typ != 'class': + if typ != "class": return None - if '.' in target: # maybe too specific - base_module, target = target.split('.', 1) - if base_module == 'graphql': - if '.' not in target: + if "." in target: # maybe too specific + base_module, target = target.split(".", 1) + if base_module == "graphql": + if "." not in target: return None - base_module, target = target.split('.', 1) - if '.' not in target: + base_module, target = target.split(".", 1) + if "." not in target: return None sub_modules = graphql_modules.get(base_module) if not sub_modules: return - sub_module = target.split('.', 1)[0] + sub_module = target.split(".", 1)[0] if sub_module not in sub_modules: return None - target = 'graphql.' + base_module + '.' + target.rsplit('.', 1)[-1] + target = "graphql." + base_module + "." + target.rsplit(".", 1)[-1] else: # maybe not specific enough base_module = graphql_classes.get(target) if not base_module: return None - target = 'graphql.' + base_module + '.' + target + target = "graphql." + base_module + "." + target # replace target - if contnode.__class__.__name__ == 'Text': + if contnode.__class__.__name__ == "Text": contnode = contnode.__class__(target) - elif contnode.__class__.__name__ == 'literal': + elif contnode.__class__.__name__ == "literal": if len(contnode.children) != 1: return None textnode = contnode.children[0] contnode.children[0] = textnode.__class__(target) else: return None - node['reftarget'] = target - fromdoc = node.get('refdoc') + node["reftarget"] = target + fromdoc = node.get("refdoc") if not fromdoc: - doc_module = node.get('py:module') + doc_module = node.get("py:module") if doc_module: - if doc_module.startswith('graphql.'): - doc_module = doc_module.split('.', 1)[-1] - if doc_module not in graphql_modules and doc_module != 'graphql': + if doc_module.startswith("graphql."): + doc_module = doc_module.split(".", 1)[-1] + if doc_module not in graphql_modules and doc_module != "graphql": doc_module = None - fromdoc = 'modules/' + (doc_module or base_module) + fromdoc = "modules/" + (doc_module or base_module) # try resolving again with replaced target - return env.domains['py'].resolve_xref( - env, fromdoc, app.builder, typ, target, node, contnode) + return env.domains["py"].resolve_xref( + env, fromdoc, app.builder, typ, target, node, contnode + ) def on_skip_member(_app, what, name, _obj, skip, _options): - if what == 'class' and name == "__init__": + if what == "class" and name == "__init__": # we could set "special-members" to "__init__", # but this gives an error when documenting modules return False @@ -224,7 +235,7 @@ def on_skip_member(_app, what, name, _obj, skip, _options): def setup(app): - app.connect('missing-reference', on_missing_reference) + app.connect("missing-reference", on_missing_reference) app.connect("autodoc-skip-member", on_skip_member) @@ -252,7 +263,7 @@ def setup(app): # show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] @@ -268,15 +279,13 @@ def setup(app): # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'sphinx_rtd_theme' +html_theme = "sphinx_rtd_theme" # 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 = { - 'navigation_depth': 5 -} +html_theme_options = {"navigation_depth": 5} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] @@ -384,34 +393,36 @@ def setup(app): # html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. -htmlhelp_basename = 'GraphQL-core-3-doc' +htmlhelp_basename = "GraphQL-core-3-doc" # -- 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', + # 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, 'GraphQL-core-3.tex', 'GraphQL-core 3 Documentation', - 'Christoph Zwerschke', 'manual'), + ( + master_doc, + "GraphQL-core-3.tex", + "GraphQL-core 3 Documentation", + "Christoph Zwerschke", + "manual", + ), ] # The name of an image file (relative to this directory) to place at the top of @@ -445,10 +456,7 @@ def setup(app): # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'graphql-core', 'GraphQL-core 3 Documentation', - [author], 1) -] +man_pages = [(master_doc, "graphql-core", "GraphQL-core 3 Documentation", [author], 1)] # If true, show URL addresses after external links. # @@ -461,9 +469,15 @@ def setup(app): # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'GraphQL-core', 'GraphQL-core 3 Documentation', - author, 'GraphQL-core 3', 'One line description of project.', - 'Miscellaneous'), + ( + master_doc, + "GraphQL-core", + "GraphQL-core 3 Documentation", + author, + "GraphQL-core 3", + "One line description of project.", + "Miscellaneous", + ), ] # Documents to append as an appendix to all manuals. diff --git a/docs/requirements.txt b/docs/requirements.txt index 0ad38822..9652132e 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,2 +1,2 @@ -sphinx>=4.3,<5 -sphinx_rtd_theme>=1,<2 +sphinx>=7,<8 +sphinx_rtd_theme>=2,<3 diff --git a/poetry.lock b/poetry.lock index 7c0e0394..7c39d2f7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,1499 +1,17 @@ -[[package]] -name = "alabaster" -version = "0.7.12" -description = "A configurable sidebar-enabled Sphinx theme" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "appdirs" -version = "1.4.4" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "atomicwrites" -version = "1.4.1" -description = "Atomic file writes." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[[package]] -name = "attrs" -version = "22.1.0" -description = "Classes Without Boilerplate" -category = "dev" -optional = false -python-versions = ">=3.5" - -[package.extras] -dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"] -docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"] -tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"] -tests_no_zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"] - -[[package]] -name = "Babel" -version = "2.10.3" -description = "Internationalization utilities" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -pytz = ">=2015.7" - -[[package]] -name = "black" -version = "20.8b1" -description = "The uncompromising code formatter." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -appdirs = "*" -click = ">=7.1.2" -dataclasses = {version = ">=0.6", markers = "python_version < \"3.7\""} -mypy-extensions = ">=0.4.3" -pathspec = ">=0.6,<1" -regex = ">=2020.1.8" -toml = ">=0.10.1" -typed-ast = ">=1.4.0" -typing-extensions = ">=3.7.4" - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] - -[[package]] -name = "black" -version = "22.8.0" -description = "The uncompromising code formatter." -category = "dev" -optional = false -python-versions = ">=3.6.2" - -[package.dependencies] -click = ">=8.0.0" -dataclasses = {version = ">=0.6", markers = "python_version < \"3.7\""} -mypy-extensions = ">=0.4.3" -pathspec = ">=0.9.0" -platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} -typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} -typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - -[[package]] -name = "build" -version = "0.8.0" -description = "A simple, correct PEP 517 build frontend" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -colorama = {version = "*", markers = "os_name == \"nt\""} -importlib-metadata = {version = ">=0.22", markers = "python_version < \"3.8\""} -packaging = ">=19.0" -pep517 = ">=0.9.1" -tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} - -[package.extras] -docs = ["furo (>=2021.08.31)", "sphinx (>=4.0,<5.0)", "sphinx-argparse-cli (>=1.5)", "sphinx-autodoc-typehints (>=1.10)"] -test = ["filelock (>=3)", "pytest (>=6.2.4)", "pytest-cov (>=2.12)", "pytest-mock (>=2)", "pytest-rerunfailures (>=9.1)", "pytest-xdist (>=1.34)", "setuptools (>=42.0.0)", "setuptools (>=56.0.0)", "toml (>=0.10.0)", "wheel (>=0.36.0)"] -typing = ["importlib-metadata (>=4.6.4)", "mypy (==0.950)", "typing-extensions (>=3.7.4.3)"] -virtualenv = ["virtualenv (>=20.0.35)"] - -[[package]] -name = "bump2version" -version = "1.0.1" -description = "Version-bump your software with a single command!" -category = "dev" -optional = false -python-versions = ">=3.5" - -[[package]] -name = "certifi" -version = "2022.9.14" -description = "Python package for providing Mozilla's CA Bundle." -category = "dev" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "charset-normalizer" -version = "2.0.12" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "dev" -optional = false -python-versions = ">=3.5.0" - -[package.extras] -unicode_backport = ["unicodedata2"] - -[[package]] -name = "check-manifest" -version = "0.48" -description = "Check MANIFEST.in in a Python source package for completeness" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -build = ">=0.1" -setuptools = "*" -tomli = "*" - -[package.extras] -test = ["mock (>=3.0.0)", "pytest"] - -[[package]] -name = "click" -version = "8.0.4" -description = "Composable command line interface toolkit" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} - -[[package]] -name = "colorama" -version = "0.4.5" -description = "Cross-platform colored terminal text." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[[package]] -name = "coverage" -version = "6.2" -description = "Code coverage measurement for Python" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -tomli = {version = "*", optional = true, markers = "extra == \"toml\""} - -[package.extras] -toml = ["tomli"] - -[[package]] -name = "dataclasses" -version = "0.8" -description = "A backport of the dataclasses module for Python 3.6" -category = "dev" -optional = false -python-versions = ">=3.6, <3.7" - -[[package]] -name = "distlib" -version = "0.3.6" -description = "Distribution utilities" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "docutils" -version = "0.17.1" -description = "Docutils -- Python Documentation Utilities" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[[package]] -name = "filelock" -version = "3.4.1" -description = "A platform independent file lock." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.extras] -docs = ["furo (>=2021.8.17b43)", "sphinx (>=4.1)", "sphinx-autodoc-typehints (>=1.12)"] -testing = ["covdefaults (>=1.2.0)", "coverage (>=4)", "pytest (>=4)", "pytest-cov", "pytest-timeout (>=1.4.2)"] - -[[package]] -name = "filelock" -version = "3.8.0" -description = "A platform independent file lock." -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.extras] -docs = ["furo (>=2022.6.21)", "sphinx (>=5.1.1)", "sphinx-autodoc-typehints (>=1.19.1)"] -testing = ["covdefaults (>=2.2)", "coverage (>=6.4.2)", "pytest (>=7.1.2)", "pytest-cov (>=3)", "pytest-timeout (>=2.1)"] - -[[package]] -name = "flake8" -version = "4.0.1" -description = "the modular source code checker: pep8 pyflakes and co" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -importlib-metadata = {version = "<4.3", markers = "python_version < \"3.8\""} -mccabe = ">=0.6.0,<0.7.0" -pycodestyle = ">=2.8.0,<2.9.0" -pyflakes = ">=2.4.0,<2.5.0" - -[[package]] -name = "flake8" -version = "5.0.4" -description = "the modular source code checker: pep8 pyflakes and co" -category = "dev" -optional = false -python-versions = ">=3.6.1" - -[package.dependencies] -importlib-metadata = {version = ">=1.1.0,<4.3", markers = "python_version < \"3.8\""} -mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.9.0,<2.10.0" -pyflakes = ">=2.5.0,<2.6.0" - -[[package]] -name = "idna" -version = "3.4" -description = "Internationalized Domain Names in Applications (IDNA)" -category = "dev" -optional = false -python-versions = ">=3.5" - -[[package]] -name = "imagesize" -version = "1.4.1" -description = "Getting image size from png/jpeg/jpeg2000/gif file" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[[package]] -name = "importlib-metadata" -version = "4.2.0" -description = "Read metadata from Python packages" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} -zipp = ">=0.5" - -[package.extras] -docs = ["jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "sphinx"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pep517", "pyfakefs", "pytest (>=4.6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy"] - -[[package]] -name = "importlib-resources" -version = "5.4.0" -description = "Read resources from Python packages" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} - -[package.extras] -docs = ["jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "sphinx"] -testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy"] - -[[package]] -name = "iniconfig" -version = "1.1.1" -description = "iniconfig: brain-dead simple config-ini parsing" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "Jinja2" -version = "3.0.3" -description = "A very fast and expressive template engine." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -MarkupSafe = ">=2.0" - -[package.extras] -i18n = ["Babel (>=2.7)"] - -[[package]] -name = "MarkupSafe" -version = "2.0.1" -description = "Safely add untrusted strings to HTML/XML markup." -category = "dev" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "mccabe" -version = "0.6.1" -description = "McCabe checker, plugin for flake8" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "mccabe" -version = "0.7.0" -description = "McCabe checker, plugin for flake8" -category = "dev" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "mypy" -version = "0.971" -description = "Optional static typing for Python" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -mypy-extensions = ">=0.4.3" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typed-ast = {version = ">=1.4.0,<2", markers = "python_version < \"3.8\""} -typing-extensions = ">=3.10" - -[package.extras] -dmypy = ["psutil (>=4.0)"] -python2 = ["typed-ast (>=1.4.0,<2)"] -reports = ["lxml"] - -[[package]] -name = "mypy-extensions" -version = "0.4.3" -description = "Experimental type system extensions for programs checked with the mypy typechecker." -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "packaging" -version = "21.3" -description = "Core utilities for Python packages" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" - -[[package]] -name = "pathspec" -version = "0.9.0" -description = "Utility library for gitignore style pattern matching of file paths." -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" - -[[package]] -name = "pep517" -version = "0.13.0" -description = "Wrappers to build Python packages using PEP 517 hooks" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -importlib_metadata = {version = "*", markers = "python_version < \"3.8\""} -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -zipp = {version = "*", markers = "python_version < \"3.8\""} - -[[package]] -name = "platformdirs" -version = "2.4.0" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.extras] -docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] -test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] - -[[package]] -name = "pluggy" -version = "1.0.0" -description = "plugin and hook calling mechanisms for python" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] - -[[package]] -name = "py" -version = "1.11.0" -description = "library with cross-python path, ini-parsing, io, code, log facilities" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[[package]] -name = "py-cpuinfo" -version = "8.0.0" -description = "Get CPU info with pure Python 2 & 3" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "pycodestyle" -version = "2.8.0" -description = "Python style guide checker" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[[package]] -name = "pycodestyle" -version = "2.9.1" -description = "Python style guide checker" -category = "dev" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "pyflakes" -version = "2.4.0" -description = "passive checker of Python programs" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[[package]] -name = "pyflakes" -version = "2.5.0" -description = "passive checker of Python programs" -category = "dev" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "Pygments" -version = "2.13.0" -description = "Pygments is a syntax highlighting package written in Python." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.extras] -plugins = ["importlib-metadata"] - -[[package]] -name = "pyparsing" -version = "3.0.7" -description = "Python parsing module" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.extras] -diagrams = ["jinja2", "railroad-diagrams"] - -[[package]] -name = "pytest" -version = "6.2.5" -description = "pytest: simple powerful testing with Python" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} -attrs = ">=19.2.0" -colorama = {version = "*", markers = "sys_platform == \"win32\""} -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} -iniconfig = "*" -packaging = "*" -pluggy = ">=0.12,<2.0" -py = ">=1.8.2" -toml = "*" - -[package.extras] -testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] - -[[package]] -name = "pytest-asyncio" -version = "0.16.0" -description = "Pytest support for asyncio." -category = "dev" -optional = false -python-versions = ">= 3.6" - -[package.dependencies] -pytest = ">=5.4.0" - -[package.extras] -testing = ["coverage", "hypothesis (>=5.7.1)"] - -[[package]] -name = "pytest-asyncio" -version = "0.19.0" -description = "Pytest support for asyncio" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -pytest = ">=6.1.0" -typing-extensions = {version = ">=3.7.2", markers = "python_version < \"3.8\""} - -[package.extras] -testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"] - -[[package]] -name = "pytest-benchmark" -version = "3.4.1" -description = "A ``pytest`` fixture for benchmarking code. It will group the tests into rounds that are calibrated to the chosen timer." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[package.dependencies] -py-cpuinfo = "*" -pytest = ">=3.8" - -[package.extras] -aspect = ["aspectlib"] -elasticsearch = ["elasticsearch"] -histogram = ["pygal", "pygaljs"] - -[[package]] -name = "pytest-cov" -version = "3.0.0" -description = "Pytest plugin for measuring coverage." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -coverage = {version = ">=5.2.1", extras = ["toml"]} -pytest = ">=4.6" - -[package.extras] -testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] - -[[package]] -name = "pytest-describe" -version = "2.0.1" -description = "Describe-style plugin for pytest" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -pytest = ">=4.0.0" - -[[package]] -name = "pytest-timeout" -version = "2.1.0" -description = "pytest plugin to abort hanging tests" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -pytest = ">=5.0.0" - -[[package]] -name = "pytz" -version = "2022.2.1" -description = "World timezone definitions, modern and historical" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "regex" -version = "2022.9.13" -description = "Alternative regular expression module, to replace re." -category = "dev" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "requests" -version = "2.27.1" -description = "Python HTTP for Humans." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} -idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} -urllib3 = ">=1.21.1,<1.27" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] -use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] - -[[package]] -name = "setuptools" -version = "59.6.0" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=8.2)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx", "sphinx-inline-tabs", "sphinxcontrib-towncrier"] -testing = ["flake8-2020", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mock", "paver", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy", "pytest-virtualenv (>=1.2.7)", "pytest-xdist", "sphinx", "virtualenv (>=13.0.0)", "wheel"] - -[[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" - -[[package]] -name = "snowballstemmer" -version = "2.2.0" -description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "Sphinx" -version = "4.3.2" -description = "Python documentation generator" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -alabaster = ">=0.7,<0.8" -babel = ">=1.3" -colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""} -docutils = ">=0.14,<0.18" -imagesize = "*" -Jinja2 = ">=2.3" -packaging = "*" -Pygments = ">=2.0" -requests = ">=2.5.0" -setuptools = "*" -snowballstemmer = ">=1.1" -sphinxcontrib-applehelp = "*" -sphinxcontrib-devhelp = "*" -sphinxcontrib-htmlhelp = ">=2.0.0" -sphinxcontrib-jsmath = "*" -sphinxcontrib-qthelp = "*" -sphinxcontrib-serializinghtml = ">=1.1.5" - -[package.extras] -docs = ["sphinxcontrib-websupport"] -lint = ["docutils-stubs", "flake8 (>=3.5.0)", "isort", "mypy (>=0.920)", "types-pkg-resources", "types-requests", "types-typed-ast"] -test = ["cython", "html5lib", "pytest", "pytest-cov", "typed-ast"] - -[[package]] -name = "sphinx-rtd-theme" -version = "1.0.0" -description = "Read the Docs theme for Sphinx" -category = "dev" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" - -[package.dependencies] -docutils = "<0.18" -sphinx = ">=1.6" - -[package.extras] -dev = ["bump2version", "sphinxcontrib-httpdomain", "transifex-client"] - -[[package]] -name = "sphinxcontrib-applehelp" -version = "1.0.2" -description = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple help books" -category = "dev" -optional = false -python-versions = ">=3.5" - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["pytest"] - -[[package]] -name = "sphinxcontrib-devhelp" -version = "1.0.2" -description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." -category = "dev" -optional = false -python-versions = ">=3.5" - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["pytest"] - -[[package]] -name = "sphinxcontrib-htmlhelp" -version = "2.0.0" -description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["html5lib", "pytest"] - -[[package]] -name = "sphinxcontrib-jsmath" -version = "1.0.1" -description = "A sphinx extension which renders display math in HTML via JavaScript" -category = "dev" -optional = false -python-versions = ">=3.5" - -[package.extras] -test = ["flake8", "mypy", "pytest"] - -[[package]] -name = "sphinxcontrib-qthelp" -version = "1.0.3" -description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." -category = "dev" -optional = false -python-versions = ">=3.5" - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["pytest"] - -[[package]] -name = "sphinxcontrib-serializinghtml" -version = "1.1.5" -description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." -category = "dev" -optional = false -python-versions = ">=3.5" - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["pytest"] - -[[package]] -name = "toml" -version = "0.10.2" -description = "Python Library for Tom's Obvious, Minimal Language" -category = "dev" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" - -[[package]] -name = "tomli" -version = "1.2.3" -description = "A lil' TOML parser" -category = "dev" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -category = "dev" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "tox" -version = "3.25.0" -description = "tox is a generic virtualenv management and test command line tool" -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" - -[package.dependencies] -colorama = {version = ">=0.4.1", markers = "platform_system == \"Windows\""} -filelock = ">=3.0.0" -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} -packaging = ">=14" -pluggy = ">=0.12.0" -py = ">=1.4.17" -six = ">=1.14.0" -toml = ">=0.9.4" -virtualenv = ">=16.0.0,<20.0.0 || >20.0.0,<20.0.1 || >20.0.1,<20.0.2 || >20.0.2,<20.0.3 || >20.0.3,<20.0.4 || >20.0.4,<20.0.5 || >20.0.5,<20.0.6 || >20.0.6,<20.0.7 || >20.0.7" - -[package.extras] -docs = ["pygments-github-lexers (>=0.0.5)", "sphinx (>=2.0.0)", "sphinxcontrib-autoprogram (>=0.1.5)", "towncrier (>=18.5.0)"] -testing = ["flaky (>=3.4.0)", "freezegun (>=0.3.11)", "pathlib2 (>=2.3.3)", "psutil (>=5.6.1)", "pytest (>=4.0.0)", "pytest-cov (>=2.5.1)", "pytest-mock (>=1.10.0)", "pytest-randomly (>=1.0.0)"] - -[[package]] -name = "tox" -version = "3.26.0" -description = "tox is a generic virtualenv management and test command line tool" -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" - -[package.dependencies] -colorama = {version = ">=0.4.1", markers = "platform_system == \"Windows\""} -filelock = ">=3.0.0" -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} -packaging = ">=14" -pluggy = ">=0.12.0" -py = ">=1.4.17" -six = ">=1.14.0" -tomli = {version = ">=2.0.1", markers = "python_version >= \"3.7\" and python_version < \"3.11\""} -virtualenv = ">=16.0.0,<20.0.0 || >20.0.0,<20.0.1 || >20.0.1,<20.0.2 || >20.0.2,<20.0.3 || >20.0.3,<20.0.4 || >20.0.4,<20.0.5 || >20.0.5,<20.0.6 || >20.0.6,<20.0.7 || >20.0.7" - -[package.extras] -docs = ["pygments-github-lexers (>=0.0.5)", "sphinx (>=2.0.0)", "sphinxcontrib-autoprogram (>=0.1.5)", "towncrier (>=18.5.0)"] -testing = ["flaky (>=3.4.0)", "freezegun (>=0.3.11)", "pathlib2 (>=2.3.3)", "psutil (>=5.6.1)", "pytest (>=4.0.0)", "pytest-cov (>=2.5.1)", "pytest-mock (>=1.10.0)", "pytest-randomly (>=1.0.0)"] - -[[package]] -name = "typed-ast" -version = "1.5.4" -description = "a fork of Python 2 and 3 ast modules with type comment support" -category = "dev" -optional = false -python-versions = ">=3.6" +# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. [[package]] name = "typing-extensions" version = "4.1.1" description = "Backported and Experimental Type Hints for Python 3.6+" -category = "main" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "typing-extensions" -version = "4.3.0" -description = "Backported and Experimental Type Hints for Python 3.7+" -category = "main" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "urllib3" -version = "1.26.12" -description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4" - -[package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] -secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] -socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] - -[[package]] -name = "virtualenv" -version = "20.16.2" -description = "Virtual Python Environment builder" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -distlib = ">=0.3.1,<1" -filelock = ">=3.2,<4" -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} -importlib-resources = {version = ">=1.0", markers = "python_version < \"3.7\""} -platformdirs = ">=2,<3" - -[package.extras] -docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=21.3)"] -testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "packaging (>=20.0)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)"] - -[[package]] -name = "zipp" -version = "3.6.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -category = "dev" optional = false python-versions = ">=3.6" - -[package.extras] -docs = ["jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "sphinx"] -testing = ["func-timeout", "jaraco.itertools", "pytest (>=4.6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy"] - -[metadata] -lock-version = "1.1" -python-versions = "^3.6" -content-hash = "dc1f4a6e11fed7704f09dcd5e3b3af090398383b36458c966a52c840b7fc6c78" - -[metadata.files] -alabaster = [ - {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"}, - {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"}, -] -appdirs = [ - {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, - {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, -] -atomicwrites = [ - {file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"}, -] -attrs = [ - {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"}, - {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"}, -] -Babel = [ - {file = "Babel-2.10.3-py3-none-any.whl", hash = "sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb"}, - {file = "Babel-2.10.3.tar.gz", hash = "sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51"}, -] -black = [ - {file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"}, - {file = "black-22.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ce957f1d6b78a8a231b18e0dd2d94a33d2ba738cd88a7fe64f53f659eea49fdd"}, - {file = "black-22.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5107ea36b2b61917956d018bd25129baf9ad1125e39324a9b18248d362156a27"}, - {file = "black-22.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8166b7bfe5dcb56d325385bd1d1e0f635f24aae14b3ae437102dedc0c186747"}, - {file = "black-22.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd82842bb272297503cbec1a2600b6bfb338dae017186f8f215c8958f8acf869"}, - {file = "black-22.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d839150f61d09e7217f52917259831fe2b689f5c8e5e32611736351b89bb2a90"}, - {file = "black-22.8.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a05da0430bd5ced89176db098567973be52ce175a55677436a271102d7eaa3fe"}, - {file = "black-22.8.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a098a69a02596e1f2a58a2a1c8d5a05d5a74461af552b371e82f9fa4ada8342"}, - {file = "black-22.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5594efbdc35426e35a7defa1ea1a1cb97c7dbd34c0e49af7fb593a36bd45edab"}, - {file = "black-22.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a983526af1bea1e4cf6768e649990f28ee4f4137266921c2c3cee8116ae42ec3"}, - {file = "black-22.8.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b2c25f8dea5e8444bdc6788a2f543e1fb01494e144480bc17f806178378005e"}, - {file = "black-22.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:78dd85caaab7c3153054756b9fe8c611efa63d9e7aecfa33e533060cb14b6d16"}, - {file = "black-22.8.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:cea1b2542d4e2c02c332e83150e41e3ca80dc0fb8de20df3c5e98e242156222c"}, - {file = "black-22.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5b879eb439094751185d1cfdca43023bc6786bd3c60372462b6f051efa6281a5"}, - {file = "black-22.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0a12e4e1353819af41df998b02c6742643cfef58282915f781d0e4dd7a200411"}, - {file = "black-22.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3a73f66b6d5ba7288cd5d6dad9b4c9b43f4e8a4b789a94bf5abfb878c663eb3"}, - {file = "black-22.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:e981e20ec152dfb3e77418fb616077937378b322d7b26aa1ff87717fb18b4875"}, - {file = "black-22.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8ce13ffed7e66dda0da3e0b2eb1bdfc83f5812f66e09aca2b0978593ed636b6c"}, - {file = "black-22.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:32a4b17f644fc288c6ee2bafdf5e3b045f4eff84693ac069d87b1a347d861497"}, - {file = "black-22.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ad827325a3a634bae88ae7747db1a395d5ee02cf05d9aa7a9bd77dfb10e940c"}, - {file = "black-22.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53198e28a1fb865e9fe97f88220da2e44df6da82b18833b588b1883b16bb5d41"}, - {file = "black-22.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:bc4d4123830a2d190e9cc42a2e43570f82ace35c3aeb26a512a2102bce5af7ec"}, - {file = "black-22.8.0-py3-none-any.whl", hash = "sha256:d2c21d439b2baf7aa80d6dd4e3659259be64c6f49dfd0f32091063db0e006db4"}, - {file = "black-22.8.0.tar.gz", hash = "sha256:792f7eb540ba9a17e8656538701d3eb1afcb134e3b45b71f20b25c77a8db7e6e"}, -] -build = [ - {file = "build-0.8.0-py3-none-any.whl", hash = "sha256:19b0ed489f92ace6947698c3ca8436cb0556a66e2aa2d34cd70e2a5d27cd0437"}, - {file = "build-0.8.0.tar.gz", hash = "sha256:887a6d471c901b1a6e6574ebaeeebb45e5269a79d095fe9a8f88d6614ed2e5f0"}, -] -bump2version = [ - {file = "bump2version-1.0.1-py2.py3-none-any.whl", hash = "sha256:37f927ea17cde7ae2d7baf832f8e80ce3777624554a653006c9144f8017fe410"}, - {file = "bump2version-1.0.1.tar.gz", hash = "sha256:762cb2bfad61f4ec8e2bdf452c7c267416f8c70dd9ecb1653fd0bbb01fa936e6"}, -] -certifi = [ - {file = "certifi-2022.9.14-py3-none-any.whl", hash = "sha256:e232343de1ab72c2aa521b625c80f699e356830fd0e2c620b465b304b17b0516"}, - {file = "certifi-2022.9.14.tar.gz", hash = "sha256:36973885b9542e6bd01dea287b2b4b3b21236307c56324fcc3f1160f2d655ed5"}, -] -charset-normalizer = [ - {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"}, - {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"}, -] -check-manifest = [ - {file = "check-manifest-0.48.tar.gz", hash = "sha256:3b575f1dade7beb3078ef4bf33a94519834457c7281dbc726b15c5466b55c657"}, - {file = "check_manifest-0.48-py3-none-any.whl", hash = "sha256:b1923685f98c1c2468601a1a7bed655db549a25d43c583caded3860ad8308f8c"}, -] -click = [ - {file = "click-8.0.4-py3-none-any.whl", hash = "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1"}, - {file = "click-8.0.4.tar.gz", hash = "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb"}, -] -colorama = [ - {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, - {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, -] -coverage = [ - {file = "coverage-6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6dbc1536e105adda7a6312c778f15aaabe583b0e9a0b0a324990334fd458c94b"}, - {file = "coverage-6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:174cf9b4bef0db2e8244f82059a5a72bd47e1d40e71c68ab055425172b16b7d0"}, - {file = "coverage-6.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:92b8c845527eae547a2a6617d336adc56394050c3ed8a6918683646328fbb6da"}, - {file = "coverage-6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c7912d1526299cb04c88288e148c6c87c0df600eca76efd99d84396cfe00ef1d"}, - {file = "coverage-6.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5d2033d5db1d58ae2d62f095e1aefb6988af65b4b12cb8987af409587cc0739"}, - {file = "coverage-6.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3feac4084291642165c3a0d9eaebedf19ffa505016c4d3db15bfe235718d4971"}, - {file = "coverage-6.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:276651978c94a8c5672ea60a2656e95a3cce2a3f31e9fb2d5ebd4c215d095840"}, - {file = "coverage-6.2-cp310-cp310-win32.whl", hash = "sha256:f506af4f27def639ba45789fa6fde45f9a217da0be05f8910458e4557eed020c"}, - {file = "coverage-6.2-cp310-cp310-win_amd64.whl", hash = "sha256:3f7c17209eef285c86f819ff04a6d4cbee9b33ef05cbcaae4c0b4e8e06b3ec8f"}, - {file = "coverage-6.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:13362889b2d46e8d9f97c421539c97c963e34031ab0cb89e8ca83a10cc71ac76"}, - {file = "coverage-6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:22e60a3ca5acba37d1d4a2ee66e051f5b0e1b9ac950b5b0cf4aa5366eda41d47"}, - {file = "coverage-6.2-cp311-cp311-win_amd64.whl", hash = "sha256:b637c57fdb8be84e91fac60d9325a66a5981f8086c954ea2772efe28425eaf64"}, - {file = "coverage-6.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f467bbb837691ab5a8ca359199d3429a11a01e6dfb3d9dcc676dc035ca93c0a9"}, - {file = "coverage-6.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2641f803ee9f95b1f387f3e8f3bf28d83d9b69a39e9911e5bfee832bea75240d"}, - {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1219d760ccfafc03c0822ae2e06e3b1248a8e6d1a70928966bafc6838d3c9e48"}, - {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9a2b5b52be0a8626fcbffd7e689781bf8c2ac01613e77feda93d96184949a98e"}, - {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:8e2c35a4c1f269704e90888e56f794e2d9c0262fb0c1b1c8c4ee44d9b9e77b5d"}, - {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5d6b09c972ce9200264c35a1d53d43ca55ef61836d9ec60f0d44273a31aa9f17"}, - {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e3db840a4dee542e37e09f30859f1612da90e1c5239a6a2498c473183a50e781"}, - {file = "coverage-6.2-cp36-cp36m-win32.whl", hash = "sha256:4e547122ca2d244f7c090fe3f4b5a5861255ff66b7ab6d98f44a0222aaf8671a"}, - {file = "coverage-6.2-cp36-cp36m-win_amd64.whl", hash = "sha256:01774a2c2c729619760320270e42cd9e797427ecfddd32c2a7b639cdc481f3c0"}, - {file = "coverage-6.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb8b8ee99b3fffe4fd86f4c81b35a6bf7e4462cba019997af2fe679365db0c49"}, - {file = "coverage-6.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:619346d57c7126ae49ac95b11b0dc8e36c1dd49d148477461bb66c8cf13bb521"}, - {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0a7726f74ff63f41e95ed3a89fef002916c828bb5fcae83b505b49d81a066884"}, - {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cfd9386c1d6f13b37e05a91a8583e802f8059bebfccde61a418c5808dea6bbfa"}, - {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:17e6c11038d4ed6e8af1407d9e89a2904d573be29d51515f14262d7f10ef0a64"}, - {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c254b03032d5a06de049ce8bca8338a5185f07fb76600afff3c161e053d88617"}, - {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dca38a21e4423f3edb821292e97cec7ad38086f84313462098568baedf4331f8"}, - {file = "coverage-6.2-cp37-cp37m-win32.whl", hash = "sha256:600617008aa82032ddeace2535626d1bc212dfff32b43989539deda63b3f36e4"}, - {file = "coverage-6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:bf154ba7ee2fd613eb541c2bc03d3d9ac667080a737449d1a3fb342740eb1a74"}, - {file = "coverage-6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f9afb5b746781fc2abce26193d1c817b7eb0e11459510fba65d2bd77fe161d9e"}, - {file = "coverage-6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edcada2e24ed68f019175c2b2af2a8b481d3d084798b8c20d15d34f5c733fa58"}, - {file = "coverage-6.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a9c8c4283e17690ff1a7427123ffb428ad6a52ed720d550e299e8291e33184dc"}, - {file = "coverage-6.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f614fc9956d76d8a88a88bb41ddc12709caa755666f580af3a688899721efecd"}, - {file = "coverage-6.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9365ed5cce5d0cf2c10afc6add145c5037d3148585b8ae0e77cc1efdd6aa2953"}, - {file = "coverage-6.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8bdfe9ff3a4ea37d17f172ac0dff1e1c383aec17a636b9b35906babc9f0f5475"}, - {file = "coverage-6.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:63c424e6f5b4ab1cf1e23a43b12f542b0ec2e54f99ec9f11b75382152981df57"}, - {file = "coverage-6.2-cp38-cp38-win32.whl", hash = "sha256:49dbff64961bc9bdd2289a2bda6a3a5a331964ba5497f694e2cbd540d656dc1c"}, - {file = "coverage-6.2-cp38-cp38-win_amd64.whl", hash = "sha256:9a29311bd6429be317c1f3fe4bc06c4c5ee45e2fa61b2a19d4d1d6111cb94af2"}, - {file = "coverage-6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03b20e52b7d31be571c9c06b74746746d4eb82fc260e594dc662ed48145e9efd"}, - {file = "coverage-6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:215f8afcc02a24c2d9a10d3790b21054b58d71f4b3c6f055d4bb1b15cecce685"}, - {file = "coverage-6.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a4bdeb0a52d1d04123b41d90a4390b096f3ef38eee35e11f0b22c2d031222c6c"}, - {file = "coverage-6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c332d8f8d448ded473b97fefe4a0983265af21917d8b0cdcb8bb06b2afe632c3"}, - {file = "coverage-6.2-cp39-cp39-win32.whl", hash = "sha256:6e1394d24d5938e561fbeaa0cd3d356207579c28bd1792f25a068743f2d5b282"}, - {file = "coverage-6.2-cp39-cp39-win_amd64.whl", hash = "sha256:86f2e78b1eff847609b1ca8050c9e1fa3bd44ce755b2ec30e70f2d3ba3844644"}, - {file = "coverage-6.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:5829192582c0ec8ca4a2532407bc14c2f338d9878a10442f5d03804a95fac9de"}, - {file = "coverage-6.2.tar.gz", hash = "sha256:e2cad8093172b7d1595b4ad66f24270808658e11acf43a8f95b41276162eb5b8"}, -] -dataclasses = [ - {file = "dataclasses-0.8-py3-none-any.whl", hash = "sha256:0201d89fa866f68c8ebd9d08ee6ff50c0b255f8ec63a71c16fda7af82bb887bf"}, - {file = "dataclasses-0.8.tar.gz", hash = "sha256:8479067f342acf957dc82ec415d355ab5edb7e7646b90dc6e2fd1d96ad084c97"}, -] -distlib = [ - {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, - {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, -] -docutils = [ - {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"}, - {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"}, -] -filelock = [ - {file = "filelock-3.4.1-py3-none-any.whl", hash = "sha256:a4bc51381e01502a30e9f06dd4fa19a1712eab852b6fb0f84fd7cce0793d8ca3"}, - {file = "filelock-3.4.1.tar.gz", hash = "sha256:0f12f552b42b5bf60dba233710bf71337d35494fc8bdd4fd6d9f6d082ad45e06"}, - {file = "filelock-3.8.0-py3-none-any.whl", hash = "sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4"}, - {file = "filelock-3.8.0.tar.gz", hash = "sha256:55447caa666f2198c5b6b13a26d2084d26fa5b115c00d065664b2124680c4edc"}, -] -flake8 = [ - {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"}, - {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"}, - {file = "flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"}, - {file = "flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"}, -] -idna = [ - {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, - {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, -] -imagesize = [ - {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, - {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, -] -importlib-metadata = [ - {file = "importlib_metadata-4.2.0-py3-none-any.whl", hash = "sha256:057e92c15bc8d9e8109738a48db0ccb31b4d9d5cfbee5a8670879a30be66304b"}, - {file = "importlib_metadata-4.2.0.tar.gz", hash = "sha256:b7e52a1f8dec14a75ea73e0891f3060099ca1d8e6a462a4dff11c3e119ea1b31"}, -] -importlib-resources = [ - {file = "importlib_resources-5.4.0-py3-none-any.whl", hash = "sha256:33a95faed5fc19b4bc16b29a6eeae248a3fe69dd55d4d229d2b480e23eeaad45"}, - {file = "importlib_resources-5.4.0.tar.gz", hash = "sha256:d756e2f85dd4de2ba89be0b21dba2a3bbec2e871a42a3a16719258a11f87506b"}, -] -iniconfig = [ - {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, - {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, -] -Jinja2 = [ - {file = "Jinja2-3.0.3-py3-none-any.whl", hash = "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8"}, - {file = "Jinja2-3.0.3.tar.gz", hash = "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7"}, -] -MarkupSafe = [ - {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"}, - {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"}, -] -mccabe = [ - {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, - {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, - {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, - {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, -] -mypy = [ - {file = "mypy-0.971-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f2899a3cbd394da157194f913a931edfd4be5f274a88041c9dc2d9cdcb1c315c"}, - {file = "mypy-0.971-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:98e02d56ebe93981c41211c05adb630d1d26c14195d04d95e49cd97dbc046dc5"}, - {file = "mypy-0.971-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:19830b7dba7d5356d3e26e2427a2ec91c994cd92d983142cbd025ebe81d69cf3"}, - {file = "mypy-0.971-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:02ef476f6dcb86e6f502ae39a16b93285fef97e7f1ff22932b657d1ef1f28655"}, - {file = "mypy-0.971-cp310-cp310-win_amd64.whl", hash = "sha256:25c5750ba5609a0c7550b73a33deb314ecfb559c350bb050b655505e8aed4103"}, - {file = "mypy-0.971-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d3348e7eb2eea2472db611486846742d5d52d1290576de99d59edeb7cd4a42ca"}, - {file = "mypy-0.971-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3fa7a477b9900be9b7dd4bab30a12759e5abe9586574ceb944bc29cddf8f0417"}, - {file = "mypy-0.971-cp36-cp36m-win_amd64.whl", hash = "sha256:2ad53cf9c3adc43cf3bea0a7d01a2f2e86db9fe7596dfecb4496a5dda63cbb09"}, - {file = "mypy-0.971-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:855048b6feb6dfe09d3353466004490b1872887150c5bb5caad7838b57328cc8"}, - {file = "mypy-0.971-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:23488a14a83bca6e54402c2e6435467a4138785df93ec85aeff64c6170077fb0"}, - {file = "mypy-0.971-cp37-cp37m-win_amd64.whl", hash = "sha256:4b21e5b1a70dfb972490035128f305c39bc4bc253f34e96a4adf9127cf943eb2"}, - {file = "mypy-0.971-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:9796a2ba7b4b538649caa5cecd398d873f4022ed2333ffde58eaf604c4d2cb27"}, - {file = "mypy-0.971-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5a361d92635ad4ada1b1b2d3630fc2f53f2127d51cf2def9db83cba32e47c856"}, - {file = "mypy-0.971-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b793b899f7cf563b1e7044a5c97361196b938e92f0a4343a5d27966a53d2ec71"}, - {file = "mypy-0.971-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d1ea5d12c8e2d266b5fb8c7a5d2e9c0219fedfeb493b7ed60cd350322384ac27"}, - {file = "mypy-0.971-cp38-cp38-win_amd64.whl", hash = "sha256:23c7ff43fff4b0df93a186581885c8512bc50fc4d4910e0f838e35d6bb6b5e58"}, - {file = "mypy-0.971-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1f7656b69974a6933e987ee8ffb951d836272d6c0f81d727f1d0e2696074d9e6"}, - {file = "mypy-0.971-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d2022bfadb7a5c2ef410d6a7c9763188afdb7f3533f22a0a32be10d571ee4bbe"}, - {file = "mypy-0.971-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef943c72a786b0f8d90fd76e9b39ce81fb7171172daf84bf43eaf937e9f220a9"}, - {file = "mypy-0.971-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d744f72eb39f69312bc6c2abf8ff6656973120e2eb3f3ec4f758ed47e414a4bf"}, - {file = "mypy-0.971-cp39-cp39-win_amd64.whl", hash = "sha256:77a514ea15d3007d33a9e2157b0ba9c267496acf12a7f2b9b9f8446337aac5b0"}, - {file = "mypy-0.971-py3-none-any.whl", hash = "sha256:0d054ef16b071149917085f51f89555a576e2618d5d9dd70bd6eea6410af3ac9"}, - {file = "mypy-0.971.tar.gz", hash = "sha256:40b0f21484238269ae6a57200c807d80debc6459d444c0489a102d7c6a75fa56"}, -] -mypy-extensions = [ - {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, - {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, -] -packaging = [ - {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, - {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, -] -pathspec = [ - {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, - {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, -] -pep517 = [ - {file = "pep517-0.13.0-py3-none-any.whl", hash = "sha256:4ba4446d80aed5b5eac6509ade100bff3e7943a8489de249654a5ae9b33ee35b"}, - {file = "pep517-0.13.0.tar.gz", hash = "sha256:ae69927c5c172be1add9203726d4b84cf3ebad1edcd5f71fcdc746e66e829f59"}, -] -platformdirs = [ - {file = "platformdirs-2.4.0-py3-none-any.whl", hash = "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"}, - {file = "platformdirs-2.4.0.tar.gz", hash = "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2"}, -] -pluggy = [ - {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, - {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, -] -py = [ - {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, - {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, -] -py-cpuinfo = [ - {file = "py-cpuinfo-8.0.0.tar.gz", hash = "sha256:5f269be0e08e33fd959de96b34cd4aeeeacac014dd8305f70eb28d06de2345c5"}, -] -pycodestyle = [ - {file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"}, - {file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"}, - {file = "pycodestyle-2.9.1-py2.py3-none-any.whl", hash = "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"}, - {file = "pycodestyle-2.9.1.tar.gz", hash = "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785"}, -] -pyflakes = [ - {file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"}, - {file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"}, - {file = "pyflakes-2.5.0-py2.py3-none-any.whl", hash = "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2"}, - {file = "pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"}, -] -Pygments = [ - {file = "Pygments-2.13.0-py3-none-any.whl", hash = "sha256:f643f331ab57ba3c9d89212ee4a2dabc6e94f117cf4eefde99a0574720d14c42"}, - {file = "Pygments-2.13.0.tar.gz", hash = "sha256:56a8508ae95f98e2b9bdf93a6be5ae3f7d8af858b43e02c5a2ff083726be40c1"}, -] -pyparsing = [ - {file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"}, - {file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"}, -] -pytest = [ - {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, - {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, -] -pytest-asyncio = [ - {file = "pytest-asyncio-0.16.0.tar.gz", hash = "sha256:7496c5977ce88c34379df64a66459fe395cd05543f0a2f837016e7144391fcfb"}, - {file = "pytest_asyncio-0.16.0-py3-none-any.whl", hash = "sha256:5f2a21273c47b331ae6aa5b36087047b4899e40f03f18397c0e65fa5cca54e9b"}, - {file = "pytest-asyncio-0.19.0.tar.gz", hash = "sha256:ac4ebf3b6207259750bc32f4c1d8fcd7e79739edbc67ad0c58dd150b1d072fed"}, - {file = "pytest_asyncio-0.19.0-py3-none-any.whl", hash = "sha256:7a97e37cfe1ed296e2e84941384bdd37c376453912d397ed39293e0916f521fa"}, -] -pytest-benchmark = [ - {file = "pytest-benchmark-3.4.1.tar.gz", hash = "sha256:40e263f912de5a81d891619032983557d62a3d85843f9a9f30b98baea0cd7b47"}, - {file = "pytest_benchmark-3.4.1-py2.py3-none-any.whl", hash = "sha256:36d2b08c4882f6f997fd3126a3d6dfd70f3249cde178ed8bbc0b73db7c20f809"}, -] -pytest-cov = [ - {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"}, - {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"}, -] -pytest-describe = [ - {file = "pytest-describe-2.0.1.tar.gz", hash = "sha256:e5cbaa31169f0060348ad5ca0191027e5f1f41f3f27fdeef208365e09c55eb9a"}, - {file = "pytest_describe-2.0.1-py3-none-any.whl", hash = "sha256:ea347838bdf774b498ee7cb4a0b802a40be89e667a399fb63d860e3223bf4183"}, -] -pytest-timeout = [ - {file = "pytest-timeout-2.1.0.tar.gz", hash = "sha256:c07ca07404c612f8abbe22294b23c368e2e5104b521c1790195561f37e1ac3d9"}, - {file = "pytest_timeout-2.1.0-py3-none-any.whl", hash = "sha256:f6f50101443ce70ad325ceb4473c4255e9d74e3c7cd0ef827309dfa4c0d975c6"}, -] -pytz = [ - {file = "pytz-2022.2.1-py2.py3-none-any.whl", hash = "sha256:220f481bdafa09c3955dfbdddb7b57780e9a94f5127e35456a48589b9e0c0197"}, - {file = "pytz-2022.2.1.tar.gz", hash = "sha256:cea221417204f2d1a2aa03ddae3e867921971d0d76f14d87abb4414415bbdcf5"}, -] -regex = [ - {file = "regex-2022.9.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0394265391a86e2bbaa7606e59ac71bd9f1edf8665a59e42771a9c9adbf6fd4f"}, - {file = "regex-2022.9.13-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:86df2049b18745f3cd4b0f4c4ef672bfac4b80ca488e6ecfd2bbfe68d2423a2c"}, - {file = "regex-2022.9.13-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce331b076b2b013e7d7f07157f957974ef0b0881a808e8a4a4b3b5105aee5d04"}, - {file = "regex-2022.9.13-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:360ffbc9357794ae41336b681dff1c0463193199dfb91fcad3ec385ea4972f46"}, - {file = "regex-2022.9.13-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18e503b1e515a10282b3f14f1b3d856194ecece4250e850fad230842ed31227f"}, - {file = "regex-2022.9.13-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6e167d1ccd41d27b7b6655bb7a2dcb1b1eb1e0d2d662043470bd3b4315d8b2b"}, - {file = "regex-2022.9.13-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4146cb7ae6029fc83b5c905ec6d806b7e5568dc14297c423e66b86294bad6c39"}, - {file = "regex-2022.9.13-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a1aec4ae549fd7b3f52ceaf67e133010e2fba1538bf4d5fc5cd162a5e058d5df"}, - {file = "regex-2022.9.13-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:cab548d6d972e1de584161487b2ac1aa82edd8430d1bde69587ba61698ad1cfb"}, - {file = "regex-2022.9.13-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3d64e1a7e6d98a4cdc8b29cb8d8ed38f73f49e55fbaa737bdb5933db99b9de22"}, - {file = "regex-2022.9.13-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:67a4c625361db04ae40ef7c49d3cbe2c1f5ff10b5a4491327ab20f19f2fb5d40"}, - {file = "regex-2022.9.13-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:5d0dd8b06896423211ce18fba0c75dacc49182a1d6514c004b535be7163dca0f"}, - {file = "regex-2022.9.13-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4318f69b79f9f7d84a7420e97d4bfe872dc767c72f891d4fea5fa721c74685f7"}, - {file = "regex-2022.9.13-cp310-cp310-win32.whl", hash = "sha256:26df88c9636a0c3f3bd9189dd435850a0c49d0b7d6e932500db3f99a6dd604d1"}, - {file = "regex-2022.9.13-cp310-cp310-win_amd64.whl", hash = "sha256:6fe1dd1021e0f8f3f454ce2811f1b0b148f2d25bb38c712fec00316551e93650"}, - {file = "regex-2022.9.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:83cc32a1a2fa5bac00f4abc0e6ce142e3c05d3a6d57e23bd0f187c59b4e1e43b"}, - {file = "regex-2022.9.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2effeaf50a6838f3dd4d3c5d265f06eabc748f476e8441892645ae3a697e273"}, - {file = "regex-2022.9.13-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59a786a55d00439d8fae4caaf71581f2aaef7297d04ee60345c3594efef5648a"}, - {file = "regex-2022.9.13-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b701dbc124558fd2b1b08005eeca6c9160e209108fbcbd00091fcfac641ac7"}, - {file = "regex-2022.9.13-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dab81cc4d58026861445230cfba27f9825e9223557926e7ec22156a1a140d55c"}, - {file = "regex-2022.9.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b0c5cc3d1744a67c3b433dce91e5ef7c527d612354c1f1e8576d9e86bc5c5e2"}, - {file = "regex-2022.9.13-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:518272f25da93e02af4f1e94985f5042cec21557ef3591027d0716f2adda5d0a"}, - {file = "regex-2022.9.13-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8418ee2cb857b83881b8f981e4c636bc50a0587b12d98cb9b947408a3c484fe7"}, - {file = "regex-2022.9.13-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cfa4c956ff0a977c4823cb3b930b0a4e82543b060733628fec7ab3eb9b1abe37"}, - {file = "regex-2022.9.13-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:a1c4d17879dd4c4432c08a1ca1ab379f12ab54af569e945b6fc1c4cf6a74ca45"}, - {file = "regex-2022.9.13-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:77c2879d3ba51e5ca6c2b47f2dcf3d04a976a623a8fc8236010a16c9e0b0a3c7"}, - {file = "regex-2022.9.13-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d2885ec6eea629c648ecc9bde0837ec6b92208b7f36381689937fe5d64a517e8"}, - {file = "regex-2022.9.13-cp311-cp311-win32.whl", hash = "sha256:2dda4b096a6f630d6531728a45bd12c67ec3badf44342046dc77d4897277d4f2"}, - {file = "regex-2022.9.13-cp311-cp311-win_amd64.whl", hash = "sha256:592b9e2e1862168e71d9e612bfdc22c451261967dbd46681f14e76dfba7105fd"}, - {file = "regex-2022.9.13-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:df8fe00b60e4717662c7f80c810ba66dcc77309183c76b7754c0dff6f1d42054"}, - {file = "regex-2022.9.13-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:995e70bb8c91d1b99ed2aaf8ec44863e06ad1dfbb45d7df95f76ef583ec323a9"}, - {file = "regex-2022.9.13-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad75173349ad79f9d21e0d0896b27dcb37bfd233b09047bc0b4d226699cf5c87"}, - {file = "regex-2022.9.13-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7681c49da1a2d4b905b4f53d86c9ba4506e79fba50c4a664d9516056e0f7dfcc"}, - {file = "regex-2022.9.13-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9bc8edc5f8ef0ebb46f3fa0d02bd825bbe9cc63d59e428ffb6981ff9672f6de1"}, - {file = "regex-2022.9.13-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bee775ff05c9d519195bd9e8aaaccfe3971db60f89f89751ee0f234e8aeac5"}, - {file = "regex-2022.9.13-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1a901ce5cd42658ab8f8eade51b71a6d26ad4b68c7cfc86b87efc577dfa95602"}, - {file = "regex-2022.9.13-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:14a7ab070fa3aec288076eed6ed828587b805ef83d37c9bfccc1a4a7cfbd8111"}, - {file = "regex-2022.9.13-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:d23ac6b4bf9e32fcde5fcdb2e1fd5e7370d6693fcac51ee1d340f0e886f50d1f"}, - {file = "regex-2022.9.13-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:4cdbfa6d2befeaee0c899f19222e9b20fc5abbafe5e9c43a46ef819aeb7b75e5"}, - {file = "regex-2022.9.13-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:ab07934725e6f25c6f87465976cc69aef1141e86987af49d8c839c3ffd367c72"}, - {file = "regex-2022.9.13-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:d2a1371dc73e921f3c2e087c05359050f3525a9a34b476ebc8130e71bec55e97"}, - {file = "regex-2022.9.13-cp36-cp36m-win32.whl", hash = "sha256:fcbd1edff1473d90dc5cf4b52d355cf1f47b74eb7c85ba6e45f45d0116b8edbd"}, - {file = "regex-2022.9.13-cp36-cp36m-win_amd64.whl", hash = "sha256:fe428822b7a8c486bcd90b334e9ab541ce6cc0d6106993d59f201853e5e14121"}, - {file = "regex-2022.9.13-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d7430f041755801b712ec804aaf3b094b9b5facbaa93a6339812a8e00d7bd53a"}, - {file = "regex-2022.9.13-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:079c182f99c89524069b9cd96f5410d6af437e9dca576a7d59599a574972707e"}, - {file = "regex-2022.9.13-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59bac44b5a07b08a261537f652c26993af9b1bbe2a29624473968dd42fc29d56"}, - {file = "regex-2022.9.13-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a59d0377e58d96a6f11636e97992f5b51b7e1e89eb66332d1c01b35adbabfe8a"}, - {file = "regex-2022.9.13-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9d68eb704b24bc4d441b24e4a12653acd07d2c39940548761e0985a08bc1fff"}, - {file = "regex-2022.9.13-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0385d66e73cdd4462f3cc42c76a6576ddcc12472c30e02a2ae82061bff132c32"}, - {file = "regex-2022.9.13-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:db45016364eec9ddbb5af93c8740c5c92eb7f5fc8848d1ae04205a40a1a2efc6"}, - {file = "regex-2022.9.13-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:03ff695518482b946a6d3d4ce9cbbd99a21320e20d94913080aa3841f880abcd"}, - {file = "regex-2022.9.13-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6b32b45433df1fad7fed738fe15200b6516da888e0bd1fdd6aa5e50cc16b76bc"}, - {file = "regex-2022.9.13-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:003a2e1449d425afc817b5f0b3d4c4aa9072dd5f3dfbf6c7631b8dc7b13233de"}, - {file = "regex-2022.9.13-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:a9eb9558e1d0f78e07082d8a70d5c4d631c8dd75575fae92105df9e19c736730"}, - {file = "regex-2022.9.13-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:f6e0321921d2fdc082ef90c1fd0870f129c2e691bfdc4937dcb5cd308aba95c4"}, - {file = "regex-2022.9.13-cp37-cp37m-win32.whl", hash = "sha256:3f3b4594d564ed0b2f54463a9f328cf6a5b2a32610a90cdff778d6e3e561d08b"}, - {file = "regex-2022.9.13-cp37-cp37m-win_amd64.whl", hash = "sha256:8aba0d01e3dfd335f2cb107079b07fdddb4cd7fb2d8c8a1986f9cb8ce9246c24"}, - {file = "regex-2022.9.13-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:944567bb08f52268d8600ee5bdf1798b2b62ea002cc692a39cec113244cbdd0d"}, - {file = "regex-2022.9.13-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0b664a4d33ffc6be10996606dfc25fd3248c24cc589c0b139feb4c158053565e"}, - {file = "regex-2022.9.13-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f06cc1190f3db3192ab8949e28f2c627e1809487e2cfc435b6524c1ce6a2f391"}, - {file = "regex-2022.9.13-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c57d50d4d5eb0c862569ca3c840eba2a73412f31d9ecc46ef0d6b2e621a592b"}, - {file = "regex-2022.9.13-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19a4da6f513045f5ba00e491215bd00122e5bd131847586522463e5a6b2bd65f"}, - {file = "regex-2022.9.13-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a926339356fe29595f8e37af71db37cd87ff764e15da8ad5129bbaff35bcc5a6"}, - {file = "regex-2022.9.13-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:091efcfdd4178a7e19a23776dc2b1fafb4f57f4d94daf340f98335817056f874"}, - {file = "regex-2022.9.13-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:880dbeb6bdde7d926b4d8e41410b16ffcd4cb3b4c6d926280fea46e2615c7a01"}, - {file = "regex-2022.9.13-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:73b985c9fc09a7896846e26d7b6f4d1fd5a20437055f4ef985d44729f9f928d0"}, - {file = "regex-2022.9.13-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c0b7cb9598795b01f9a3dd3f770ab540889259def28a3bf9b2fa24d52edecba3"}, - {file = "regex-2022.9.13-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:37e5a26e76c46f54b3baf56a6fdd56df9db89758694516413757b7d127d4c57b"}, - {file = "regex-2022.9.13-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:99945ddb4f379bb9831c05e9f80f02f079ba361a0fb1fba1fc3b267639b6bb2e"}, - {file = "regex-2022.9.13-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dcbcc9e72a791f622a32d17ff5011326a18996647509cac0609a7fc43adc229"}, - {file = "regex-2022.9.13-cp38-cp38-win32.whl", hash = "sha256:d3102ab9bf16bf541ca228012d45d88d2a567c9682a805ae2c145a79d3141fdd"}, - {file = "regex-2022.9.13-cp38-cp38-win_amd64.whl", hash = "sha256:14216ea15efc13f28d0ef1c463d86d93ca7158a79cd4aec0f9273f6d4c6bb047"}, - {file = "regex-2022.9.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9a165a05979e212b2c2d56a9f40b69c811c98a788964e669eb322de0a3e420b4"}, - {file = "regex-2022.9.13-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:14c71437ffb89479c89cc7022a5ea2075a842b728f37205e47c824cc17b30a42"}, - {file = "regex-2022.9.13-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee7045623a5ace70f3765e452528b4c1f2ce669ed31959c63f54de64fe2f6ff7"}, - {file = "regex-2022.9.13-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6e521d9db006c5e4a0f8acfef738399f72b704913d4e083516774eb51645ad7c"}, - {file = "regex-2022.9.13-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b86548b8234b2be3985dbc0b385e35f5038f0f3e6251464b827b83ebf4ed90e5"}, - {file = "regex-2022.9.13-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2b39ee3b280e15824298b97cec3f7cbbe6539d8282cc8a6047a455b9a72c598"}, - {file = "regex-2022.9.13-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e6e6e61e9a38b6cc60ca3e19caabc90261f070f23352e66307b3d21a24a34aaf"}, - {file = "regex-2022.9.13-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d837ccf3bd2474feabee96cd71144e991472e400ed26582edc8ca88ce259899c"}, - {file = "regex-2022.9.13-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6adfe300848d61a470ec7547adc97b0ccf86de86a99e6830f1d8c8d19ecaf6b3"}, - {file = "regex-2022.9.13-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d5b003d248e6f292475cd24b04e5f72c48412231961a675edcb653c70730e79e"}, - {file = "regex-2022.9.13-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:d5edd3eb877c9fc2e385173d4a4e1d792bf692d79e25c1ca391802d36ecfaa01"}, - {file = "regex-2022.9.13-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:50e764ffbd08b06aa8c4e86b8b568b6722c75d301b33b259099f237c46b2134e"}, - {file = "regex-2022.9.13-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6d43bd402b27e0e7eae85c612725ba1ce7798f20f6fab4e8bc3de4f263294f03"}, - {file = "regex-2022.9.13-cp39-cp39-win32.whl", hash = "sha256:7fcf7f94ccad19186820ac67e2ec7e09e0ac2dac39689f11cf71eac580503296"}, - {file = "regex-2022.9.13-cp39-cp39-win_amd64.whl", hash = "sha256:322bd5572bed36a5b39952d88e072738926759422498a96df138d93384934ff8"}, - {file = "regex-2022.9.13.tar.gz", hash = "sha256:f07373b6e56a6f3a0df3d75b651a278ca7bd357a796078a26a958ea1ce0588fd"}, -] -requests = [ - {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"}, - {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, -] -setuptools = [ - {file = "setuptools-59.6.0-py3-none-any.whl", hash = "sha256:4ce92f1e1f8f01233ee9952c04f6b81d1e02939d6e1b488428154974a4d0783e"}, - {file = "setuptools-59.6.0.tar.gz", hash = "sha256:22c7348c6d2976a52632c67f7ab0cdf40147db7789f9aed18734643fe9cf3373"}, -] -six = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] -snowballstemmer = [ - {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, - {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, -] -Sphinx = [ - {file = "Sphinx-4.3.2-py3-none-any.whl", hash = "sha256:6a11ea5dd0bdb197f9c2abc2e0ce73e01340464feaece525e64036546d24c851"}, - {file = "Sphinx-4.3.2.tar.gz", hash = "sha256:0a8836751a68306b3fe97ecbe44db786f8479c3bf4b80e3a7f5c838657b4698c"}, -] -sphinx-rtd-theme = [ - {file = "sphinx_rtd_theme-1.0.0-py2.py3-none-any.whl", hash = "sha256:4d35a56f4508cfee4c4fb604373ede6feae2a306731d533f409ef5c3496fdbd8"}, - {file = "sphinx_rtd_theme-1.0.0.tar.gz", hash = "sha256:eec6d497e4c2195fa0e8b2016b337532b8a699a68bcb22a512870e16925c6a5c"}, -] -sphinxcontrib-applehelp = [ - {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"}, - {file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"}, -] -sphinxcontrib-devhelp = [ - {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, - {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, -] -sphinxcontrib-htmlhelp = [ - {file = "sphinxcontrib-htmlhelp-2.0.0.tar.gz", hash = "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"}, - {file = "sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl", hash = "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07"}, -] -sphinxcontrib-jsmath = [ - {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, - {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, -] -sphinxcontrib-qthelp = [ - {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, - {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, -] -sphinxcontrib-serializinghtml = [ - {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, - {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, -] -toml = [ - {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, - {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, -] -tomli = [ - {file = "tomli-1.2.3-py3-none-any.whl", hash = "sha256:e3069e4be3ead9668e21cb9b074cd948f7b3113fd9c8bba083f48247aab8b11c"}, - {file = "tomli-1.2.3.tar.gz", hash = "sha256:05b6166bff487dc068d322585c7ea4ef78deed501cc124060e0f238e89a9231f"}, - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] -tox = [ - {file = "tox-3.25.0-py2.py3-none-any.whl", hash = "sha256:0805727eb4d6b049de304977dfc9ce315a1938e6619c3ab9f38682bb04662a5a"}, - {file = "tox-3.25.0.tar.gz", hash = "sha256:37888f3092aa4e9f835fc8cc6dadbaaa0782651c41ef359e3a5743fcb0308160"}, - {file = "tox-3.26.0-py2.py3-none-any.whl", hash = "sha256:bf037662d7c740d15c9924ba23bb3e587df20598697bb985ac2b49bdc2d847f6"}, - {file = "tox-3.26.0.tar.gz", hash = "sha256:44f3c347c68c2c68799d7d44f1808f9d396fc8a1a500cbc624253375c7ae107e"}, -] -typed-ast = [ - {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, - {file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"}, - {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac"}, - {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe"}, - {file = "typed_ast-1.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72"}, - {file = "typed_ast-1.5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79b1e0869db7c830ba6a981d58711c88b6677506e648496b1f64ac7d15633aec"}, - {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a94d55d142c9265f4ea46fab70977a1944ecae359ae867397757d836ea5a3f47"}, - {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:183afdf0ec5b1b211724dfef3d2cad2d767cbefac291f24d69b00546c1837fb6"}, - {file = "typed_ast-1.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:639c5f0b21776605dd6c9dbe592d5228f021404dafd377e2b7ac046b0349b1a1"}, - {file = "typed_ast-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6"}, - {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"}, - {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c"}, - {file = "typed_ast-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2"}, - {file = "typed_ast-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d"}, - {file = "typed_ast-1.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f"}, - {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc"}, - {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6"}, - {file = "typed_ast-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e"}, - {file = "typed_ast-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35"}, - {file = "typed_ast-1.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97"}, - {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3"}, - {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72"}, - {file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"}, - {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, -] -typing-extensions = [ +files = [ {file = "typing_extensions-4.1.1-py3-none-any.whl", hash = "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"}, {file = "typing_extensions-4.1.1.tar.gz", hash = "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42"}, - {file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"}, - {file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"}, -] -urllib3 = [ - {file = "urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"}, - {file = "urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"}, -] -virtualenv = [ - {file = "virtualenv-20.16.2-py2.py3-none-any.whl", hash = "sha256:635b272a8e2f77cb051946f46c60a54ace3cb5e25568228bd6b57fc70eca9ff3"}, - {file = "virtualenv-20.16.2.tar.gz", hash = "sha256:0ef5be6d07181946891f5abc8047fda8bc2f0b4b9bf222c64e6e8963baee76db"}, -] -zipp = [ - {file = "zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"}, - {file = "zipp-3.6.0.tar.gz", hash = "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832"}, ] + +[metadata] +lock-version = "2.0" +python-versions = "^3.6" +content-hash = "101c4a8c58c012e2a35491927fc7f20dd2ec86a7d7297998be0bf6c4b12204af" diff --git a/pyproject.toml b/pyproject.toml index 439a4256..51166716 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "graphql-core" -version = "3.2.2" +version = "3.2.6" description = """ GraphQL-core is a Python port of GraphQL.js,\ the JavaScript reference implementation for GraphQL.""" @@ -22,7 +22,10 @@ classifiers = [ "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10" + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13" ] packages = [ { include = "graphql", from = "src" }, @@ -37,6 +40,7 @@ packages = [ { include = 'poetry.lock', format = "sdist" }, { include = 'tox.ini', format = "sdist" }, { include = 'setup.cfg', format = "sdist" }, + { include = 'setup.py', format = "sdist" }, { include = 'CODEOWNERS', format = "sdist" }, { include = 'MANIFEST.in', format = "sdist" }, { include = 'SECURITY.md', format = "sdist" } @@ -45,45 +49,33 @@ packages = [ [tool.poetry.dependencies] python = "^3.6" typing-extensions = [ - { version = "^4.3", python = ">=3.7,<3.8" }, - { version = "^4.1", python = "<3.7" } -] - -[tool.poetry.dev-dependencies] -pytest = "^6.2" -pytest-asyncio = [ - {version=">=0.19,<1", python = ">=3.7" }, - {version=">=0.16,<0.17", python = "<3.7" }, -] -pytest-benchmark = "^3.4" -pytest-cov = "^3.0" -pytest-describe = "^2.0" -pytest-timeout = "^2.1" -black = [ - {version = "22.8.0", python = ">=3.6.2"}, - {version = "20.8b1", python = "<3.6.2"} -] -flake8 = [ - {version = "^5.0", python = ">=3.6.1"}, - {version = "^4.0", python = "<3.6.1"} -] -mypy = "0.971" -sphinx = "^4.3" -sphinx_rtd_theme = ">=1,<2" -check-manifest = ">=0.48,<1" -bump2version = ">=1.0,<2" -tomli = [ - {version="^2", python = ">=3.7"}, - {version="^1.2", python = "<3.7"} -] -tox = [ - {version = "^3.26", python = ">=3.7"}, - {version = "3.25", python = "<3.7"} + { version = ">=4.1,<5", python = "<3.10" } ] [tool.black] -target-version = ['py36', 'py37', 'py38', 'py39', 'py310'] +target-version = ['py36', 'py37', 'py38', 'py39', 'py310', 'py311', 'py312', 'py313'] + +[tool.pyright] +reportIncompatibleVariableOverride = false +reportMissingTypeArgument = false +reportUnknownArgumentType = false +reportUnknownMemberType = false +reportUnknownParameterType = false +reportUnnecessaryIsInstance = false +reportUnknownVariableType = false +ignore = ["**/test_*"] # test functions + +[tool.pylint.basic] +max-module-lines = 2000 + +[tool.pylint.messages_control] +disable = [ + "method-hidden", + "missing-module-docstring", # test modules + "redefined-outer-name", + "unused-variable", # test functions +] [build-system] -requires = ["poetry_core>=1,<2", "setuptools>=59,<70"] +requires = ["poetry_core>=1,<3", "setuptools>=59,<76"] build-backend = "poetry.core.masonry.api" diff --git a/setup.py b/setup.py index d307a66a..1cf7e83b 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,6 @@ from re import search -from setuptools import setup, find_packages + +from setuptools import find_packages, setup with open("src/graphql/version.py") as version_file: version = search('version = "(.*)"', version_file.read()).group(1) @@ -31,9 +32,12 @@ "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", ], install_requires=[ - "typing-extensions>=4.2,<5; python_version < '3.8'", + "typing-extensions>=4,<5; python_version < '3.10'", ], python_requires=">=3.6,<4", packages=find_packages("src"), diff --git a/src/graphql/execution/execute.py b/src/graphql/execution/execute.py index 01ec288a..869f59f3 100644 --- a/src/graphql/execution/execute.py +++ b/src/graphql/execution/execute.py @@ -10,9 +10,9 @@ Iterable, List, Optional, - Union, Tuple, Type, + Union, cast, ) @@ -30,25 +30,27 @@ OperationType, ) from ..pyutils import ( - inspect, - is_awaitable as default_is_awaitable, - is_iterable, AwaitableOrValue, Path, Undefined, + inspect, + is_iterable, +) +from ..pyutils import ( + is_awaitable as default_is_awaitable, ) from ..type import ( GraphQLAbstractType, GraphQLField, + GraphQLFieldResolver, GraphQLLeafType, GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLOutputType, + GraphQLResolveInfo, GraphQLSchema, - GraphQLFieldResolver, GraphQLTypeResolver, - GraphQLResolveInfo, SchemaMetaFieldDef, TypeMetaFieldDef, TypeNameMetaFieldDef, @@ -213,13 +215,13 @@ def __init__( self.context_value = context_value self.operation = operation self.variable_values = variable_values - self.field_resolver = field_resolver # type: ignore - self.type_resolver = type_resolver # type: ignore - self.subscribe_field_resolver = subscribe_field_resolver # type: ignore + self.field_resolver = field_resolver + self.type_resolver = type_resolver + self.subscribe_field_resolver = subscribe_field_resolver self.errors = errors self.middleware_manager = middleware_manager if is_awaitable: - self.is_awaitable = is_awaitable + self.is_awaitable = is_awaitable # type: ignore self._subfields_cache: Dict[Tuple, Dict[str, List[FieldNode]]] = {} @classmethod @@ -793,7 +795,7 @@ def complete_abstract_value( that value, then complete the value for that type. """ resolve_type_fn = return_type.resolve_type or self.type_resolver - runtime_type = resolve_type_fn(result, info, return_type) # type: ignore + runtime_type = resolve_type_fn(result, info, return_type) if self.is_awaitable(runtime_type): runtime_type = cast(Awaitable, runtime_type) @@ -1096,10 +1098,10 @@ def execute_sync( # Assert that the execution was synchronous. if isawaitable(result): - ensure_future(cast(Awaitable[ExecutionResult], result)).cancel() + ensure_future(result).cancel() raise RuntimeError("GraphQL execution failed to complete synchronously.") - return cast(ExecutionResult, result) + return result def assert_valid_execution_arguments( diff --git a/src/graphql/execution/map_async_iterator.py b/src/graphql/execution/map_async_iterator.py index 43400fd3..3010a5f4 100644 --- a/src/graphql/execution/map_async_iterator.py +++ b/src/graphql/execution/map_async_iterator.py @@ -1,8 +1,8 @@ from asyncio import CancelledError, Event, Task, ensure_future, wait from concurrent.futures import FIRST_COMPLETED from inspect import isasyncgen, isawaitable -from typing import cast, Any, AsyncIterable, Callable, Optional, Set, Type, Union from types import TracebackType +from typing import Any, AsyncIterable, Callable, Optional, Set, Type, Union __all__ = ["MapAsyncIterator"] @@ -73,19 +73,19 @@ async def athrow( """Throw an exception into the asynchronous iterator.""" if self.is_closed: return + if isinstance(type_, BaseException): + value = type_ + type_ = type(value) + traceback = value.__traceback__ athrow = getattr(self.iterator, "athrow", None) if athrow: - await athrow(type_, value, traceback) + await athrow(type_ if value is None else value) else: await self.aclose() if value is None: if traceback is None: - raise type_ - value = ( - type_ - if isinstance(value, BaseException) - else cast(Type[BaseException], type_)() - ) + raise type_ # pragma: no cover + value = type_ if isinstance(value, BaseException) else type_() if traceback is not None: value = value.with_traceback(traceback) raise value diff --git a/src/graphql/execution/values.py b/src/graphql/execution/values.py index e11733fc..9a877f0f 100644 --- a/src/graphql/execution/values.py +++ b/src/graphql/execution/values.py @@ -5,8 +5,8 @@ DirectiveNode, EnumValueDefinitionNode, ExecutableDefinitionNode, - FieldNode, FieldDefinitionNode, + FieldNode, InputValueDefinitionNode, NullValueNode, SchemaDefinitionNode, @@ -17,12 +17,13 @@ VariableNode, print_ast, ) -from ..pyutils import inspect, print_path_list, Undefined +from ..pyutils import Undefined, inspect, print_path_list from ..type import ( GraphQLDirective, GraphQLField, GraphQLInputType, GraphQLSchema, + is_input_object_type, is_input_type, is_non_null_type, ) @@ -131,7 +132,7 @@ def on_input_value_error( GraphQLError( prefix + "; " + error.message, var_def_node, - original_error=error.original_error, + original_error=error, ) ) @@ -160,8 +161,13 @@ def get_argument_values( argument_node = arg_node_map.get(name) if argument_node is None: - if arg_def.default_value is not Undefined: - coerced_values[arg_def.out_name or name] = arg_def.default_value + value = arg_def.default_value + if value is not Undefined: + if is_input_object_type(arg_def.type): + # coerce input value so that out_names are used + value = coerce_input_value(value, arg_def.type) + + coerced_values[arg_def.out_name or name] = value elif is_non_null_type(arg_type): # pragma: no cover else raise GraphQLError( f"Argument '{name}' of required type '{arg_type}'" @@ -176,8 +182,12 @@ def get_argument_values( if isinstance(value_node, VariableNode): variable_name = value_node.name.value if variable_values is None or variable_name not in variable_values: - if arg_def.default_value is not Undefined: - coerced_values[arg_def.out_name or name] = arg_def.default_value + value = arg_def.default_value + if value is not Undefined: + if is_input_object_type(arg_def.type): + # coerce input value so that out_names are used + value = coerce_input_value(value, arg_def.type) + coerced_values[arg_def.out_name or name] = value elif is_non_null_type(arg_type): # pragma: no cover else raise GraphQLError( f"Argument '{name}' of required type '{arg_type}'" diff --git a/src/graphql/graphql.py b/src/graphql/graphql.py index c2e804cd..cbf22865 100644 --- a/src/graphql/graphql.py +++ b/src/graphql/graphql.py @@ -1,10 +1,10 @@ from asyncio import ensure_future from inspect import isawaitable -from typing import Any, Awaitable, Callable, Dict, Optional, Union, Type, cast +from typing import Any, Callable, Dict, Optional, Type, Union from .error import GraphQLError -from .execution import execute, ExecutionResult, ExecutionContext, Middleware -from .language import parse, Source +from .execution import ExecutionContext, ExecutionResult, Middleware, execute +from .language import Source, parse from .pyutils import AwaitableOrValue from .type import ( GraphQLFieldResolver, @@ -90,9 +90,9 @@ async def graphql( ) if isawaitable(result): - return await cast(Awaitable[ExecutionResult], result) + return await result - return cast(ExecutionResult, result) + return result def assume_not_awaitable(_value: Any) -> bool: @@ -143,10 +143,10 @@ def graphql_sync( # Assert that the execution was synchronous. if isawaitable(result): - ensure_future(cast(Awaitable[ExecutionResult], result)).cancel() + ensure_future(result).cancel() raise RuntimeError("GraphQL execution failed to complete synchronously.") - return cast(ExecutionResult, result) + return result def graphql_impl( diff --git a/src/graphql/language/parser.py b/src/graphql/language/parser.py index 389913a5..43d96566 100644 --- a/src/graphql/language/parser.py +++ b/src/graphql/language/parser.py @@ -1,6 +1,7 @@ -from typing import Callable, Dict, List, Optional, Union, TypeVar, cast from functools import partial +from typing import Callable, Dict, List, Optional, TypeVar, Union, cast +from ..error import GraphQLError, GraphQLSyntaxError from .ast import ( ArgumentNode, BooleanValueNode, @@ -24,14 +25,14 @@ InputObjectTypeDefinitionNode, InputObjectTypeExtensionNode, InputValueDefinitionNode, - IntValueNode, InterfaceTypeDefinitionNode, InterfaceTypeExtensionNode, + IntValueNode, ListTypeNode, ListValueNode, Location, - NameNode, NamedTypeNode, + NameNode, NonNullTypeNode, NullValueNode, ObjectFieldNode, @@ -48,6 +49,7 @@ SelectionNode, SelectionSetNode, StringValueNode, + Token, TypeNode, TypeSystemExtensionNode, UnionTypeDefinitionNode, @@ -57,11 +59,9 @@ VariableNode, ) from .directive_locations import DirectiveLocation -from .ast import Token from .lexer import Lexer, is_punctuator_token_kind from .source import Source, is_source from .token_kind import TokenKind -from ..error import GraphQLError, GraphQLSyntaxError __all__ = ["parse", "parse_type", "parse_value", "parse_const_value"] @@ -73,6 +73,7 @@ def parse( source: SourceType, no_location: bool = False, + max_tokens: Optional[int] = None, allow_legacy_fragment_variables: bool = False, ) -> DocumentNode: """Given a GraphQL source, parse it into a Document. @@ -80,8 +81,14 @@ def parse( Throws GraphQLError if a syntax error is encountered. By default, the parser creates AST nodes that know the location in the source that - they correspond to. The ``no_location`` option disables that behavior for - performance or testing. + they correspond to. Setting the ``no_location`` parameter to False disables that + behavior for performance or testing. + + Parser CPU and memory usage is linear to the number of tokens in a document, + however in extreme cases it becomes quadratic due to memory exhaustion. + Parsing happens before validation, so even invalid queries can burn lots of + CPU time and memory. To prevent this, you can set a maximum number of tokens + allowed within a document using the ``max_tokens`` parameter. Legacy feature (will be removed in v3.3): @@ -100,6 +107,7 @@ def parse( parser = Parser( source, no_location=no_location, + max_tokens=max_tokens, allow_legacy_fragment_variables=allow_legacy_fragment_variables, ) return parser.parse_document() @@ -108,6 +116,7 @@ def parse( def parse_value( source: SourceType, no_location: bool = False, + max_tokens: Optional[int] = None, allow_legacy_fragment_variables: bool = False, ) -> ValueNode: """Parse the AST for a given string containing a GraphQL value. @@ -123,6 +132,7 @@ def parse_value( parser = Parser( source, no_location=no_location, + max_tokens=max_tokens, allow_legacy_fragment_variables=allow_legacy_fragment_variables, ) parser.expect_token(TokenKind.SOF) @@ -134,6 +144,7 @@ def parse_value( def parse_const_value( source: SourceType, no_location: bool = False, + max_tokens: Optional[int] = None, allow_legacy_fragment_variables: bool = False, ) -> ConstValueNode: """Parse the AST for a given string containing a GraphQL constant value. @@ -144,6 +155,7 @@ def parse_const_value( parser = Parser( source, no_location=no_location, + max_tokens=max_tokens, allow_legacy_fragment_variables=allow_legacy_fragment_variables, ) parser.expect_token(TokenKind.SOF) @@ -155,6 +167,7 @@ def parse_const_value( def parse_type( source: SourceType, no_location: bool = False, + max_tokens: Optional[int] = None, allow_legacy_fragment_variables: bool = False, ) -> TypeNode: """Parse the AST for a given string containing a GraphQL Type. @@ -170,6 +183,7 @@ def parse_type( parser = Parser( source, no_location=no_location, + max_tokens=max_tokens, allow_legacy_fragment_variables=allow_legacy_fragment_variables, ) parser.expect_token(TokenKind.SOF) @@ -191,13 +205,16 @@ class Parser: """ _lexer: Lexer - _no_Location: bool + _no_location: bool + _max_tokens: Optional[int] _allow_legacy_fragment_variables: bool + _token_counter: int def __init__( self, source: SourceType, no_location: bool = False, + max_tokens: Optional[int] = None, allow_legacy_fragment_variables: bool = False, ): source = ( @@ -206,7 +223,9 @@ def __init__( self._lexer = Lexer(source) self._no_location = no_location + self._max_tokens = max_tokens self._allow_legacy_fragment_variables = allow_legacy_fragment_variables + self._token_counter = 0 def parse_name(self) -> NameNode: """Convert a name lex token into a name parse node.""" @@ -327,9 +346,11 @@ def parse_variable_definition(self) -> VariableDefinitionNode: return VariableDefinitionNode( variable=self.parse_variable(), type=self.expect_token(TokenKind.COLON) and self.parse_type_reference(), - default_value=self.parse_const_value_literal() - if self.expect_optional_token(TokenKind.EQUALS) - else None, + default_value=( + self.parse_const_value_literal() + if self.expect_optional_token(TokenKind.EQUALS) + else None + ), directives=self.parse_const_directives(), loc=self.loc(start), ) @@ -371,17 +392,18 @@ def parse_field(self) -> FieldNode: name=name, arguments=self.parse_arguments(False), directives=self.parse_directives(False), - selection_set=self.parse_selection_set() - if self.peek(TokenKind.BRACE_L) - else None, + selection_set=( + self.parse_selection_set() if self.peek(TokenKind.BRACE_L) else None + ), loc=self.loc(start), ) def parse_arguments(self, is_const: bool) -> List[ArgumentNode]: """Arguments[Const]: (Argument[?Const]+)""" item = self.parse_const_argument if is_const else self.parse_argument - item = cast(Callable[[], ArgumentNode], item) - return self.optional_many(TokenKind.PAREN_L, item, TokenKind.PAREN_R) + return self.optional_many( + TokenKind.PAREN_L, cast(Callable[[], ArgumentNode], item), TokenKind.PAREN_R + ) def parse_argument(self, is_const: bool = False) -> ArgumentNode: """Argument[Const]: Name : Value[?Const]""" @@ -477,7 +499,7 @@ def parse_value_literal(self, is_const: bool) -> ValueNode: def parse_string_literal(self, _is_const: bool = False) -> StringValueNode: token = self._lexer.token - self._lexer.advance() + self.advance_lexer() return StringValueNode( value=token.value, block=token.kind == TokenKind.BLOCK_STRING, @@ -514,18 +536,18 @@ def parse_object(self, is_const: bool) -> ObjectValueNode: def parse_int(self, _is_const: bool = False) -> IntValueNode: token = self._lexer.token - self._lexer.advance() + self.advance_lexer() return IntValueNode(value=token.value, loc=self.loc(token)) def parse_float(self, _is_const: bool = False) -> FloatValueNode: token = self._lexer.token - self._lexer.advance() + self.advance_lexer() return FloatValueNode(value=token.value, loc=self.loc(token)) def parse_named_values(self, _is_const: bool = False) -> ValueNode: token = self._lexer.token value = token.value - self._lexer.advance() + self.advance_lexer() if value == "true": return BooleanValueNode(value=True, loc=self.loc(token)) if value == "false": @@ -1020,7 +1042,7 @@ def expect_token(self, kind: TokenKind) -> Token: """ token = self._lexer.token if token.kind == kind: - self._lexer.advance() + self.advance_lexer() return token raise GraphQLSyntaxError( @@ -1037,7 +1059,7 @@ def expect_optional_token(self, kind: TokenKind) -> bool: """ token = self._lexer.token if token.kind == kind: - self._lexer.advance() + self.advance_lexer() return True return False @@ -1050,7 +1072,7 @@ def expect_keyword(self, value: str) -> None: """ token = self._lexer.token if token.kind == TokenKind.NAME and token.value == value: - self._lexer.advance() + self.advance_lexer() else: raise GraphQLSyntaxError( self._lexer.source, @@ -1066,7 +1088,7 @@ def expect_optional_keyword(self, value: str) -> bool: """ token = self._lexer.token if token.kind == TokenKind.NAME and token.value == value: - self._lexer.advance() + self.advance_lexer() return True return False @@ -1154,6 +1176,20 @@ def delimited_many( break return nodes + def advance_lexer(self) -> None: + max_tokens = self._max_tokens + token = self._lexer.advance() + + if max_tokens is not None and token.kind != TokenKind.EOF: + self._token_counter += 1 + if self._token_counter > max_tokens: + raise GraphQLSyntaxError( + self._lexer.source, + token.start, + f"Document contains more than {max_tokens} tokens." + " Parsing aborted.", + ) + def get_token_desc(token: Token) -> str: """Describe a token as a string for debugging.""" diff --git a/src/graphql/language/predicates.py b/src/graphql/language/predicates.py index 24d7c7a5..75acad38 100644 --- a/src/graphql/language/predicates.py +++ b/src/graphql/language/predicates.py @@ -53,9 +53,11 @@ def is_const_value_node(node: Node) -> bool: return is_value_node(node) and ( any(is_const_value_node(value) for value in node.values) if isinstance(node, ListValueNode) - else any(is_const_value_node(field.value) for field in node.fields) - if isinstance(node, ObjectValueNode) - else not isinstance(node, VariableNode) + else ( + any(is_const_value_node(field.value) for field in node.fields) + if isinstance(node, ObjectValueNode) + else not isinstance(node, VariableNode) + ) ) diff --git a/src/graphql/language/print_location.py b/src/graphql/language/print_location.py index 6d13b1e1..bf48f30d 100644 --- a/src/graphql/language/print_location.py +++ b/src/graphql/language/print_location.py @@ -48,9 +48,11 @@ def print_source_location(source: Source, source_location: SourceLocation) -> st ("|", "^".rjust(sub_line_column_num)), ( "|", - sub_lines[sub_line_index + 1] - if sub_line_index < len(sub_lines) - 1 - else None, + ( + sub_lines[sub_line_index + 1] + if sub_line_index < len(sub_lines) - 1 + else None + ), ), ) diff --git a/src/graphql/language/visitor.py b/src/graphql/language/visitor.py index 996c7194..692d3e49 100644 --- a/src/graphql/language/visitor.py +++ b/src/graphql/language/visitor.py @@ -14,8 +14,7 @@ from ..pyutils import inspect, snake_to_camel from . import ast - -from .ast import Node, QUERY_DOCUMENT_KEYS +from .ast import QUERY_DOCUMENT_KEYS, Node __all__ = [ "Visitor", @@ -288,7 +287,7 @@ def visit( else: stack = Stack(in_array, idx, keys, edits, stack) in_array = isinstance(node, tuple) - keys = node if in_array else visitor_keys.get(node.kind, ()) + keys = node if in_array else visitor_keys.get(node.kind, ()) # type: ignore idx = -1 edits = [] if parent: diff --git a/src/graphql/pyutils/description.py b/src/graphql/pyutils/description.py index ccb858ea..763dd4ec 100644 --- a/src/graphql/pyutils/description.py +++ b/src/graphql/pyutils/description.py @@ -44,7 +44,7 @@ def unregister(cls, base: type) -> None: if not isinstance(base, type): raise TypeError("Only types can be unregistered.") if isinstance(cls.bases, tuple): - if base in cls.bases: + if base in cls.bases: # pragma: no branch cls.bases = tuple(b for b in cls.bases if b is not base) if not cls.bases: cls.bases = object diff --git a/src/graphql/pyutils/frozen_dict.py b/src/graphql/pyutils/frozen_dict.py index 93283596..2c8e4872 100644 --- a/src/graphql/pyutils/frozen_dict.py +++ b/src/graphql/pyutils/frozen_dict.py @@ -6,7 +6,7 @@ __all__ = ["FrozenDict"] KT = TypeVar("KT") -VT = TypeVar("VT", covariant=True) +VT = TypeVar("VT") class FrozenDict(Dict[KT, VT]): @@ -25,7 +25,7 @@ def __setitem__(self, key, value): def __iadd__(self, value): raise FrozenError - def __hash__(self): + def __hash__(self) -> int: # type: ignore return hash(tuple(self.items())) def __copy__(self) -> "FrozenDict": @@ -48,5 +48,5 @@ def popitem(self): def setdefault(self, key, default=None): raise FrozenError - def update(self, other=None): + def update(self, other=None): # type: ignore raise FrozenError diff --git a/src/graphql/pyutils/frozen_list.py b/src/graphql/pyutils/frozen_list.py index 01ead7c4..1e6c6a13 100644 --- a/src/graphql/pyutils/frozen_list.py +++ b/src/graphql/pyutils/frozen_list.py @@ -6,7 +6,7 @@ __all__ = ["FrozenList"] -T = TypeVar("T", covariant=True) +T = TypeVar("T") class FrozenList(List[T]): @@ -36,7 +36,7 @@ def __mul__(self, value): def __imul__(self, value): raise FrozenError - def __hash__(self): + def __hash__(self) -> int: # type: ignore return hash(tuple(self)) def __copy__(self) -> "FrozenList": diff --git a/src/graphql/pyutils/inspect.py b/src/graphql/pyutils/inspect.py index 8fc99dce..d3470fec 100644 --- a/src/graphql/pyutils/inspect.py +++ b/src/graphql/pyutils/inspect.py @@ -62,11 +62,13 @@ def inspect_recursive(value: Any, seen_values: List) -> str: items = trunc_list(items) if isinstance(value, dict): s = ", ".join( - "..." - if v is ELLIPSIS - else inspect_recursive(v[0], seen_values) - + ": " - + inspect_recursive(v[1], seen_values) + ( + "..." + if v is ELLIPSIS + else inspect_recursive(v[0], seen_values) + + ": " + + inspect_recursive(v[1], seen_values) + ) for v in items ) else: diff --git a/src/graphql/pyutils/is_iterable.py b/src/graphql/pyutils/is_iterable.py index cffbc51c..142c3a14 100644 --- a/src/graphql/pyutils/is_iterable.py +++ b/src/graphql/pyutils/is_iterable.py @@ -1,12 +1,5 @@ -from typing import ( - Any, - ByteString, - Collection, - Iterable, - Mapping, - Text, - ValuesView, -) +from collections.abc import Collection, Iterable, Mapping, ValuesView +from typing import Any __all__ = ["is_collection", "is_iterable"] @@ -14,7 +7,7 @@ if not isinstance({}.values(), Collection): # Python < 3.7.2 collection_types = (Collection, ValuesView) iterable_types: Any = Iterable -not_iterable_types: Any = (ByteString, Mapping, Text) +not_iterable_types: Any = (bytes, bytearray, memoryview, str, Mapping) def is_collection(value: Any) -> bool: diff --git a/src/graphql/type/definition.py b/src/graphql/type/definition.py index dbe03ada..f2bca559 100644 --- a/src/graphql/type/definition.py +++ b/src/graphql/type/definition.py @@ -1,5 +1,6 @@ from enum import Enum from typing import ( + TYPE_CHECKING, Any, Callable, Collection, @@ -10,7 +11,6 @@ NamedTuple, Optional, Tuple, - TYPE_CHECKING, Type, TypeVar, Union, @@ -21,8 +21,8 @@ from ..error import GraphQLError from ..language import ( EnumTypeDefinitionNode, - EnumValueDefinitionNode, EnumTypeExtensionNode, + EnumValueDefinitionNode, EnumValueNode, FieldDefinitionNode, FieldNode, @@ -47,16 +47,16 @@ from ..pyutils import ( AwaitableOrValue, Path, + Undefined, cached_property, did_you_mean, inspect, is_collection, is_description, suggestion_list, - Undefined, ) from ..utilities.value_from_ast_untyped import value_from_ast_untyped -from .assert_name import assert_name, assert_enum_value_name +from .assert_name import assert_enum_value_name, assert_name try: from typing import TypedDict @@ -438,16 +438,22 @@ def to_kwargs(self) -> GraphQLScalarTypeKwargs: # noinspection PyArgumentList return GraphQLScalarTypeKwargs( # type: ignore super().to_kwargs(), - serialize=None - if self.serialize is GraphQLScalarType.serialize - else self.serialize, - parse_value=None - if self.parse_value is GraphQLScalarType.parse_value - else self.parse_value, - parse_literal=None - if getattr(self.parse_literal, "__func__", None) - is GraphQLScalarType.parse_literal - else self.parse_literal, + serialize=( + None + if self.serialize is GraphQLScalarType.serialize + else self.serialize + ), + parse_value=( + None + if self.parse_value is GraphQLScalarType.parse_value + else self.parse_value + ), + parse_literal=( + None + if getattr(self.parse_literal, "__func__", None) + is GraphQLScalarType.parse_literal + else self.parse_literal + ), specified_by_url=self.specified_by_url, ) @@ -517,9 +523,11 @@ def __init__( ) else: args = { - assert_name(name): value - if isinstance(value, GraphQLArgument) - else GraphQLArgument(cast(GraphQLInputType, value)) + assert_name(name): ( + value + if isinstance(value, GraphQLArgument) + else GraphQLArgument(cast(GraphQLInputType, value)) + ) for name, value in args.items() } if resolve is not None and not callable(resolve): @@ -824,9 +832,9 @@ def fields(self) -> GraphQLFieldMap: f"{self.name} fields must be GraphQLField or output type objects." ) return { - assert_name(name): value - if isinstance(value, GraphQLField) - else GraphQLField(value) # type: ignore + assert_name(name): ( + value if isinstance(value, GraphQLField) else GraphQLField(value) + ) for name, value in fields.items() } @@ -958,9 +966,9 @@ def fields(self) -> GraphQLFieldMap: f"{self.name} fields must be GraphQLField or output type objects." ) return { - assert_name(name): value - if isinstance(value, GraphQLField) - else GraphQLField(value) # type: ignore + assert_name(name): ( + value if isinstance(value, GraphQLField) else GraphQLField(value) + ) for name, value in fields.items() } @@ -1167,12 +1175,12 @@ def __init__( ): try: # noinspection PyTypeChecker - values = dict(values) # type: ignore - except (TypeError, ValueError): + values = dict(values) + except (TypeError, ValueError) as error: raise TypeError( f"{name} values must be an Enum or a mapping" " with value names as keys." - ) + ) from error values = cast(Dict[str, Any], values) else: values = cast(Dict[str, Enum], values) @@ -1181,9 +1189,11 @@ def __init__( elif names_as_values is True: values = {key: key for key in values} values = { - assert_enum_value_name(key): value - if isinstance(value, GraphQLEnumValue) - else GraphQLEnumValue(value) + assert_enum_value_name(key): ( + value + if isinstance(value, GraphQLEnumValue) + else GraphQLEnumValue(value) + ) for key, value in values.items() } if ast_node and not isinstance(ast_node, EnumTypeDefinitionNode): @@ -1375,7 +1385,7 @@ class GraphQLInputObjectType(GraphQLNamedType): Example:: - NonNullFloat = GraphQLNonNull(GraphQLFloat()) + NonNullFloat = GraphQLNonNull(GraphQLFloat) class GeoPoint(GraphQLInputObjectType): name = 'GeoPoint' @@ -1383,7 +1393,7 @@ class GeoPoint(GraphQLInputObjectType): 'lat': GraphQLInputField(NonNullFloat), 'lon': GraphQLInputField(NonNullFloat), 'alt': GraphQLInputField( - GraphQLFloat(), default_value=0) + GraphQLFloat, default_value=0) } The outbound values will be Python dictionaries by default, but you can have them @@ -1441,9 +1451,11 @@ def to_kwargs(self) -> GraphQLInputObjectTypeKwargs: return GraphQLInputObjectTypeKwargs( # type: ignore super().to_kwargs(), fields=self.fields.copy(), - out_type=None - if self.out_type is GraphQLInputObjectType.out_type - else self.out_type, + out_type=( + None + if self.out_type is GraphQLInputObjectType.out_type + else self.out_type + ), ) def __copy__(self) -> "GraphQLInputObjectType": # pragma: no cover @@ -1473,9 +1485,11 @@ def fields(self) -> GraphQLInputFieldMap: " GraphQLInputField or input type objects." ) return { - assert_name(name): value - if isinstance(value, GraphQLInputField) - else GraphQLInputField(value) # type: ignore + assert_name(name): ( + value + if isinstance(value, GraphQLInputField) + else GraphQLInputField(value) + ) for name, value in fields.items() } @@ -1633,7 +1647,7 @@ class GraphQLNonNull(GraphQLWrappingType[GNT], Generic[GNT]): class RowType(GraphQLObjectType): name = 'Row' fields = { - 'id': GraphQLField(GraphQLNonNull(GraphQLString())) + 'id': GraphQLField(GraphQLNonNull(GraphQLString)) } Note: the enforcement of non-nullability occurs within the executor. @@ -1695,18 +1709,15 @@ def assert_nullable_type(type_: Any) -> GraphQLNullableType: @overload -def get_nullable_type(type_: None) -> None: - ... +def get_nullable_type(type_: None) -> None: ... @overload -def get_nullable_type(type_: GraphQLNullableType) -> GraphQLNullableType: - ... +def get_nullable_type(type_: GraphQLNullableType) -> GraphQLNullableType: ... @overload -def get_nullable_type(type_: GraphQLNonNull) -> GraphQLNullableType: - ... +def get_nullable_type(type_: GraphQLNonNull) -> GraphQLNullableType: ... def get_nullable_type( @@ -1798,13 +1809,11 @@ def assert_named_type(type_: Any) -> GraphQLNamedType: @overload -def get_named_type(type_: None) -> None: - ... +def get_named_type(type_: None) -> None: ... @overload -def get_named_type(type_: GraphQLType) -> GraphQLNamedType: - ... +def get_named_type(type_: GraphQLType) -> GraphQLNamedType: ... def get_named_type(type_: Optional[GraphQLType]) -> Optional[GraphQLNamedType]: diff --git a/src/graphql/type/directives.py b/src/graphql/type/directives.py index 310968d1..b2ce7a7a 100644 --- a/src/graphql/type/directives.py +++ b/src/graphql/type/directives.py @@ -1,6 +1,6 @@ from typing import Any, Collection, Dict, Optional, Tuple, cast -from ..language import ast, DirectiveLocation +from ..language import DirectiveLocation, ast from ..pyutils import inspect, is_description from .assert_name import assert_name from .definition import GraphQLArgument, GraphQLInputType, GraphQLNonNull, is_input_type @@ -65,9 +65,11 @@ def __init__( assert_name(name) try: locations = tuple( - value - if isinstance(value, DirectiveLocation) - else DirectiveLocation[cast(str, value)] + ( + value + if isinstance(value, DirectiveLocation) + else DirectiveLocation[cast(str, value)] + ) for value in locations ) except (KeyError, TypeError): @@ -90,9 +92,11 @@ def __init__( ) else: args = { - assert_name(name): value - if isinstance(value, GraphQLArgument) - else GraphQLArgument(cast(GraphQLInputType, value)) + assert_name(name): ( + value + if isinstance(value, GraphQLArgument) + else GraphQLArgument(cast(GraphQLInputType, value)) + ) for name, value in args.items() } if not isinstance(is_repeatable, bool): @@ -220,17 +224,17 @@ def assert_directive(directive: Any) -> GraphQLDirective: description="Marks an element of a GraphQL schema as no longer supported.", ) -# Used to provide a URL for specifying the behaviour of custom scalar definitions: +# Used to provide a URL for specifying the behavior of custom scalar definitions: GraphQLSpecifiedByDirective = GraphQLDirective( name="specifiedBy", locations=[DirectiveLocation.SCALAR], args={ "url": GraphQLArgument( GraphQLNonNull(GraphQLString), - description="The URL that specifies the behaviour of this scalar.", + description="The URL that specifies the behavior of this scalar.", ) }, - description="Exposes a URL that specifies the behaviour of this scalar.", + description="Exposes a URL that specifies the behavior of this scalar.", ) diff --git a/src/graphql/type/schema.py b/src/graphql/type/schema.py index 321659c5..8e250a69 100644 --- a/src/graphql/type/schema.py +++ b/src/graphql/type/schema.py @@ -13,16 +13,17 @@ ) from ..error import GraphQLError -from ..language import ast, OperationType +from ..language import OperationType, ast from ..pyutils import inspect, is_collection, is_description from .definition import ( GraphQLAbstractType, - GraphQLInterfaceType, GraphQLInputObjectType, + GraphQLInputType, + GraphQLInterfaceType, GraphQLNamedType, GraphQLObjectType, - GraphQLUnionType, GraphQLType, + GraphQLUnionType, GraphQLWrappingType, get_named_type, is_input_object_type, @@ -31,7 +32,7 @@ is_union_type, is_wrapping_type, ) -from .directives import GraphQLDirective, specified_directives, is_directive +from .directives import GraphQLDirective, is_directive, specified_directives from .introspection import introspection_types try: @@ -270,9 +271,9 @@ def __init__( if iface.name in implementations_map: implementations = implementations_map[iface.name] else: - implementations = implementations_map[ - iface.name - ] = InterfaceImplementations(objects=[], interfaces=[]) + implementations = implementations_map[iface.name] = ( + InterfaceImplementations(objects=[], interfaces=[]) + ) implementations.interfaces.append(named_type) elif is_object_type(named_type): @@ -284,9 +285,9 @@ def __init__( if iface.name in implementations_map: implementations = implementations_map[iface.name] else: - implementations = implementations_map[ - iface.name - ] = InterfaceImplementations(objects=[], interfaces=[]) + implementations = implementations_map[iface.name] = ( + InterfaceImplementations(objects=[], interfaces=[]) + ) implementations.objects.append(named_type) @@ -310,8 +311,8 @@ def __copy__(self) -> "GraphQLSchema": # pragma: no cover def __deepcopy__(self, memo_: Dict) -> "GraphQLSchema": from ..type import ( is_introspection_type, - is_specified_scalar_type, is_specified_directive, + is_specified_scalar_type, ) type_map: TypeMap = { @@ -326,6 +327,8 @@ def __deepcopy__(self, memo_: Dict) -> "GraphQLSchema": directive if is_specified_directive(directive) else copy(directive) for directive in self.directives ] + for directive in directives: + remap_directive(directive, type_map) return self.__class__( self.query_type and cast(GraphQLObjectType, type_map[self.query_type.name]), self.mutation_type @@ -461,12 +464,7 @@ def remapped_type(type_: GraphQLType, type_map: TypeMap) -> GraphQLType: def remap_named_type(type_: GraphQLNamedType, type_map: TypeMap) -> None: """Change all references in the given named type to use this type map.""" - if is_union_type(type_): - type_ = cast(GraphQLUnionType, type_) - type_.types = [ - type_map.get(member_type.name, member_type) for member_type in type_.types - ] - elif is_object_type(type_) or is_interface_type(type_): + if is_object_type(type_) or is_interface_type(type_): type_ = cast(Union[GraphQLObjectType, GraphQLInterfaceType], type_) type_.interfaces = [ type_map.get(interface_type.name, interface_type) @@ -482,6 +480,11 @@ def remap_named_type(type_: GraphQLNamedType, type_map: TypeMap) -> None: arg.type = remapped_type(arg.type, type_map) args[arg_name] = arg fields[field_name] = field + elif is_union_type(type_): + type_ = cast(GraphQLUnionType, type_) + type_.types = [ + type_map.get(member_type.name, member_type) for member_type in type_.types + ] elif is_input_object_type(type_): type_ = cast(GraphQLInputObjectType, type_) fields = type_.fields @@ -489,3 +492,12 @@ def remap_named_type(type_: GraphQLNamedType, type_map: TypeMap) -> None: field = copy(field) field.type = remapped_type(field.type, type_map) fields[field_name] = field + + +def remap_directive(directive: GraphQLDirective, type_map: TypeMap) -> None: + """Change all references in the given directive to use this type map.""" + args = directive.args + for arg_name, arg in args.items(): + arg = copy(arg) # noqa: PLW2901 + arg.type = cast(GraphQLInputType, remapped_type(arg.type, type_map)) + args[arg_name] = arg diff --git a/src/graphql/type/validate.py b/src/graphql/type/validate.py index 555bc30d..2d639ee4 100644 --- a/src/graphql/type/validate.py +++ b/src/graphql/type/validate.py @@ -408,11 +408,13 @@ def validate_type_implements_ancestors( for transitive in iface_interfaces: if transitive not in type_interfaces: self.report_error( - f"Type {type_.name} cannot implement {iface.name}" - " because it would create a circular reference." - if transitive is type_ - else f"Type {type_.name} must implement {transitive.name}" - f" because it is implemented by {iface.name}.", + ( + f"Type {type_.name} cannot implement {iface.name}" + " because it would create a circular reference." + if transitive is type_ + else f"Type {type_.name} must implement {transitive.name}" + f" because it is implemented by {iface.name}." + ), get_all_implements_interface_nodes(iface, transitive) + get_all_implements_interface_nodes(type_, iface), ) diff --git a/src/graphql/utilities/ast_to_dict.py b/src/graphql/utilities/ast_to_dict.py index 9cacd8ab..f344a5e2 100644 --- a/src/graphql/utilities/ast_to_dict.py +++ b/src/graphql/utilities/ast_to_dict.py @@ -3,15 +3,13 @@ from ..language import Node, OperationType from ..pyutils import is_iterable - __all__ = ["ast_to_dict"] @overload def ast_to_dict( node: Node, locations: bool = False, cache: Optional[Dict[Node, Any]] = None -) -> Dict: - ... +) -> Dict: ... @overload @@ -19,8 +17,7 @@ def ast_to_dict( node: Collection[Node], locations: bool = False, cache: Optional[Dict[Node, Any]] = None, -) -> List[Node]: - ... +) -> List[Node]: ... @overload @@ -28,8 +25,7 @@ def ast_to_dict( node: OperationType, locations: bool = False, cache: Optional[Dict[Node, Any]] = None, -) -> str: - ... +) -> str: ... def ast_to_dict( @@ -37,10 +33,8 @@ def ast_to_dict( ) -> Any: """Convert a language AST to a nested Python dictionary. - Set `location` to True in order to get the locations as well. + Set `locations` to True in order to get the locations as well. """ - - """Convert a node to a nested Python dictionary.""" if isinstance(node, Node): if cache is None: cache = {} diff --git a/src/graphql/utilities/extend_schema.py b/src/graphql/utilities/extend_schema.py index 93c8dce4..0a9802a9 100644 --- a/src/graphql/utilities/extend_schema.py +++ b/src/graphql/utilities/extend_schema.py @@ -661,9 +661,11 @@ def build_type(ast_node: TypeDefinitionNode) -> GraphQLNamedType: replace_directive(directive) for directive in schema_kwargs["directives"] ) + tuple(build_directive(directive) for directive in directive_defs), - description=schema_def.description.value - if schema_def and schema_def.description - else None, + description=( + schema_def.description.value + if schema_def and schema_def.description + else None + ), extensions={}, ast_node=schema_def or schema_kwargs["ast_node"], extension_ast_nodes=schema_kwargs["extension_ast_nodes"] diff --git a/src/graphql/utilities/find_breaking_changes.py b/src/graphql/utilities/find_breaking_changes.py index 281a2def..290b9e2b 100644 --- a/src/graphql/utilities/find_breaking_changes.py +++ b/src/graphql/utilities/find_breaking_changes.py @@ -134,7 +134,7 @@ def find_directive_changes( ) ) - for (old_directive, new_directive) in directives_diff.persisted: + for old_directive, new_directive in directives_diff.persisted: args_diff = dict_diff(old_directive.args, new_directive.args) for arg_name, new_arg in args_diff.added.items(): @@ -185,10 +185,12 @@ def find_type_changes( schema_changes.append( BreakingChange( BreakingChangeType.TYPE_REMOVED, - f"Standard scalar {type_name} was removed" - " because it is not referenced anymore." - if is_specified_scalar_type(old_type) - else f"{type_name} was removed.", + ( + f"Standard scalar {type_name} was removed" + " because it is not referenced anymore." + if is_specified_scalar_type(old_type) + else f"{type_name} was removed." + ), ) ) diff --git a/src/graphql/utilities/get_introspection_query.py b/src/graphql/utilities/get_introspection_query.py index aed2348a..ec2c7edc 100644 --- a/src/graphql/utilities/get_introspection_query.py +++ b/src/graphql/utilities/get_introspection_query.py @@ -4,9 +4,9 @@ from ..language import DirectiveLocation try: - from typing import TypedDict, Literal + from typing import Literal, TypedDict except ImportError: # Python < 3.8 - from typing_extensions import TypedDict, Literal # type: ignore + from typing_extensions import Literal, TypedDict # type: ignore __all__ = [ "get_introspection_query", @@ -139,6 +139,14 @@ def input_deprecation(string: str) -> Optional[str]: ofType {{ kind name + ofType {{ + kind + name + ofType {{ + kind + name + }} + }} }} }} }} @@ -284,7 +292,9 @@ class IntrospectionSchema(MaybeWithDescription): directives: List[IntrospectionDirective] -class IntrospectionQuery(TypedDict): - """The root typed dictionary for schema introspections.""" - - __schema: IntrospectionSchema +# The root typed dictionary for schema introspections. +# Note: We don't use class syntax here since the key looks like a private attribute. +IntrospectionQuery = TypedDict( + "IntrospectionQuery", + {"__schema": IntrospectionSchema}, +) diff --git a/src/graphql/utilities/type_from_ast.py b/src/graphql/utilities/type_from_ast.py index d8f2a5be..5c4623b0 100644 --- a/src/graphql/utilities/type_from_ast.py +++ b/src/graphql/utilities/type_from_ast.py @@ -1,13 +1,13 @@ -from typing import cast, overload, Optional +from typing import Optional, cast, overload from ..language import ListTypeNode, NamedTypeNode, NonNullTypeNode, TypeNode from ..pyutils import inspect from ..type import ( - GraphQLSchema, - GraphQLNamedType, GraphQLList, + GraphQLNamedType, GraphQLNonNull, GraphQLNullableType, + GraphQLSchema, GraphQLType, ) @@ -17,27 +17,25 @@ @overload def type_from_ast( schema: GraphQLSchema, type_node: NamedTypeNode -) -> Optional[GraphQLNamedType]: - ... +) -> Optional[GraphQLNamedType]: ... @overload def type_from_ast( schema: GraphQLSchema, type_node: ListTypeNode -) -> Optional[GraphQLList]: - ... +) -> Optional[GraphQLList]: ... @overload def type_from_ast( schema: GraphQLSchema, type_node: NonNullTypeNode -) -> Optional[GraphQLNonNull]: - ... +) -> Optional[GraphQLNonNull]: ... @overload -def type_from_ast(schema: GraphQLSchema, type_node: TypeNode) -> Optional[GraphQLType]: - ... +def type_from_ast( + schema: GraphQLSchema, type_node: TypeNode +) -> Optional[GraphQLType]: ... def type_from_ast( diff --git a/src/graphql/validation/rules/known_type_names.py b/src/graphql/validation/rules/known_type_names.py index 68e10454..aa210899 100644 --- a/src/graphql/validation/rules/known_type_names.py +++ b/src/graphql/validation/rules/known_type_names.py @@ -62,9 +62,11 @@ def enter_named_type( suggested_types = suggestion_list( type_name, - list(standard_type_names) + self.type_names - if is_sdl - else self.type_names, + ( + list(standard_type_names) + self.type_names + if is_sdl + else self.type_names + ), ) self.report_error( GraphQLError( diff --git a/src/graphql/validation/rules/no_undefined_variables.py b/src/graphql/validation/rules/no_undefined_variables.py index a890473f..7e4a6873 100644 --- a/src/graphql/validation/rules/no_undefined_variables.py +++ b/src/graphql/validation/rules/no_undefined_variables.py @@ -34,10 +34,12 @@ def leave_operation_definition( if var_name not in defined_variables: self.report_error( GraphQLError( - f"Variable '${var_name}' is not defined" - f" by operation '{operation.name.value}'." - if operation.name - else f"Variable '${var_name}' is not defined.", + ( + f"Variable '${var_name}' is not defined" + f" by operation '{operation.name.value}'." + if operation.name + else f"Variable '${var_name}' is not defined." + ), [node, operation], ) ) diff --git a/src/graphql/validation/rules/no_unused_variables.py b/src/graphql/validation/rules/no_unused_variables.py index b8770944..968b0a4b 100644 --- a/src/graphql/validation/rules/no_unused_variables.py +++ b/src/graphql/validation/rules/no_unused_variables.py @@ -37,10 +37,12 @@ def leave_operation_definition( if variable_name not in variable_name_used: self.report_error( GraphQLError( - f"Variable '${variable_name}' is never used" - f" in operation '{operation.name.value}'." - if operation.name - else f"Variable '${variable_name}' is never used.", + ( + f"Variable '${variable_name}' is never used" + f" in operation '{operation.name.value}'." + if operation.name + else f"Variable '${variable_name}' is never used." + ), variable_def, ) ) diff --git a/src/graphql/validation/rules/overlapping_fields_can_be_merged.py b/src/graphql/validation/rules/overlapping_fields_can_be_merged.py index 33c1b09e..d8e509d4 100644 --- a/src/graphql/validation/rules/overlapping_fields_can_be_merged.py +++ b/src/graphql/validation/rules/overlapping_fields_can_be_merged.py @@ -3,13 +3,13 @@ from ...error import GraphQLError from ...language import ( + DirectiveNode, FieldNode, FragmentDefinitionNode, FragmentSpreadNode, InlineFragmentNode, - ObjectFieldNode, - ObjectValueNode, SelectionSetNode, + ValueNode, print_ast, ) from ...type import ( @@ -551,7 +551,7 @@ def find_conflict( ) # Two field calls must have the same arguments. - if stringify_arguments(node1) != stringify_arguments(node2): + if not same_arguments(node1, node2): return (response_name, "they have differing arguments"), [node1], [node2] if type1 and type2 and do_types_conflict(type1, type2): @@ -582,14 +582,34 @@ def find_conflict( return None # no conflict -def stringify_arguments(field_node: FieldNode) -> str: - input_object_with_args = ObjectValueNode( - fields=tuple( - ObjectFieldNode(name=arg_node.name, value=arg_node.value) - for arg_node in field_node.arguments - ) - ) - return print_ast(sort_value_node(input_object_with_args)) +def same_arguments( + node1: Union[FieldNode, DirectiveNode], node2: Union[FieldNode, DirectiveNode] +) -> bool: + args1 = node1.arguments + args2 = node2.arguments + + if not args1: + return not args2 + + if not args2: + return False + + if len(args1) != len(args2): + return False # pragma: no cover + + values2 = {arg.name.value: arg.value for arg in args2} + + for arg1 in args1: + value1 = arg1.value + value2 = values2.get(arg1.name.value) + if value2 is None or stringify_value(value1) != stringify_value(value2): + return False + + return True + + +def stringify_value(value: ValueNode) -> str: + return print_ast(sort_value_node(value)) def do_types_conflict(type1: GraphQLOutputType, type2: GraphQLOutputType) -> bool: diff --git a/src/graphql/validation/validate.py b/src/graphql/validation/validate.py index 13dc5243..70e7f68d 100644 --- a/src/graphql/validation/validate.py +++ b/src/graphql/validation/validate.py @@ -2,8 +2,8 @@ from ..error import GraphQLError from ..language import DocumentNode, ParallelVisitor, visit -from ..type import GraphQLSchema, assert_valid_schema from ..pyutils import inspect, is_collection +from ..type import GraphQLSchema, assert_valid_schema from ..utilities import TypeInfo, TypeInfoVisitor from .rules import ASTValidationRule from .specified_rules import specified_rules, specified_sdl_rules @@ -65,7 +65,7 @@ def validate( errors: List[GraphQLError] = [] def on_error(error: GraphQLError) -> None: - if len(errors) >= max_errors: # type: ignore + if len(errors) >= max_errors: errors.append( GraphQLError( "Too many validation errors, error limit reached." diff --git a/src/graphql/version.py b/src/graphql/version.py index 424a9851..c7231c92 100644 --- a/src/graphql/version.py +++ b/src/graphql/version.py @@ -4,9 +4,9 @@ __all__ = ["version", "version_info", "version_js", "version_info_js"] -version = "3.2.2" +version = "3.2.6" -version_js = "16.4.0" +version_js = "16.8.2" _re_version = re.compile(r"(\d+)\.(\d+)\.(\d+)(\D*)(\d*)") diff --git a/tests/benchmarks/test_repeated_fields.py b/tests/benchmarks/test_repeated_fields.py new file mode 100644 index 00000000..daba6169 --- /dev/null +++ b/tests/benchmarks/test_repeated_fields.py @@ -0,0 +1,25 @@ +from graphql import ( + GraphQLField, + GraphQLObjectType, + GraphQLSchema, + GraphQLString, + graphql_sync, +) + +schema = GraphQLSchema( + query=GraphQLObjectType( + name="Query", + fields={ + "hello": GraphQLField( + GraphQLString, + resolve=lambda _obj, _info: "world", + ) + }, + ) +) +source = f"{{ {'hello ' * 250}}}" + + +def test_many_repeated_fields(benchmark): + result = benchmark(lambda: graphql_sync(schema, source)) + assert result == ({"hello": "world"}, None) diff --git a/tests/execution/test_abstract.py b/tests/execution/test_abstract.py index efc130f1..84da37c4 100644 --- a/tests/execution/test_abstract.py +++ b/tests/execution/test_abstract.py @@ -3,7 +3,7 @@ from pytest import mark -from graphql.execution import execute, execute_sync, ExecutionResult +from graphql.execution import ExecutionResult, execute, execute_sync from graphql.language import parse from graphql.type import ( GraphQLBoolean, @@ -40,9 +40,7 @@ async def execute_query( assert isinstance(schema, GraphQLSchema) assert isinstance(query, str) document = parse(query) - result = (execute_sync if sync else execute)( - schema, document, root_value - ) # type: ignore + result = (execute_sync if sync else execute)(schema, document, root_value) if not sync and isawaitable(result): result = await result assert isinstance(result, ExecutionResult) diff --git a/tests/execution/test_executor.py b/tests/execution/test_executor.py index 7cd2260a..b6a968d0 100644 --- a/tests/execution/test_executor.py +++ b/tests/execution/test_executor.py @@ -1,12 +1,10 @@ import asyncio -from typing import cast, Any, Awaitable, Optional - -from pytest import mark, raises +from typing import Any, Awaitable, Optional, cast from graphql.error import GraphQLError from graphql.execution import execute, execute_sync -from graphql.language import parse, FieldNode, OperationDefinitionNode -from graphql.pyutils import inspect, Undefined +from graphql.language import FieldNode, OperationDefinitionNode, parse +from graphql.pyutils import Undefined, inspect from graphql.type import ( GraphQLArgument, GraphQLBoolean, @@ -17,12 +15,13 @@ GraphQLNonNull, GraphQLObjectType, GraphQLResolveInfo, - GraphQLSchema, GraphQLScalarType, + GraphQLSchema, GraphQLString, GraphQLUnionType, ResponsePath, ) +from pytest import mark, raises def describe_execute_handles_basic_execution_tasks(): @@ -567,6 +566,54 @@ async def asyncReturnErrorWithExtensions(self, _info): ], ) + @mark.filterwarnings("ignore:.* was never awaited:RuntimeWarning") + def handles_sync_errors_combined_with_async_ones(): + is_async_resolver_finished = False + + async def async_resolver(_obj, _info): + nonlocal is_async_resolver_finished + is_async_resolver_finished = True # pragma: no cover + + schema = GraphQLSchema( + GraphQLObjectType( + "Query", + { + "syncNullError": GraphQLField( + GraphQLNonNull(GraphQLString), resolve=lambda _obj, _info: None + ), + "asyncNullError": GraphQLField( + GraphQLNonNull(GraphQLString), resolve=async_resolver + ), + }, + ) + ) + + document = parse( + """ + { + asyncNullError + syncNullError + } + """ + ) + + result = execute(schema, document) + + assert is_async_resolver_finished is False + + assert result == ( + None, + [ + { + "message": "Cannot return null" + " for non-nullable field Query.syncNullError.", + "locations": [(4, 15)], + "path": ["syncNullError"], + } + ], + ) + + @mark.filterwarnings("ignore:.* was never awaited:RuntimeWarning") def full_response_path_is_included_for_non_nullable_fields(): def resolve_ok(*_args): return {} @@ -616,6 +663,7 @@ def resolve_error(*_args): ], ) + @mark.filterwarnings("ignore:.* was never awaited:RuntimeWarning") def uses_the_inline_operation_if_no_operation_name_is_provided(): schema = GraphQLSchema( GraphQLObjectType("Type", {"a": GraphQLField(GraphQLString)}) @@ -629,6 +677,7 @@ class Data: result = execute_sync(schema, document, Data()) assert result == ({"a": "b"}, None) + @mark.filterwarnings("ignore:.* was never awaited:RuntimeWarning") def uses_the_only_operation_if_no_operation_name_is_provided(): schema = GraphQLSchema( GraphQLObjectType("Type", {"a": GraphQLField(GraphQLString)}) @@ -642,6 +691,7 @@ class Data: result = execute_sync(schema, document, Data()) assert result == ({"a": "b"}, None) + @mark.filterwarnings("ignore:.* was never awaited:RuntimeWarning") def uses_the_named_operation_if_operation_name_is_provided(): schema = GraphQLSchema( GraphQLObjectType("Type", {"a": GraphQLField(GraphQLString)}) diff --git a/tests/execution/test_map_async_iterator.py b/tests/execution/test_map_async_iterator.py index 299d010a..4f7be06f 100644 --- a/tests/execution/test_map_async_iterator.py +++ b/tests/execution/test_map_async_iterator.py @@ -1,13 +1,14 @@ +import platform import sys from asyncio import CancelledError, Event, ensure_future, sleep -from pytest import mark, raises - from graphql.execution import MapAsyncIterator +from pytest import mark, raises +is_pypy = platform.python_implementation() == "PyPy" try: # pragma: no cover - anext + anext # type: ignore except NameError: # pragma: no cover (Python < 3.10) # noinspection PyShadowingBuiltins async def anext(iterator): @@ -344,6 +345,10 @@ def double(x): with raises(StopAsyncIteration): await anext(doubles) + # no more exceptions should be thrown + if is_pypy: + # need to investigate why this is needed with PyPy + await doubles.aclose() # pragma: no cover await doubles.athrow(RuntimeError("no more ouch")) with raises(StopAsyncIteration): diff --git a/tests/execution/test_resolve.py b/tests/execution/test_resolve.py index f945e6c3..0da39b46 100644 --- a/tests/execution/test_resolve.py +++ b/tests/execution/test_resolve.py @@ -2,14 +2,16 @@ from typing import Any from graphql.error import GraphQLError -from graphql.execution import execute_sync, ExecutionResult -from graphql.language import parse, SourceLocation +from graphql.execution import ExecutionResult, execute_sync +from graphql.language import SourceLocation, parse from graphql.type import ( GraphQLArgument, GraphQLField, + GraphQLID, GraphQLInputField, GraphQLInputObjectType, GraphQLInt, + GraphQLList, GraphQLObjectType, GraphQLSchema, GraphQLString, @@ -213,6 +215,91 @@ def execute_query(query: str, root_value: Any = None) -> ExecutionResult: None, ) + def transforms_default_values_using_out_names(): + # This is an extension of GraphQL.js. + resolver_kwargs: Any + + def search_resolver(_obj: None, _info, **kwargs): + nonlocal resolver_kwargs + resolver_kwargs = kwargs + return [{"id": "42"}] + + filters_type = GraphQLInputObjectType( + "SearchFilters", + {"pageSize": GraphQLInputField(GraphQLInt, out_name="page_size")}, + ) + result_type = GraphQLObjectType("SearchResult", {"id": GraphQLField(GraphQLID)}) + query = GraphQLObjectType( + "Query", + { + "search": GraphQLField( + GraphQLList(result_type), + { + "searchFilters": GraphQLArgument( + filters_type, {"pageSize": 10}, out_name="search_filters" + ) + }, + resolve=search_resolver, + ) + }, + ) + schema = GraphQLSchema(query) + + resolver_kwargs = None + result = execute_sync(schema, parse("{ search { id } }")) + assert result == ({"search": [{"id": "42"}]}, None) + assert resolver_kwargs == {"search_filters": {"page_size": 10}} + + resolver_kwargs = None + result = execute_sync( + schema, parse("{ search(searchFilters:{pageSize: 25}) { id } }") + ) + assert result == ({"search": [{"id": "42"}]}, None) + assert resolver_kwargs == {"search_filters": {"page_size": 25}} + + resolver_kwargs = None + result = execute_sync( + schema, + parse( + """ + query ($searchFilters: SearchFilters) { + search(searchFilters: $searchFilters) { id } + } + """ + ), + ) + assert result == ({"search": [{"id": "42"}]}, None) + assert resolver_kwargs == {"search_filters": {"page_size": 10}} + + resolver_kwargs = None + result = execute_sync( + schema, + parse( + """ + query ($searchFilters: SearchFilters) { + search(searchFilters: $searchFilters) { id } + } + """ + ), + variable_values={"searchFilters": {"pageSize": 25}}, + ) + assert result == ({"search": [{"id": "42"}]}, None) + assert resolver_kwargs == {"search_filters": {"page_size": 25}} + + resolver_kwargs = None + result = execute_sync( + schema, + parse( + """ + query ($searchFilters: SearchFilters = {pageSize: 25}) { + search(searchFilters: $searchFilters) { id } + } + """ + ), + ) + assert result == ({"search": [{"id": "42"}]}, None) + assert resolver_kwargs == {"search_filters": {"page_size": 25}} + def pass_error_from_resolver_wrapped_as_located_graphql_error(): def resolve(_obj, _info): raise ValueError("Some error") diff --git a/tests/execution/test_subscribe.py b/tests/execution/test_subscribe.py index 8b1fe639..a4a780a3 100644 --- a/tests/execution/test_subscribe.py +++ b/tests/execution/test_subscribe.py @@ -1,9 +1,12 @@ import asyncio +from typing import Any, Callable, Dict, List -from typing import Any, Dict, List, Callable - -from pytest import mark, raises - +from graphql.execution import ( + ExecutionResult, + MapAsyncIterator, + create_source_event_stream, + subscribe, +) from graphql.language import parse from graphql.pyutils import SimplePubSub from graphql.type import ( @@ -16,10 +19,10 @@ GraphQLSchema, GraphQLString, ) -from graphql.execution import create_source_event_stream, subscribe, MapAsyncIterator +from pytest import mark, raises try: - anext + anext # type: ignore except NameError: # pragma: no cover (Python < 3.10) # noinspection PyShadowingBuiltins async def anext(iterator): @@ -448,6 +451,8 @@ async def resolves_to_an_error_if_variables_were_wrong_type(): # resolve to an ExecutionResult that contains an informative error description. result = await subscribe(schema, document, variable_values=variable_values) + assert isinstance(result, ExecutionResult) + assert result == ( None, [ @@ -459,7 +464,9 @@ async def resolves_to_an_error_if_variables_were_wrong_type(): ], ) - assert result.errors[0].original_error is None # type: ignore + errors = result.errors + assert errors + assert errors[0].original_error # Once a subscription returns a valid AsyncIterator, it can still yield errors. @@ -855,7 +862,7 @@ def resolve_message(message, _info): subscription = await subscribe(schema, document) assert isinstance(subscription, MapAsyncIterator) - assert await (anext(subscription)) == ({"newMessage": "Hello"}, None) + assert await anext(subscription) == ({"newMessage": "Hello"}, None) with raises(RuntimeError) as exc_info: await anext(subscription) diff --git a/tests/execution/test_variables.py b/tests/execution/test_variables.py index dde674c7..095d4663 100644 --- a/tests/execution/test_variables.py +++ b/tests/execution/test_variables.py @@ -1,9 +1,10 @@ from math import nan from typing import Any, Dict, Optional -from graphql.execution import execute_sync, ExecutionResult +from graphql.error import GraphQLError +from graphql.execution import ExecutionResult, execute_sync from graphql.execution.values import get_variable_values -from graphql.language import parse, OperationDefinitionNode, StringValueNode, ValueNode +from graphql.language import OperationDefinitionNode, StringValueNode, ValueNode, parse from graphql.pyutils import Undefined from graphql.type import ( GraphQLArgument, @@ -21,6 +22,25 @@ GraphQLString, ) +TestFaultyScalarGraphQLError = GraphQLError( + "FaultyScalarErrorMessage", extensions={"code": "FaultyScalarExtensionCode"} +) + + +def faulty_parse_value(value: str) -> str: + raise TestFaultyScalarGraphQLError + + +def faulty_parse_literal(ast: ValueNode, _variables=None) -> str: + raise TestFaultyScalarGraphQLError + + +TestFaultyScalar = GraphQLScalarType( + name="FaultyScalar", + parse_value=faulty_parse_value, + parse_literal=faulty_parse_literal, +) + def parse_serialized_value(value: str) -> str: assert value == "SerializedValue" @@ -47,6 +67,7 @@ def parse_literal_value(ast: ValueNode, _variables=None) -> str: "b": GraphQLInputField(GraphQLList(GraphQLString)), "c": GraphQLInputField(GraphQLNonNull(GraphQLString)), "d": GraphQLInputField(TestComplexScalar), + "e": GraphQLInputField(TestFaultyScalar), }, ) @@ -83,9 +104,9 @@ def field_with_input_arg(input_arg: GraphQLArgument): return GraphQLField( GraphQLString, args={"input": input_arg}, - resolve=lambda _obj, _info, **args: repr(args["input"]) - if "input" in args - else None, + resolve=lambda _obj, _info, **args: ( + repr(args["input"]) if "input" in args else None + ), ) @@ -253,6 +274,27 @@ def properly_runs_parse_literal_on_complex_scalar_types(): None, ) + def errors_on_faulty_scalar_type_input(): + result = execute_query( + """ + { + fieldWithObjectInput(input: {c: "foo", e: "bar"}) + } + """ + ) + + assert result == ( + {"fieldWithObjectInput": None}, + [ + { + "message": "Argument 'input' has invalid value" + ' {c: "foo", e: "bar"}.', + "path": ["fieldWithObjectInput"], + "locations": [(3, 51)], + } + ], + ) + def describe_using_variables(): doc = """ query ($input: TestInputObject) { @@ -365,6 +407,22 @@ def executes_with_complex_scalar_input(): None, ) + def errors_on_faulty_scalar_type_input(): + params = {"input": {"c": "foo", "e": "SerializedValue"}} + result = execute_query(doc, params) + + assert result == ( + None, + [ + { + "message": "Variable '$input' got invalid value" + " 'SerializedValue' at 'input.e'; FaultyScalarErrorMessage", + "locations": [(2, 24)], + "extensions": {"code": "FaultyScalarExtensionCode"}, + } + ], + ) + def errors_on_null_for_nested_non_null(): params = {"input": {"a": "foo", "b": "bar", "c": None}} result = execute_query(doc, params) @@ -676,8 +734,8 @@ def reports_error_for_array_passed_into_string_input(): ) errors = result.errors - assert errors is not None - assert errors[0].original_error is None + assert errors + assert errors[0].original_error def reports_error_for_non_provided_variables_for_non_nullable_inputs(): # Note: this test would typically fail validation before diff --git a/tests/fixtures/__init__.py b/tests/fixtures/__init__.py index fe63e16b..3dcb8791 100644 --- a/tests/fixtures/__init__.py +++ b/tests/fixtures/__init__.py @@ -1,4 +1,5 @@ """Fixtures for graphql tests""" + import json from os.path import dirname, join diff --git a/tests/language/test_parser.py b/tests/language/test_parser.py index 027a605b..650e4c65 100644 --- a/tests/language/test_parser.py +++ b/tests/language/test_parser.py @@ -91,6 +91,23 @@ def parse_provides_useful_error_when_using_source(): """ ) + def limits_maximum_number_of_tokens(): + assert parse("{ foo }", max_tokens=3) + with raises( + GraphQLSyntaxError, + match="Syntax Error: Document contains more than 2 tokens." + " Parsing aborted.", + ): + assert parse("{ foo }", max_tokens=2) + + assert parse('{ foo(bar: "baz") }', max_tokens=8) + with raises( + GraphQLSyntaxError, + match="Syntax Error: Document contains more than 7 tokens." + " Parsing aborted.", + ): + assert parse('{ foo(bar: "baz") }', max_tokens=7) + def parses_variable_inline_values(): parse("{ field(complex: { a: { b: [ $var ] } }) }") diff --git a/tests/pyutils/test_description.py b/tests/pyutils/test_description.py index af87ccb6..61e1db95 100644 --- a/tests/pyutils/test_description.py +++ b/tests/pyutils/test_description.py @@ -1,27 +1,26 @@ from contextlib import contextmanager from typing import cast -from pytest import raises - from graphql import graphql_sync +from graphql.pyutils import ( + Description, + is_description, + register_description, + unregister_description, +) from graphql.type import ( GraphQLArgument, GraphQLDirective, GraphQLEnumValue, GraphQLField, GraphQLInputField, - GraphQLObjectType, GraphQLNamedType, + GraphQLObjectType, GraphQLSchema, GraphQLString, ) -from graphql.pyutils import ( - Description, - is_description, - register_description, - unregister_description, -) from graphql.utilities import get_introspection_query, print_schema +from pytest import raises from ..utils import dedent @@ -43,7 +42,7 @@ def registered(base: type): try: yield None finally: - unregister_description(LazyString) + unregister_description(base) def describe_description(): diff --git a/tests/pyutils/test_frozen_dict.py b/tests/pyutils/test_frozen_dict.py index 594f4826..43615b7f 100644 --- a/tests/pyutils/test_frozen_dict.py +++ b/tests/pyutils/test_frozen_dict.py @@ -1,9 +1,8 @@ from copy import copy, deepcopy +from graphql.pyutils import FrozenDict, FrozenError from pytest import raises -from graphql.pyutils import FrozenError, FrozenDict - def describe_frozen_list(): def can_read(): @@ -59,7 +58,7 @@ def can_hash(): fd2 = FrozenDict({1: 2, 3: 4}) assert fd2 == fd1 assert fd2 is not fd1 - assert hash(fd2) is not hash(fd1) + assert hash(fd2) == hash(fd1) fd3 = FrozenDict({1: 2, 3: 5}) assert fd3 != fd1 assert hash(fd3) != hash(fd1) diff --git a/tests/pyutils/test_is_awaitable.py b/tests/pyutils/test_is_awaitable.py index 896697d5..96810713 100644 --- a/tests/pyutils/test_is_awaitable.py +++ b/tests/pyutils/test_is_awaitable.py @@ -1,9 +1,9 @@ import asyncio +import sys from inspect import isawaitable -from pytest import mark - from graphql.pyutils import is_awaitable +from pytest import mark def describe_is_awaitable(): @@ -76,7 +76,11 @@ async def some_coroutine(): assert is_awaitable(some_coroutine()) @mark.filterwarnings("ignore::Warning") # Deprecation and Runtime - def recognizes_an_old_style_coroutine(): + @mark.skipif( + sys.version_info >= (3, 11), + reason="Generator-based coroutines not supported after Python 3.11", + ) + def recognizes_an_old_style_coroutine(): # pragma: no cover @asyncio.coroutine def some_old_style_coroutine(): yield False # pragma: no cover diff --git a/tests/test_user_registry.py b/tests/test_user_registry.py index d5f2ba95..9e38448b 100644 --- a/tests/test_user_registry.py +++ b/tests/test_user_registry.py @@ -15,12 +15,7 @@ except ImportError: # Python < 3.7 create_task = None # type: ignore -from pytest import fixture, mark - from graphql import ( - graphql, - parse, - subscribe, GraphQLArgument, GraphQLBoolean, GraphQLEnumType, @@ -33,10 +28,13 @@ GraphQLObjectType, GraphQLSchema, GraphQLString, + graphql, + parse, + subscribe, ) - -from graphql.pyutils import SimplePubSub, SimplePubSubIterator from graphql.execution.map_async_iterator import MapAsyncIterator +from graphql.pyutils import SimplePubSub, SimplePubSubIterator +from pytest import fixture, mark class User(NamedTuple): @@ -498,22 +496,23 @@ async def mutate_users(): ) async def receive_one(): - async for result in subscription_one: # type: ignore # pragma: no cover + async for result in subscription_one: # pragma: no cover received_one.append(result) if len(received_one) == 3: # pragma: no cover else break async def receive_all(): - async for result in subscription_all: # type: ignore # pragma: no cover + async for result in subscription_all: # pragma: no cover received_all.append(result) if len(received_all) == 6: # pragma: no cover else break tasks = [ - create_task(task()) if create_task else task() + create_task(task()) if create_task else task() # type: ignore for task in (mutate_users, receive_one, receive_all) ] done, pending = await wait(tasks, timeout=1) + assert len(done) == len(tasks) assert not pending expected_data: List[Dict[str, Any]] = [ diff --git a/tests/type/test_definition.py b/tests/type/test_definition.py index 1c5b5bfc..075e3f74 100644 --- a/tests/type/test_definition.py +++ b/tests/type/test_definition.py @@ -1,21 +1,18 @@ from enum import Enum from math import isnan, nan -from typing import cast, Dict - -from pytest import mark, raises +from typing import Dict, cast from graphql.error import GraphQLError from graphql.language import ( - parse_value, EnumTypeDefinitionNode, EnumTypeExtensionNode, EnumValueNode, - Node, InputObjectTypeDefinitionNode, InputObjectTypeExtensionNode, InputValueDefinitionNode, InterfaceTypeDefinitionNode, InterfaceTypeExtensionNode, + Node, ObjectTypeDefinitionNode, ObjectTypeExtensionNode, ScalarTypeDefinitionNode, @@ -23,15 +20,16 @@ StringValueNode, TypeDefinitionNode, TypeExtensionNode, - ValueNode, UnionTypeDefinitionNode, UnionTypeExtensionNode, + ValueNode, + parse_value, ) from graphql.pyutils import Undefined from graphql.type import ( GraphQLArgument, - GraphQLEnumValue, GraphQLEnumType, + GraphQLEnumValue, GraphQLField, GraphQLInputField, GraphQLInputObjectType, @@ -44,6 +42,7 @@ GraphQLString, GraphQLUnionType, ) +from pytest import mark, raises ScalarType = GraphQLScalarType("Scalar") ObjectType = GraphQLObjectType("Object", {}) @@ -1071,42 +1070,40 @@ def defines_an_enum_using_an_enum_value_map(): assert enum_type.values == {"RED": red, "BLUE": blue} def defines_an_enum_using_a_python_enum(): - colors = Enum("Colors", "RED BLUE") - enum_type = GraphQLEnumType("SomeEnum", colors) + Colors = Enum("Colors", "RED BLUE") + enum_type = GraphQLEnumType("SomeEnum", Colors) assert enum_type.values == { "RED": GraphQLEnumValue(1), "BLUE": GraphQLEnumValue(2), } def defines_an_enum_using_values_of_a_python_enum(): - colors = Enum("Colors", "RED BLUE") - enum_type = GraphQLEnumType("SomeEnum", colors, names_as_values=False) + Colors = Enum("Colors", "RED BLUE") + enum_type = GraphQLEnumType("SomeEnum", Colors, names_as_values=False) assert enum_type.values == { "RED": GraphQLEnumValue(1), "BLUE": GraphQLEnumValue(2), } def defines_an_enum_using_names_of_a_python_enum(): - colors = Enum("Colors", "RED BLUE") - enum_type = GraphQLEnumType("SomeEnum", colors, names_as_values=True) + Colors = Enum("Colors", "RED BLUE") + enum_type = GraphQLEnumType("SomeEnum", Colors, names_as_values=True) assert enum_type.values == { "RED": GraphQLEnumValue("RED"), "BLUE": GraphQLEnumValue("BLUE"), } def defines_an_enum_using_members_of_a_python_enum(): - colors = Enum("Colors", "RED BLUE") - enum_type = GraphQLEnumType("SomeEnum", colors, names_as_values=None) + Colors = Enum("Colors", "RED BLUE") + enum_type = GraphQLEnumType("SomeEnum", Colors, names_as_values=None) assert enum_type.values == { - "RED": GraphQLEnumValue(colors.RED), - "BLUE": GraphQLEnumValue(colors.BLUE), + "RED": GraphQLEnumValue(Colors.RED), + "BLUE": GraphQLEnumValue(Colors.BLUE), } def defines_an_enum_type_with_a_description(): description = "nice enum" - enum_type = GraphQLEnumType( - "SomeEnum", {}, description=description # type: ignore - ) + enum_type = GraphQLEnumType("SomeEnum", {}, description=description) assert enum_type.description is description assert enum_type.to_kwargs()["description"] is description @@ -1256,7 +1253,7 @@ def accepts_an_enum_type_with_ast_node_and_extension_ast_nodes(): extension_ast_nodes = [EnumTypeExtensionNode()] enum_type = GraphQLEnumType( "SomeEnum", - {}, # type: ignore + {}, ast_node=ast_node, extension_ast_nodes=extension_ast_nodes, ) diff --git a/tests/type/test_enum.py b/tests/type/test_enum.py index f85a466b..89b5a9a7 100644 --- a/tests/type/test_enum.py +++ b/tests/type/test_enum.py @@ -75,10 +75,13 @@ class Complex2: resolve=lambda _source, info, **args: # Note: this is one of the references of the internal values # which ComplexEnum allows. - complex2 if args.get("provideGoodValue") - # Note: similar object, but not the same *reference* as - # complex2 above. Enum internal values require object equality. - else Complex2() if args.get("provideBadValue") else args.get("fromEnum"), + ( + complex2 + if args.get("provideGoodValue") + # Note: similar object, but not the same *reference* as + # complex2 above. Enum internal values require object equality. + else Complex2() if args.get("provideBadValue") else args.get("fromEnum") + ), ), }, ) diff --git a/tests/utilities/test_build_ast_schema.py b/tests/utilities/test_build_ast_schema.py index ba9c6050..26ba3b8c 100644 --- a/tests/utilities/test_build_ast_schema.py +++ b/tests/utilities/test_build_ast_schema.py @@ -4,24 +4,24 @@ from pytest import mark, raises from graphql import graphql_sync -from graphql.language import parse, print_ast, DocumentNode, InterfaceTypeDefinitionNode +from graphql.language import DocumentNode, InterfaceTypeDefinitionNode, parse, print_ast from graphql.type import ( - GraphQLDeprecatedDirective, - GraphQLIncludeDirective, - GraphQLSchema, - GraphQLSkipDirective, - GraphQLSpecifiedByDirective, - GraphQLBoolean, - GraphQLFloat, - GraphQLID, - GraphQLInt, - GraphQLString, GraphQLArgument, + GraphQLBoolean, + GraphQLDeprecatedDirective, GraphQLEnumType, GraphQLEnumValue, GraphQLField, + GraphQLFloat, + GraphQLID, + GraphQLIncludeDirective, GraphQLInputField, + GraphQLInt, GraphQLNamedType, + GraphQLSchema, + GraphQLSkipDirective, + GraphQLSpecifiedByDirective, + GraphQLString, assert_directive, assert_enum_type, assert_input_object_type, @@ -1121,7 +1121,7 @@ def can_build_invalid_schema(): assert errors def do_not_override_standard_types(): - # Note: not sure it's desired behaviour to just silently ignore override + # Note: not sure it's desired behavior to just silently ignore override # attempts so just documenting it here. schema = build_schema( diff --git a/tests/utilities/test_build_client_schema.py b/tests/utilities/test_build_client_schema.py index b566ba1b..9bea7c7e 100644 --- a/tests/utilities/test_build_client_schema.py +++ b/tests/utilities/test_build_client_schema.py @@ -1,7 +1,5 @@ from typing import cast -from pytest import raises - from graphql import graphql_sync from graphql.type import ( GraphQLArgument, @@ -18,8 +16,8 @@ assert_enum_type, ) from graphql.utilities import ( - build_schema, build_client_schema, + build_schema, introspection_from_schema, print_schema, ) @@ -31,6 +29,7 @@ IntrospectionType, IntrospectionUnionType, ) +from pytest import raises from ..utils import dedent @@ -991,11 +990,11 @@ def throws_when_missing_directive_args(): build_client_schema(introspection) def describe_very_deep_decorators_are_not_supported(): - def fails_on_very_deep_lists_more_than_7_levels(): + def fails_on_very_deep_lists_more_than_8_levels(): schema = build_schema( """ type Query { - foo: [[[[[[[[String]]]]]]]] + foo: [[[[[[[[[[String]]]]]]]]]] } """ ) @@ -1010,11 +1009,11 @@ def fails_on_very_deep_lists_more_than_7_levels(): " Decorated type deeper than introspection query." ) - def fails_on_a_very_deep_non_null_more_than_7_levels(): + def fails_on_a_very_deep_non_null_more_than_8_levels(): schema = build_schema( """ type Query { - foo: [[[[String!]!]!]!] + foo: [[[[[String!]!]!]!]!] } """ ) @@ -1029,12 +1028,12 @@ def fails_on_a_very_deep_non_null_more_than_7_levels(): " Decorated type deeper than introspection query." ) - def succeeds_on_deep_types_less_or_equal_7_levels(): - # e.g., fully non-null 3D matrix + def succeeds_on_deep_types_less_or_equal_8_levels(): + # e.g., fully non-null 4D matrix sdl = dedent( """ type Query { - foo: [[[String!]!]!]! + foo: [[[[String!]!]!]!]! } """ ) diff --git a/tests/utilities/test_print_schema.py b/tests/utilities/test_print_schema.py index 37337b23..aaad8d15 100644 --- a/tests/utilities/test_print_schema.py +++ b/tests/utilities/test_print_schema.py @@ -1,12 +1,14 @@ -from typing import cast, Any, Dict +from typing import Any, Dict, cast from graphql.language import DirectiveLocation from graphql.type import ( GraphQLArgument, GraphQLBoolean, + GraphQLDirective, GraphQLEnumType, GraphQLField, GraphQLFloat, + GraphQLInputField, GraphQLInputObjectType, GraphQLInt, GraphQLInterfaceType, @@ -17,13 +19,11 @@ GraphQLSchema, GraphQLString, GraphQLUnionType, - GraphQLInputField, - GraphQLDirective, ) from graphql.utilities import ( build_schema, - print_schema, print_introspection_schema, + print_schema, print_value, ) @@ -667,9 +667,9 @@ def prints_introspection_schema(): reason: String = "No longer supported" ) on FIELD_DEFINITION | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION | ENUM_VALUE - """Exposes a URL that specifies the behaviour of this scalar.""" + """Exposes a URL that specifies the behavior of this scalar.""" directive @specifiedBy( - """The URL that specifies the behaviour of this scalar.""" + """The URL that specifies the behavior of this scalar.""" url: String! ) on SCALAR diff --git a/tests/validation/harness.py b/tests/validation/harness.py index 214a91bc..b30ae9f7 100644 --- a/tests/validation/harness.py +++ b/tests/validation/harness.py @@ -1,10 +1,10 @@ -from typing import List, Optional, Type +from typing import Any, Dict, List, Optional, Type, Union from graphql.error import GraphQLError from graphql.language import parse from graphql.type import GraphQLSchema from graphql.utilities import build_schema -from graphql.validation import ValidationRule, SDLValidationRule +from graphql.validation import ASTValidationRule from graphql.validation.validate import validate, validate_sdl __all__ = [ @@ -121,9 +121,9 @@ def assert_validation_errors( - rule: Type[ValidationRule], + rule: Type[ASTValidationRule], query_str: str, - errors: List[GraphQLError], + errors: List[Union[GraphQLError, Dict[str, Any]]], schema: GraphQLSchema = test_schema, ) -> List[GraphQLError]: doc = parse(query_str) @@ -133,9 +133,9 @@ def assert_validation_errors( def assert_sdl_validation_errors( - rule: Type[SDLValidationRule], + rule: Type[ASTValidationRule], sdl_str: str, - errors: List[GraphQLError], + errors: List[Union[GraphQLError, Dict[str, Any]]], schema: Optional[GraphQLSchema] = None, ) -> List[GraphQLError]: doc = parse(sdl_str) diff --git a/tox.ini b/tox.ini index f9c3a5e2..249bdccc 100644 --- a/tox.ini +++ b/tox.ini @@ -1,59 +1,67 @@ [tox] -envlist = py3{6,7,8,9,10}, black, flake8, mypy, docs, manifest +envlist = py3{6,7,8,9,10,11,12,13}, pypy3{9,10}, black, flake8, mypy, docs, manifest isolated_build = true [gh-actions] python = + 3: py313 3.6: py36 3.7: py37 3.8: py38 3.9: py39 3.10: py310 + 3.11: py311 + 3.12: py312 + 3.13: py313 + pypy3: pypy39 + pypy3.9: pypy39 + pypy3.10: pypy310 [testenv:black] -basepython = python3.9 -deps = black==22.8.0 +basepython = python3.12 +deps = black==24.10.0 commands = - black src tests setup.py -t py39 --check + black src tests setup.py -t py312 --check [testenv:flake8] -basepython = python3.9 -deps = flake8>=5,<6 +basepython = python3.12 +deps = flake8>=7,<8 commands = flake8 src tests setup.py [testenv:mypy] -basepython = python3.9 +basepython = python3.12 deps = - mypy==0.971 - pytest>=6.2,<7 + mypy>=1.15,<2 + pytest>=8.3,<9 commands = mypy src tests [testenv:docs] -basepython = python3.9 +basepython = python3.12 deps = - sphinx>=4.3,<5 - sphinx_rtd_theme>=1,<2 + sphinx>=8,<9 + sphinx_rtd_theme>=3,<4 commands = sphinx-build -b html -nEW docs docs/_build/html [testenv:manifest] -basepython = python3.9 -deps = check-manifest>=0.48,<1 +basepython = python3.12 +deps = check-manifest>=0.50,<1 commands = check-manifest -v [testenv] deps = - py37,py38,py39,py310: pytest>=7.1,<8 - py36: pytest>=6.2,<7 + pytest>=6.2,<9 pytest-asyncio>=0.16,<1 - pytest-benchmark>=3.4,<4 - pytest-cov>=3,<4 + pytest-benchmark>=3.4,<6 + pytest-cov>=4,<7 pytest-describe>=2,<3 pytest-timeout>=2,<3 - py37: typing-extensions>=4.3,<5 - py36: typing-extensions>=4.1,<5 + py3{6,7,8,9},pypy39: typing-extensions>=4.1,<5 commands = - pytest tests {posargs: --cov-report=term-missing --cov=graphql --cov=tests --cov-fail-under=100} + # to also run the time-consuming tests: tox -e py311 -- --run-slow + # to run the benchmarks: tox -e py311 -- -k benchmarks --benchmark-enable + py3{6,7,8,9,10,11,13},pypy3{9,10}: pytest tests {posargs} + py312: pytest tests {posargs: --cov-report=term-missing --cov=graphql --cov=tests --cov-fail-under=100}