GraphQL Engine built with Python 3.6+ / asyncio

Overview

Tartiflette

Tartiflette is a GraphQL Server implementation built with Python 3.6+.

Summary

Motivation

Read this blogpost about our motivations TL; DR We reached the limits of Graphene, we wanted to build something which met certain requirements:

  • Offers a better developer experience that respects the Python mindset
  • Uses SDL (Schema Definition Language)
  • Uses asyncio as the sole execution engine
  • Be 100% open source

Status

The first milestone is behind us, we are now on the road to the milestone 2.

DNA

Discover Tartiflette with our fabulous tutorial on https://tartiflette.io/docs/tutorial/getting-started

Usage

import asyncio

from tartiflette import Resolver, create_engine

@Resolver("Query.hello")
async def resolver_hello(parent, args, ctx, info):
    return "hello " + args["name"]


async def run():
    engine = await create_engine(
        """
        type Query {
            hello(name: String): String
        }
        """
    )

    result = await engine.execute(
        query='query { hello(name: "Chuck") }'
    )

    print(result)
    # {'data': {'hello': 'hello Chuck'}}

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(run())

More details on the API Documentation

Installation

Tartiflette is available on pypi.org.

pip install tartiflette

Installation dependencies

1.2.0+

As Tartiflette based its Executor engine on libgraphqlparser. You'll need these following commands on your environment to use the library. cmake

MacOSX

brew install cmake

Ubuntu

apt-get install cmake

Before 1.2.0

As Tartiflette based its Executor engine on libgraphqlparser. You'll need these following commands on your environment to use the library. cmake, bison and flex.

MacOSX

brew install cmake flex bison

Ubuntu

apt-get install cmake flex bison

Make sure you have bison in version 3

Note to Mac OS users: Make sure bison in your path is really Bison 3, look here for details.

Tartiflette over HTTP

Discover our implementation of tartiflette over HTTP called tartiflette-aiohttp.

Overview

pip install tartiflette-aiohttp
from aiohttp import web
from tartiflette_aiohttp import register_graphql_handlers

sdl = """
    type Query {
        hello(name: String): String
    }
"""

ctx = {
    'user_service': user_service
}

web.run_app(
    register_graphql_handlers(
        app=web.Application(),
        engine_sdl=sdl,
        engine_schema_name="default",
        executor_context=ctx,
        executor_http_endpoint='/graphql',
        executor_http_methods=['POST', 'GET']
    )
)

Roadmaps

How to contribute to the documentation?

As you may know, the documentation is hosted on https://tartiflette.io. This fabulous website is built thanks to another amazing tool, docusaurus.

The content of the documentation is hosted in this repository, to be as close as possible to the code. You will find everything you need/want in the folder /docs.

How to run the website locally?

We built a docker image for the documentation (tartiflette/tartiflette.io on docker hub), which allow us to provide you an easy way to launch the documentation locally, without installing a specific version of node.

prerequisite:

  • Docker
  • Docker Compose
  • Make
make run-docs

Every change you will make in the /docs folder will be automatically hot reloaded. 🎉

Known issues

Badges

Quality gate Total alerts Language grade: Python

Comments
  • ASGI support

    ASGI support

    Request a feature

    Hi, I saw that Tartiflette supports HTTP as a transport layer via tartiflette-aiohttp. That's pretty cool, but I think there may be a better option to enable interoperability with other parts of the Python async web stack: ASGI.

    It's the equivalent of WSGI for async: the de-facto standard interface between async web apps and async web servers (eg uvicorn). AFAIK, Tartiflette doesn't have support for ASGI yet.

    I think providing an ASGI adapter for Tartiflette would foster interest in the project, as it would allow any web framework based on ASGI (i.e. all of them now) to mount a Tartiflette endpoint. The most popular option for now is Graphene, which Starlette provides an ASGI adapter for, but it is synchronous and thus insufficient.

    To be honest, I am considering a potential integration with Bocadillo, an async web framework I am the maintainer of, but for now there doesn't seem to be a way to serve a Tartiflette GraphQL endpoint through ASGI.

    I'm not that familiar with aiohttp, and perhaps it has a feature that would already enable this. If so, I'd be glad to read more!

    P.S.: I'm not sure whether I should have opened this on tartiflette-aiohttp. If necessary I can transfer the issue!

    opened by florimondmanca 19
  • Improve performance for I/O bound list resolvers by using parallel execution

    Improve performance for I/O bound list resolvers by using parallel execution

    At the moment, when an output list contains objects that themselves have an I/O bound resolver (eg. a secondary network RPC call to add more fields to the list items), then the list item resolvers are executed sequentially (see discussion yesterday, and also the original PR that introduced sequential resolution).

    The concern here is that the elapsed wall-clock time to resolve the entire query is unnecessarily slow, because we have to wait for a sequence of network calls, which could potentially be executed in parallel instead. I'd like to start a discussion about how we could restore parallel execution for this scenario, whilst still addressing the concerns that led us to use sequential execution.

    I am not fully aware of the issues. I am guessing they include:

    1. Reduce the overhead of an asyncio task for trivial resolvers
    2. Reduce the overhead of an asyncio task for non-output coercion
    3. Reduce the cost of starting an asyncio task for every member of a lengthy list

    As a conversation starter, here are some ideas for the type of behaviour we might want (either standalone, or potentially combined) to address the I/O bound list resolver scenario outlined above:

    1. Only apply this functionality to the list_coercer for outputs (since they are most likely to involve I/O)
    2. Create asyncio tasks in chunks (eg. maximum 10 at a time)
    3. Only create asyncio tasks if there is at least 1 resolver (I don't think I am expressing this correctly - I mean if there is a @resolver-decorated async python function that needs to be executed in order to get the output value for a requested field)
    4. Provide a hook on the resolver for the field that produces the list, allowing it to specify an alternate list_coercer
    5. Provide a hook on the engine, allowing developers to specify a custom list_coercer
    6. Or something else... ;-)

    Can you please comment? Thanks

    opened by garyd203 15
  • OSError: cannot load library '/var/task/tartiflette/language/parsers/libgraphqlparser/cffi/libgraphqlparser.dylib': /var/task/tartiflette/language/parsers/libgraphqlparser/cffi/libgraphqlparser.dylib: cannot open shared object file: No such file or directory.  Additionally, ctypes.util.find_library() did not manage to locate a library called

    OSError: cannot load library '/var/task/tartiflette/language/parsers/libgraphqlparser/cffi/libgraphqlparser.dylib': /var/task/tartiflette/language/parsers/libgraphqlparser/cffi/libgraphqlparser.dylib: cannot open shared object file: No such file or directory. Additionally, ctypes.util.find_library() did not manage to locate a library called

    I'm trying to load tartiflette in an aws lambda. This is what is happening.

    tartiflette = "^1.3.1" python 3.8

    Click to expand
    [ERROR] OSError: cannot load library '/var/task/tartiflette/language/parsers/libgraphqlparser/cffi/libgraphqlparser.dylib': /var/task/tartiflette/language/parsers/libgraphqlparser/cffi/libgraphqlparser.dylib: cannot open shared object file: No such file or directory.  Additionally, ctypes.util.find_library() did not manage to locate a library called '/var/task/tartiflette/language/parsers/libgraphqlparser/cffi/libgraphqlparser.dylib'
    Traceback (most recent call last):
      File "/var/lang/lib/python3.8/imp.py", line 234, in load_module
        return load_source(name, filename, file)
      File "/var/lang/lib/python3.8/imp.py", line 171, in load_source
        module = _load(spec)
      File "<frozen importlib._bootstrap>", line 702, in _load
      File "<frozen importlib._bootstrap>", line 671, in _load_unlocked
      File "<frozen importlib._bootstrap_external>", line 783, in exec_module
      File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
      File "/var/task/data_request_form_api/lambda_handler.py", line 8, in <module>
        from .service import setup_graphql
      File "/var/task/data_request_form_api/service.py", line 1, in <module>
        from tartiflette import Resolver, create_engine, Engine
      File "/var/task/tartiflette/__init__.py", line 5, in <module>
        from tartiflette.engine import Engine
      File "/var/task/tartiflette/engine.py", line 19, in <module>
        from tartiflette.execution.collect import parse_and_validate_query
      File "/var/task/tartiflette/execution/collect.py", line 11, in <module>
        from tartiflette.language.parsers.libgraphqlparser import parse_to_document
      File "/var/task/tartiflette/language/parsers/libgraphqlparser/__init__.py", line 1, in <module>
        from .parser import parse_to_document
      File "/var/task/tartiflette/language/parsers/libgraphqlparser/parser.py", line 35, in <module>
        _LIB = _FFI.dlopen(f"{_LIBGRAPHQLPARSER_DIR}/libgraphqlparser.dylib")
      File "/var/task/cffi/api.py", line 150, in dlopen
        lib, function_cache = _make_ffi_library(self, name, flags)
      File "/var/task/cffi/api.py", line 832, in _make_ffi_library
        backendlib = _load_backend_lib(backend, libname, flags)
      File "/var/task/cffi/api.py", line 827, in _load_backend_lib
        raise OSError(msg)
    
    opened by Lilja 12
  • Middlewares?

    Middlewares?

    Request a feature

    From the docs and the code I can't seem o find a way of defining middlewares. I wouldn't want to do it with directives as I would have to manually decorate every operation. The only way I can think of is by extending the Resolver decorator and adding the middleware logic there, although that's not a real middlware.

    Are there any plans to add this? Other GraphQL libraries already provide that (graphene, ariadne, etc)

    Thanks

    opened by sebastiandev 12
  • Upgrade Libgraphqlparser to python3 only version

    Upgrade Libgraphqlparser to python3 only version

    closes #301

    With this PR, ctypegen is no more needed for us. Only python 3 is required to generated the .h files (but this is useless for us too, I'm thinking about removing it too). If I have more time later I'll do an "optionnable" thing so it'll be more easily mainlined.

    @florimondmanca I'll ping you with a test.pypi.org/simple lib when it'll be ready so you could test it.

    opened by abusi 11
  • Tartiflette fails to build libgraphqlparser on install

    Tartiflette fails to build libgraphqlparser on install

    • Tartiflette fails to build libgraphqlparser on install.
    • Python version is 3.7.4
    • Not executed in Docker, but here is the trace:
    Command ['/Users/james.smith/Library/Caches/pypoetry/virtualenvs/ttftt-recipies-manager-CjYWEP0q-py3.7/bin/python', '-m', 'pip', 'install', '--no-deps', 'tartiflette==1.1.1'] errored with the following return code 1, and output: 
    Collecting tartiflette==1.1.1
      Using cached https://files.pythonhosted.org/packages/4e/ef/311d95c46dd59c0eebad9db11f8ba74ec010f59df5bd9cab88a7bac09dfb/tartiflette-1.1.1.tar.gz
    Installing collected packages: tartiflette
      Running setup.py install for tartiflette: started
        Running setup.py install for tartiflette: finished with status 'error'
        Complete output from command /Users/james.smith/Library/Caches/pypoetry/virtualenvs/ttftt-recipies-manager-CjYWEP0q-py3.7/bin/python -u -c "import setuptools, tokenize;__file__='/private/var/folders/27/6lbgh3fs1j5856dfvhf024fw0000gp/T/pip-install-2o0hke12/tartiflette/setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" install --record /private/var/folders/27/6lbgh3fs1j5856dfvhf024fw0000gp/T/pip-record-27b2k_v5/install-record.txt --single-version-externally-managed --compile --install-headers /Users/james.smith/Library/Caches/pypoetry/virtualenvs/ttftt-recipies-manager-CjYWEP0q-py3.7/include/site/python3.7/tartiflette:
        running install
        running build
        running build_py
        CMake Error at CMakeLists.txt:27 (FILE):
          FILE COPY given no DESTINATION
        
        
        make: *** No targets specified and no makefile found.  Stop.
        Libgraphqlparser compilation has failed
        
        ----------------------------------------
    Command "/Users/james.smith/Library/Caches/pypoetry/virtualenvs/ttftt-recipies-manager-CjYWEP0q-py3.7/bin/python -u -c "import setuptools, tokenize;__file__='/private/var/folders/27/6lbgh3fs1j5856dfvhf024fw0000gp/T/pip-install-2o0hke12/tartiflette/setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" install --record /private/var/folders/27/6lbgh3fs1j5856dfvhf024fw0000gp/T/pip-record-27b2k_v5/install-record.txt --single-version-externally-managed --compile --install-headers /Users/james.smith/Library/Caches/pypoetry/virtualenvs/ttftt-recipies-manager-CjYWEP0q-py3.7/include/site/python3.7/tartiflette" failed with error code 255 in /private/var/folders/27/6lbgh3fs1j5856dfvhf024fw0000gp/T/pip-install-2o0hke12/tartiflette/
    You are using pip version 19.0.3, however version 19.3.1 is available.
    You should consider upgrading via the 'pip install --upgrade pip' command.
    
    opened by jamessmith-decisionlab 10
  • Add an

    Add an "Edit on GitHub" link on the docs

    Request a feature

    • A part of the project could be improved

    Hi! First, I'm very excited about Tartiflette, especially because it's async-first and quite Pythonic on the outside, so thanks for this project.

    I am following along the recipes manager tutorial, and noticed typos in several places, be it in text copy or in code snippets.

    I read the contributing guidelines and tbh having to fork, clone, and lint everything in order to send a simple typo fix seems kind of overkill / way to high a barrier.

    Hence, I'm wondering if you guys could incorporate a "Edit on GitHub" link to the docs. I believe this would allow more people to help improve the docs. :-)

    question 
    opened by florimondmanca 9
  • Subscribing to a subscription from the client side.

    Subscribing to a subscription from the client side.

    Hi,

    I have implemented subscriptions on the server side as per your getting started tutorial. On the relevant page...

    https://tartiflette.io/docs/tutorial/running-a-subscription

    ...I see that you do not provide the code to subscribe to the subscription using Python, you simply subscribe to it via the GraphiQL client. However, on this page...

    https://tartiflette.io/docs/api/subscription

    ...it appears that you do indeed provide the relevant code. I've implemented this is a separate application, thus:

        async def readSensorValues() -> None:
            engine = await create_engine("./app/sdl")
    
            query = """
            
                subscription {
                  readSensorValues ( pmsId: "1" ) {
                    pmsId
                    dataValue
                  }
                }
            
            """
    
            async for result in engine.subscribe(query=query):
                print(result)
    

    The ./app/sdl directory contains the identical schema file to the one that is being used by the server to implement the subscription.

    Now I need some way to connect this engine to an aiohttp client session somehow? But there is no code example to show how this is done.

    Can you please provide something, assuming it's possible?

    opened by jamessmith-decisionlab 8
  • Embedded libgraphqlparser build fails with latest ctypesgen

    Embedded libgraphqlparser build fails with latest ctypesgen

    Report a bug

    • [X] Explain with a simple sentence the expected behavior The build of the embedded libgraphqlparser fails with the latest ctypesgen module.

    • [X] Tartiflette version: - latest

    • [X] Python version: 3.6, 3.7

    Since ctypesgen 0.1.1 (now 1.0.0) it has installed as "ctypesgen" instead of "ctypesgen.py".

    The CMakeLists.txt for libgraphqlparser only looks for "ctypesgen.py" and doesn't find "ctypesgen".

    I pushed a PR to the tartiflette/libgraphqlparser repo/branch that holds the Py3 version of libgraphqlparser to fix the CMakeLists.txt file to look for ctypesgen if it can't find ctypesgen.py, and I will submit the same change upstream to libgraphqlparser .

    • [X] Executed in docker: No
    • [X] Dockerfile sample: N/A
    • [X] GraphQL Schema & Query: N/A
    • [X] Is it a regression from a previous versions? No
    • [X] Stack trace N/A
    opened by bobh66 8
  • .cook() is not idempotent

    .cook() is not idempotent

    • [x] Explain with a simple sentence the expected behavior: it should be possible to call .cook() multiple times without it failing with hard-to-debug error tracebacks.
    • [x] Tartiflette version: 0.11.2
    • [x] Python version: 3.7.2
    • [x] Executed in docker: No
    • [x] Is it a regression from a previous versions? No (I think the root cause has always been there?)
    • [x] Stack trace: I pushed a failing test in #255
    _______________________________________ ERROR at setup of test_error_handling _______________________________________
    
    ttftt = <tartiflette_starlette.app.TartifletteApp object at 0x105b25668>
    
        @pytest.fixture(name="client")
        def fixture_client(ttftt: TartifletteApp) -> TestClient:
    >       with TestClient(ttftt) as client:
    
    tests/conftest.py:28: 
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    /Users/Florimond/Developer/tartiflette-projects/tartiflette-starlette/venv/lib/python3.7/site-packages/starlette/testclient.py:449: in __enter__
        loop.run_until_complete(self.wait_startup())
    /Users/Florimond/.pyenv/versions/3.7.2/lib/python3.7/asyncio/base_events.py:584: in run_until_complete
        return future.result()
    /Users/Florimond/Developer/tartiflette-projects/tartiflette-starlette/venv/lib/python3.7/site-packages/starlette/testclient.py:467: in wait_startup
        self.task.result()
    /Users/Florimond/Developer/tartiflette-projects/tartiflette-starlette/venv/lib/python3.7/site-packages/starlette/testclient.py:459: in lifespan
        await self.app(scope, self.receive_queue.get, self.send_queue.put)
    tartiflette_starlette/app.py:55: in __call__
        await self.lifespan(scope, receive, send)
    /Users/Florimond/Developer/tartiflette-projects/tartiflette-starlette/venv/lib/python3.7/site-packages/starlette/routing.py:472: in __call__
        await self.startup()
    /Users/Florimond/Developer/tartiflette-projects/tartiflette-starlette/venv/lib/python3.7/site-packages/starlette/routing.py:458: in startup
        await handler()
    tartiflette_starlette/app.py:51: in on_startup
        await self.engine.cook()
    /Users/Florimond/Developer/tartiflette-projects/tartiflette-starlette/venv/lib/python3.7/site-packages/tartiflette/engine.py:157: in cook
        self._modules, modules_sdl = await _import_modules(modules, schema_name)
    /Users/Florimond/Developer/tartiflette-projects/tartiflette-starlette/venv/lib/python3.7/site-packages/tartiflette/engine.py:76: in _import_modules
        module = import_module(module["name"])
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    
    name = <module 'tests.resolvers' from '/Users/florimond/Developer/tartiflette-projects/tartiflette-starlette/tests/resolvers.py'>
    package = None
    
        def import_module(name, package=None):
            """Import a module.
        
            The 'package' argument is required when performing a relative import. It
            specifies the package to use as the anchor point from which to resolve the
            relative import to an absolute import.
        
            """
            level = 0
    >       if name.startswith('.'):
    E       AttributeError: module 'tests.resolvers' has no attribute 'startswith'
    
    /Users/Florimond/.pyenv/versions/3.7.2/lib/python3.7/importlib/__init__.py:118: AttributeError
    

    More details

    While playing with https://github.com/tartiflette/tartiflette-starlette/issues/32 I ran into issues when .cook() was being called multiple times. (I need to to this because we can't create an Engine multiple times in the same session, and so one single Engine is reused across tests.)

    Ultimately, here are the reasons why I think this doesn't work:

    • .cook() tries to import modules, although self._modules might already contain the imported module objects from a previous call. Overall I think this (and potentially other issues) can be fixed by adding a simple ._cooked flag and skipping .cook() if it's True.
    • .cook() always registers the SDL, even though it may have already been added to the SchemaRegistry before, which fails on second time because the same SDL is used, resulting in conflicting types. (I suppose this is why we use a clean_registry in the engine unit tests.) I don't really have an idea on how to fix this. Should I just clean the registry like we do in the tests before calling .cook() in tartiflette-starlette?
    enhancement 
    opened by florimondmanca 8
  • [Extensibility through @directive] hook's specification

    [Extensibility through @directive] hook's specification

    The June2018 GraphQL Specification described an interesting list of locations where directive could be applied on.

    Context

    As explained in the website, tartiflette based its extensibility on the directive mechanism.

    The current version of tartiflette (< 1.0.0) provides some "hooks" through the directives.

    These hooks allow the developers to implement some interesting behavior, e.g:

    • Apply some validation rules on input argument with the event On argument execution
    • Change the behavior of the introspection by hiding some fields with the event On introspection
    • Add access rules on field with the event On field execution

    Objective

    The main objective of this issue is to discuss the next Directive API which will allow us to apply directives on all the locations listed by the GraphQL Specification, both on the ExecutableDirectiveLocation and TypeSystemDirectiveLocation.

    Signature proposal

    TypeSystem Directive

    | Directive Method name (hook) | Schema | Scalar | Object | Field Definition | Argument Definition | Interface | Union | Enum | Enum Value | Input Object | Input Field Definition | | -------------------------------- | ------ | ------ | ------ | ---------------- | ------------------- | --------- | ----- | ---- | ---------- | ------------ | ---------------------- | | on_schema_execution | X | | | | | | | | | | | | on_scalar_argument_execution | | X | | | | | | | | | | | on_scalar_field_execution | | X | | | | | | | | | | | on_object_execution | | | X | | | | | | | | | | on_field_execution | | | | X | | | | | | | | | on_argument_execution | | | | | X | | | | | | X | | on_interface_execution | | | | | | X | | | | | | | on_union_execution | | | | | | | X | | | | | | on_enum_argument_execution | | | | | | | | X | | | | | on_enum_field_execution | | | | | | | | X | | | | | on_enum_value_argument_execution | | | | | | | | | X | | | | on_enum_value_field_execution | | | | | | | | | X | | | | on_input_object_execution | | | | | | | | | | X | | | on_introspection | X | X | X | X | X | X | X | X | X | X | X | | on_build | X | X | X | X | X | X | X | X | X | X | X |

    Executable Directive

    | Directive Method name (hook) | Query | Mutation | Subscription | Field | Fragment Definition | Fragment Spread | Inline Fragment | | ------------------------------ | ----- | -------- | ------------ | ----- | ------------------- | --------------- | --------------- | | on_operation_execution | X | X | X | | | | | | on_field_execution | | | | X | | | | | on_fragment_execution | | | | | X | X | | | on_inline_fragment_execution | | | | | | | X |

    Next

    1. Define the execution's order.
    2. Define the signature of each methods.
    enhancement 
    opened by tsunammis 7
  • chore(deps-dev): bump isort from 5.10.1 to 5.11.4

    chore(deps-dev): bump isort from 5.10.1 to 5.11.4

    Bumps isort from 5.10.1 to 5.11.4.

    Release notes

    Sourced from isort's releases.

    5.11.4

    Changes

    :package: Dependencies

    5.11.3

    Changes

    :beetle: Fixes

    :construction_worker: Continuous Integration

    v5.11.3

    Changes

    :beetle: Fixes

    :construction_worker: Continuous Integration

    5.11.2

    Changes

    5.11.1

    Changes December 12 2022

    ... (truncated)

    Changelog

    Sourced from isort's changelog.

    5.11.4 December 21 2022

    5.11.3 December 16 2022

    5.11.2 December 12 2022

    5.11.1 December 12 2022

    5.11.0 December 12 2022

    Commits
    • 98390f5 Merge pull request #2059 from PyCQA/version/5.11.4
    • df69a05 Bump version 5.11.4
    • f9add58 Merge pull request #2058 from PyCQA/deps/poetry-1.3.1
    • 36caa91 Bump Poetry 1.3.1
    • 3c2e2d0 Merge pull request #1978 from mgorny/toml-test
    • 45d6abd Remove obsolete toml import from the test suite
    • 3020e0b Merge pull request #2057 from mgorny/poetry-install
    • a6fdbfd Stop installing documentation files to top-level site-packages
    • ff306f8 Fix tag template to match old standard
    • 227c4ae Merge pull request #2052 from hugovk/main
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies 
    opened by dependabot[bot] 0
  • chore(deps-dev): bump black from 21.8b0 to 23.1a1

    chore(deps-dev): bump black from 21.8b0 to 23.1a1

    Bumps black from 21.8b0 to 23.1a1.

    Release notes

    Sourced from black's releases.

    23.1a1

    This release provides a preview of Black's 2023 stable style. Black's default formatting style includes the following changes:

    • Enforce empty lines before classes and functions with sticky leading comments (#3302) (22.12.0)
    • Reformat empty and whitespace-only files as either an empty file (if no newline is present) or as a single newline character (if a newline is present) (#3348) (22.12.0)
    • Implicitly concatenated strings used as function args are now wrapped inside parentheses (#3307) (22.12.0)
    • Correctly handle trailing commas that are inside a line's leading non-nested parens (#3370) (22.12.0)
    • --skip-string-normalization / -S now prevents docstring prefixes from being normalized as expected (#3168) (since 22.8.0)
    • When using --skip-magic-trailing-comma or -C, trailing commas are stripped from subscript expressions with more than 1 element (#3209) (22.8.0)
    • Implicitly concatenated strings inside a list, set, or tuple are now wrapped inside parentheses (#3162) (22.8.0)
    • Fix a string merging/split issue when a comment is present in the middle of implicitly concatenated strings on its own line (#3227) (22.8.0)
    • Docstring quotes are no longer moved if it would violate the line length limit (#3044, #3430) (22.6.0)
    • Parentheses around return annotations are now managed (#2990) (22.6.0)
    • Remove unnecessary parentheses around awaited objects (#2991) (22.6.0)
    • Remove unnecessary parentheses in with statements (#2926) (22.6.0)
    • Remove trailing newlines after code block open (#3035) (22.6.0)
    • Code cell separators #%% are now standardised to # %% (#2919) (22.3.0)
    • Remove unnecessary parentheses from except statements (#2939) (22.3.0)
    • Remove unnecessary parentheses from tuple unpacking in for loops (#2945) (22.3.0)
    • Avoid magic-trailing-comma in single-element subscripts (#2942) (22.3.0)

    Please try it out and give feedback here: psf/black#3407

    A stable 23.1.0 release will follow in January 2023.

    22.12.0

    Preview style

    • Enforce empty lines before classes and functions with sticky leading comments (#3302)
    • Reformat empty and whitespace-only files as either an empty file (if no newline is present) or as a single newline character (if a newline is present) (#3348)
    • Implicitly concatenated strings used as function args are now wrapped inside parentheses (#3307)
    • Correctly handle trailing commas that are inside a line's leading non-nested parens (#3370)

    Configuration

    ... (truncated)

    Changelog

    Sourced from black's changelog.

    Change Log

    Unreleased

    Highlights

    Stable style

    • Fix a crash when a colon line is marked between # fmt: off and # fmt: on (#3439)

    Preview style

    • Fix a crash in preview style with assert + parenthesized string (#3415)
    • Fix crashes in preview style with walrus operators used in function return annotations and except clauses (#3423)
    • Do not put the closing quotes in a docstring on a separate line, even if the line is too long (#3430)
    • Long values in dict literals are now wrapped in parentheses; correspondingly unnecessary parentheses around short values in dict literals are now removed; long string lambda values are now wrapped in parentheses (#3440)

    Configuration

    Packaging

    • Upgrade mypyc from 0.971 to 0.991 so mypycified Black can be built on armv7 (#3380)
    • Drop specific support for the tomli requirement on 3.11 alpha releases, working around a bug that would cause the requirement not to be installed on any non-final Python releases (#3448)

    Parser

    Performance

    Output

    ... (truncated)

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies 
    opened by dependabot[bot] 0
  • chore(deps-dev): bump pylint from 2.11.1 to 2.15.9

    chore(deps-dev): bump pylint from 2.11.1 to 2.15.9

    Bumps pylint from 2.11.1 to 2.15.9.

    Commits
    • 1ded4d0 Bump pylint to 2.15.9, update changelog (#7952)
    • 785c629 [testutil] More information in output for functional test fail (#7948)
    • 3c3ab98 [pypy3.8] Disable multiple-statements false positive on affected functional t...
    • dca3940 Fix inconsistent argument exit code when argparse exit with its own error cod...
    • 494e514 Fix ModuleNotFoundError when using pylint_django (#7940) (#7941)
    • 83668de fix: bump dill to >= 0.3.6, prevents tests hanging with python3.11 (#7918)
    • eadc308 [github actions] Fix enchant's install in the spelling job
    • 391323e Avoid hanging forever after a parallel job was killed (#7834) (#7930)
    • 4655b92 Prevent used-before-assignment in pattern matching with a guard (#7922) (#7...
    • 1f84ed9 Bump pylint to 2.15.8, update changelog (#7899)
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies 
    opened by dependabot[bot] 0
  • chore(deps-dev): bump pytest-asyncio from 0.16.0 to 0.20.3

    chore(deps-dev): bump pytest-asyncio from 0.16.0 to 0.20.3

    Bumps pytest-asyncio from 0.16.0 to 0.20.3.

    Release notes

    Sourced from pytest-asyncio's releases.

    pytest-asyncio 0.20.3


    title: 'pytest-asyncio'

    image

    image

    image

    Supported Python versions

    image

    pytest-asyncio is a pytest plugin. It facilitates testing of code that uses the asyncio library.

    Specifically, pytest-asyncio provides support for coroutines as test functions. This allows users to await code inside their tests. For example, the following code is executed as a test item by pytest:

    @pytest.mark.asyncio
    async def test_some_asyncio_code():
        res = await library.do_something()
        assert b"expected result" == res
    

    Note that test classes subclassing the standard unittest library are not supported. Users are advised to use unittest.IsolatedAsyncioTestCase or an async framework such as asynctest.

    pytest-asyncio is available under the Apache License 2.0.

    Installation

    To install pytest-asyncio, simply:

    $ pip install pytest-asyncio
    

    ... (truncated)

    Changelog

    Sourced from pytest-asyncio's changelog.

    0.20.3 (22-12-08)

    • Prevent DeprecationWarning to bubble up on CPython 3.10.9 and 3.11.1. [#460](https://github.com/pytest-dev/pytest-asyncio/issues/460) <https://github.com/pytest-dev/pytest-asyncio/issues/460>_

    0.20.2 (22-11-11)

    • Fixes an issue with async fixtures that are defined as methods on a test class not being rebound to the actual test instance. [#197](https://github.com/pytest-dev/pytest-asyncio/issues/197) <https://github.com/pytest-dev/pytest-asyncio/issues/197>_
    • Replaced usage of deprecated @pytest.mark.tryfirst with @pytest.hookimpl(tryfirst=True) [#438](https://github.com/pytest-dev/pytest-asyncio/issues/438) <https://github.com/pytest-dev/pytest-asyncio/pull/438>_

    0.20.1 (22-10-21)

    • Fixes an issue that warned about using an old version of pytest, even though the most recent version was installed. [#430](https://github.com/pytest-dev/pytest-asyncio/issues/430) <https://github.com/pytest-dev/pytest-asyncio/issues/430>_

    0.20.0 (22-10-21)

    • BREAKING: Removed legacy mode. If you're upgrading from v0.19 and you haven't configured asyncio_mode = legacy, you can upgrade without taking any additional action. If you're upgrading from an earlier version or you have explicitly enabled legacy mode, you need to switch to auto or strict mode before upgrading to this version.
    • Deprecate use of pytest v6.
    • Fixed an issue which prevented fixture setup from being cached. [#404](https://github.com/pytest-dev/pytest-asyncio/issues/404) <https://github.com/pytest-dev/pytest-asyncio/pull/404>_

    0.19.0 (22-07-13)

    • BREAKING: The default asyncio_mode is now strict. [#293](https://github.com/pytest-dev/pytest-asyncio/issues/293) <https://github.com/pytest-dev/pytest-asyncio/issues/293>_
    • Removes setup.py since all relevant configuration is present setup.cfg. Users requiring an editable installation of pytest-asyncio need to use pip v21.1 or newer. [#283](https://github.com/pytest-dev/pytest-asyncio/issues/283) <https://github.com/pytest-dev/pytest-asyncio/issues/283>_
    • Declare support for Python 3.11.

    0.18.3 (22-03-25)

    • Adds pytest-trio <https://pypi.org/project/pytest-trio/>_ to the test dependencies
    • Fixes a bug that caused pytest-asyncio to try to set up async pytest_trio fixtures in strict mode. [#298](https://github.com/pytest-dev/pytest-asyncio/issues/298) <https://github.com/pytest-dev/pytest-asyncio/issues/298>_

    0.18.2 (22-03-03)

    • Fix asyncio auto mode not marking static methods. [#295](https://github.com/pytest-dev/pytest-asyncio/issues/295) <https://github.com/pytest-dev/pytest-asyncio/issues/295>_
    • Fix a compatibility issue with Hypothesis 6.39.0. [#302](https://github.com/pytest-dev/pytest-asyncio/issues/302) <https://github.com/pytest-dev/pytest-asyncio/issues/302>_

    0.18.1 (22-02-10)

    • Fixes a regression that prevented async fixtures from working in synchronous tests. [#286](https://github.com/pytest-dev/pytest-asyncio/issues/286) <https://github.com/pytest-dev/pytest-asyncio/issues/286>_

    0.18.0 (22-02-07)

    • Raise a warning if @​pytest.mark.asyncio is applied to non-async function. [#275](https://github.com/pytest-dev/pytest-asyncio/issues/275) <https://github.com/pytest-dev/pytest-asyncio/issues/275>_
    • Support parametrized event_loop fixture. [#278](https://github.com/pytest-dev/pytest-asyncio/issues/278) <https://github.com/pytest-dev/pytest-asyncio/issues/278>_

    0.17.2 (22-01-17)

    • Require typing-extensions on Python`_

    ... (truncated)

    Commits
    • 007e8ec [fix] Prevent DeprecationWarning about existing event loops to bubble up into...
    • 44ca3da Build(deps): Bump zipp from 3.10.0 to 3.11.0 in /dependencies/default (#455)
    • c3c601c Build(deps): Bump pypa/gh-action-pypi-publish from 1.5.1 to 1.5.2 (#456)
    • a962e2b Build(deps): Bump importlib-metadata in /dependencies/default (#454)
    • 56a393a Simplify README, move most content to a separate user documentation. (#448)
    • 3c78732 Build(deps): Bump hypothesis in /dependencies/default (#453)
    • d6a9a72 Build(deps): Bump exceptiongroup in /dependencies/default (#451)
    • 42da7a0 Build(deps): Bump hypothesis in /dependencies/default (#450)
    • 0b281b1 Build(deps): Bump mypy from 0.990 to 0.991 in /dependencies/default (#446)
    • d39589c Update pre-commit hooks (#449)
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies 
    opened by dependabot[bot] 0
  • chore(deps-dev): bump pytest-xdist from 2.4.0 to 3.1.0

    chore(deps-dev): bump pytest-xdist from 2.4.0 to 3.1.0

    Bumps pytest-xdist from 2.4.0 to 3.1.0.

    Changelog

    Sourced from pytest-xdist's changelog.

    pytest-xdist 3.1.0 (2022-12-01)

    Features

    • [#789](https://github.com/pytest-dev/pytest-xdist/issues/789) <https://github.com/pytest-dev/pytest-xdist/issues/789>_: Users can now set a default distribution mode in their configuration file:

      .. code-block:: ini

      [pytest]
      addopts = --dist loadscope
      
    • [#842](https://github.com/pytest-dev/pytest-xdist/issues/842) <https://github.com/pytest-dev/pytest-xdist/issues/842>_: Python 3.11 is now officially supported.

    Removals

    • [#842](https://github.com/pytest-dev/pytest-xdist/issues/842) <https://github.com/pytest-dev/pytest-xdist/issues/842>_: Python 3.6 is no longer supported.

    pytest-xdist 3.0.2 (2022-10-25)

    Bug Fixes

    • [#813](https://github.com/pytest-dev/pytest-xdist/issues/813) <https://github.com/pytest-dev/pytest-xdist/issues/813>_: Cancel shutdown when a crashed worker is restarted.

    Deprecations

    • [#825](https://github.com/pytest-dev/pytest-xdist/issues/825) <https://github.com/pytest-dev/pytest-xdist/issues/825>_: The --rsyncdir command line argument and rsyncdirs config variable are deprecated.

      The rsync feature will be removed in pytest-xdist 4.0.

    • [#826](https://github.com/pytest-dev/pytest-xdist/issues/826) <https://github.com/pytest-dev/pytest-xdist/issues/826>_: The --looponfail command line argument and looponfailroots config variable are deprecated.

      The loop-on-fail feature will be removed in pytest-xdist 4.0.

    Improved Documentation

    • [#791](https://github.com/pytest-dev/pytest-xdist/issues/791) <https://github.com/pytest-dev/pytest-xdist/issues/791>_: Document the pytest_xdist_auto_num_workers hook.

    • [#796](https://github.com/pytest-dev/pytest-xdist/issues/796) <https://github.com/pytest-dev/pytest-xdist/issues/796>_: Added known limitations section to documentation.

    ... (truncated)

    Commits
    • 92a76bb Release 3.1.0
    • 6226965 Merge pull request #851 from nicoddemus/789-default-dist-mode
    • 7a0bc4c Let users configure dist mode in the configuration file
    • c6bcd20 [pre-commit.ci] pre-commit autoupdate (#849)
    • 99c80c3 Fix typo psutils -> psutil (#848)
    • e14895a [pre-commit.ci] pre-commit autoupdate (#846)
    • bb27210 Merge pull request #844 from pytest-dev/pre-commit-ci-update-config
    • 4a33933 Use ternary operator to remove mypy error
    • 41620d2 [pre-commit.ci] pre-commit autoupdate
    • 6b6f133 Merge pull request #842 from nicoddemus/drop-py36-add-py311
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies 
    opened by dependabot[bot] 0
  • chore(deps-dev): bump pytest from 6.2.5 to 7.2.0

    chore(deps-dev): bump pytest from 6.2.5 to 7.2.0

    Bumps pytest from 6.2.5 to 7.2.0.

    Release notes

    Sourced from pytest's releases.

    7.2.0

    pytest 7.2.0 (2022-10-23)

    Deprecations

    • #10012: Update pytest.PytestUnhandledCoroutineWarning{.interpreted-text role="class"} to a deprecation; it will raise an error in pytest 8.

    • #10396: pytest no longer depends on the py library. pytest provides a vendored copy of py.error and py.path modules but will use the py library if it is installed. If you need other py.* modules, continue to install the deprecated py library separately, otherwise it can usually be removed as a dependency.

    • #4562: Deprecate configuring hook specs/impls using attributes/marks.

      Instead use :pypytest.hookimpl{.interpreted-text role="func"} and :pypytest.hookspec{.interpreted-text role="func"}. For more details, see the docs <legacy-path-hooks-deprecated>{.interpreted-text role="ref"}.

    • #9886: The functionality for running tests written for nose has been officially deprecated.

      This includes:

      • Plain setup and teardown functions and methods: this might catch users by surprise, as setup() and teardown() are not pytest idioms, but part of the nose support.
      • Setup/teardown using the @​with_setup decorator.

      For more details, consult the deprecation docs <nose-deprecation>{.interpreted-text role="ref"}.

    Features

    • #9897: Added shell-style wildcard support to testpaths.

    Improvements

    • #10218: @pytest.mark.parametrize() (and similar functions) now accepts any Sequence[str] for the argument names, instead of just list[str] and tuple[str, ...].

      (Note that str, which is itself a Sequence[str], is still treated as a comma-delimited name list, as before).

    • #10381: The --no-showlocals flag has been added. This can be passed directly to tests to override --showlocals declared through addopts.

    • #3426: Assertion failures with strings in NFC and NFD forms that normalize to the same string now have a dedicated error message detailing the issue, and their utf-8 representation is expresed instead.

    • #7337: A warning is now emitted if a test function returns something other than [None]{.title-ref}. This prevents a common mistake among beginners that expect that returning a [bool]{.title-ref} (for example [return foo(a, b) == result]{.title-ref}) would cause a test to pass or fail, instead of using [assert]{.title-ref}.

    • #8508: Introduce multiline display for warning matching via :pypytest.warns{.interpreted-text role="func"} and enhance match comparison for :py_pytest._code.ExceptionInfo.match{.interpreted-text role="func"} as returned by :pypytest.raises{.interpreted-text role="func"}.

    • #8646: Improve :pypytest.raises{.interpreted-text role="func"}. Previously passing an empty tuple would give a confusing error. We now raise immediately with a more helpful message.

    • #9741: On Python 3.11, use the standard library's tomllib{.interpreted-text role="mod"} to parse TOML.

      tomli{.interpreted-text role="mod"}` is no longer a dependency on Python 3.11.

    • #9742: Display assertion message without escaped newline characters with -vv.

    • #9823: Improved error message that is shown when no collector is found for a given file.

    ... (truncated)

    Commits
    • 3af3f56 Prepare release version 7.2.0
    • bc2c3b6 Merge pull request #10408 from NateMeyvis/patch-2
    • d84ed48 Merge pull request #10409 from pytest-dev/asottile-patch-1
    • ffe49ac Merge pull request #10396 from pytest-dev/pylib-hax
    • d352098 allow jobs to pass if codecov.io fails
    • c5c562b Fix typos in CONTRIBUTING.rst
    • d543a45 add deprecation changelog for py library vendoring
    • f341a5c Merge pull request #10407 from NateMeyvis/patch-1
    • 1027dc8 [pre-commit.ci] auto fixes from pre-commit.com hooks
    • 6b905ee Add note on tags to CONTRIBUTING.rst
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies 
    opened by dependabot[bot] 0
Releases(1.4.1)
  • 1.4.1(Nov 15, 2021)

    [1.4.1] - 2021-11-15

    Added

    • PR-525 - Python 3.10 is now officially supported
    • PR-522 - Add an optional sdl_file_encoding argument to Engine class, to specify the encoding of SDL files
    • PR-526 - Add an optional sdl_file_encoding argument to create_engine method, to specify the encoding of SDL files

    Changed

    Source code(tar.gz)
    Source code(zip)
  • 1.4.0(Aug 2, 2021)

    [1.4.0] 2021-08-02

    Added

    • PR-461 - Add a coerce_parent_concurrently parameter to create_engine, Engine.__init__ & Engine.cook to control whether or not field will be coerced concurrently
    • PR-461 - Add a parent_concurrently parameter to both @Resolver & @Subscription in order to control whether or not the decorated field should be coerced concurrently
    • PR-514 - Provides wheels for macOS (x86_64 only) and Linux (x86_64 only). If your system architecture is one of those, you may not need to install cmake anymore, as the wheels come pre-built with a compiled version of libgraphqlparser

    Changed

    • PR-461 - Rename the concurrently parameter to list_concurrently on both @Resolver & @Subscription
    • PR-508 - Simplify GitHub actions workflows
    • Upgrade lark-parser from 0.11.2 to 0.11.3
    • Upgrade pytest from 6.2.2 to 6.2.4
    • Upgrade pytest-cov from 2.11.1 to 2.12.1
    • Upgrade pytest-asyncio from 0.14.0 to 0.15.1
    • Upgrade pylint from 2.7.2 to 2.9.5
    • Upgrade black from 20.8b1 to 21.7b0
    • Upgrade isort from 5.7.0 to 5.9.2
    • Upgrade pytest-benchmark from 3.2.3 to 3.4.1
    Source code(tar.gz)
    Source code(zip)
  • 1.3.3(Apr 9, 2021)

  • 1.3.2(Mar 30, 2021)

  • 1.3.1(Jan 27, 2021)

    [1.3.1] 2021-01-27

    Added

    • Built-in introspection directive @nonIntrospectable now supports SCHEMA object decoration.
    schema @nonIntrospectable {
        ...
    }
    

    Fixed

    • ISSUE-453 - Exceptions in schema directive are now handled
    Source code(tar.gz)
    Source code(zip)
  • 1.3.0(Dec 8, 2020)

    [1.3.0] - 2020-12-08

    Added

    • Setup a CodeQL analysis Github Action
    • ISSUE-457 - Add a coerce_list_concurrently parameter to create_engine, Engine.__init__ & Engine.cook to control whether or not output list should be coerced concurrently
    • ISSUE-457 - Add a concurrently parameter to both @Resolver & @Subscription in order to control whether or not the output list of the decorated field should be coerced concurrently

    Fixed

    • Fix link issue on 1.2.0 changelog (thanks @mkniewallner)
    • Fix year reference for 1.2.0 changelog (thanks @garyd203)
    • Improve documentation (thanks @dkbarn)
    • Fix README.md typo (thanks @mazzi)
    • Add clarification on the breaking change on output list coercion introduced with the 1.2.0 version (thanks @garyd203)
    • ISSUE-457 - Output list are now coerced concurrently by default (breaking the change made on 1.2.0)

    Changed

    • Upgrade black from 19.10b0 to 20.8b1
    • Upgrade isort from 4.3.21 to 5.6.4
    • Upgrade lark-parser from 0.8.5 to 0.11.1
    • Upgrade pylint from 2.5.2 to 2.6.0
    • Upgrade pytest-asyncio from 0.12.0 to 0.14.0
    • Upgrade pytest-cov from 2.8.1 to 2.10.1
    • Upgrade pytest-xdist from 1.32.0 to 2.1.0
    • Upgrade pytest from 5.4.1 to 6.1.2
    Source code(tar.gz)
    Source code(zip)
  • 1.2.1(May 6, 2020)

    [1.2.1] - 2020-05-06

    Fixed

    • ISSUE-381 - Now pre_output_coercion hooks are called when OBJECT types are retrieved through an UNION or an INTERFACE. Furthermore UNION/INTERFACE hooks are also called before the Object ones are called.

    Changed

    • pytest is now in version 2.5.2
    Source code(tar.gz)
    Source code(zip)
  • 1.2.0(May 2, 2020)

    [1.2.0] - 2020-04-30

    Added

    • ISSUE-363 - Add an optional query_cache_decorator argument at engine initialisation allowing to forward a custom decorator to use to cache query parsing.
    • ISSUE-362 - Add an optional json_loader argument to engine creation APIs so json loader can be customized.
    • ISSUE-361 - Add an optional custom_default_arguments_coercer argument at engine initialisation to override the callable used to coerce arguments.
    • ISSUE-361 - Add an optional arguments_coercer to @Directive, @Subscription & @Resolver decorator to override the callable used to coerce arguments on the decorated directive/field.

    Changed

    • ISSUE-356 - Removed dependencies on flex and bison for installing Tartiflette. cmake is still necessary.
    • ISSUE-361 - Coerce lists (input, literal, output) synchronously to avoid creation of too many asyncio tasks.
    • ISSUE-365 - Forward the InputValueDefinitionNode to the on_argument_execution hook.

      Note: this brings a break changes from previous versions, to upgrade to this version you'll have to update your on_argument_execution methods:

      @Directive("MyDirective")
      class MyDirective:
          async def on_argument_execution(
              self,
              directive_args: Dict[str, Any],
              next_directive: Callable,
              parent_node: Union["FieldNode", "DirectiveNode"],
      +       argument_definition_node: "InputValueDefinitionNode",
      -       argument_node: "ArgumentNode",
      +       argument_node: Optional["ArgumentNode"],
              value: Any,
              ctx: Optional[Any],
          ) -> Any:
              # Write your business logic here
      -       return next_directive(parent_node, argument_node, value, ctx)
      +       return next_directive(parent_node, argument_definition_node, argument_node, value, ctx)
      

    Fixed

    • ISSUE-370 - Fix EnumValue uniqueness in schema definition validation rule. It should now throw the correct error in the correct case.

      enum anEnum {
          A
          A
          B
      }
      

      Will throw a GraphQLSchemaError exception at engine build time. You can't have duplicates values.

      But now:

      type X {
          afield:String
      }
      
      enum anEnum {
          Value1
          X
      }
      

      Doesn't throw a GraphQLSchemaError for the use of X as an EnumValue. This was a buggy schema error detection

    • ISSUE-372 - Fix SDL Validation, Now ObjectFollowInterface validator validate field arguments and allows for field type to be covariant of the interface defined type.

    • Typing on the documentation related to the argument_node argument on the on_argument_execution directive hook.

    Source code(tar.gz)
    Source code(zip)
  • 1.1.3(Feb 19, 2020)

  • 1.1.2(Feb 5, 2020)

    [1.1.2] - 2020-02-05

    Changed

    • Update pytest to 5.3.5
    • Update lark-parser to 0.8.1

    Fixed

    • Typo (Varibable -> Variable) in error messages (Thanks @davestone)
    • Typo (Exists -> Exist) in error messages (Thanks @garyd203)
    • ISSUE-345 - Now support correctly class method for on_subscription_execution.
    Source code(tar.gz)
    Source code(zip)
  • 1.1.1(Oct 10, 2019)

    [1.1.1] - 2019-10-10

    Changed

    Update some Test Framework dependencies:

    • Update pytest to 5.2.1
    • Update pytest-xdist to 1.30.0
    • Update pytest-co to 2.81

    Update dependencies:

    • Update lark-parser to 0.7.7

    Update libgraphqlparser to HEAD of abu/remove_python2 branch.

    Fixed

    • ISSUE-301 - CtypeGen should not bother you anymore.
    Source code(tar.gz)
    Source code(zip)
  • 1.1.0(Oct 7, 2019)

    [1.1.0] - 2019-10-03

    Added

    • ISSUE-134 - Adding two new hooks for directives:
      • on_schema_execution which is called for each Query/Mutation executed
      • on_schema_subscription which is called for each Subscription executed

    See documentation for more details

    Changed

    Fixed

    • Replace and operator for or operator in the Mutation.updateRecipe tutorial resolver condition - ISSUE-298 thrown by @helio-correia
    • Take into account the value filled in into the error_coercer parameter at Engine initialization - ISSUE-309
    Source code(tar.gz)
    Source code(zip)
  • 1.0.0(Sep 20, 2019)

    [1.0.0] - 2019-09-20

    Added

    • ISSUE-122 - Implementation of a new @TypeResolver decorator to allow the resolution of the concrete type of a result linked to an abstract type (union or interface) in a generic way (more detail here)
    • ISSUE-122 - Implementation of a new type_resolver parameter on the @Resolver decorator to allow the resolution of the concrete type of the field result linked to the resolved field abstract type (union or interface) (more detail here)
    • ISSUE-122 - Implementation of a new custom_default_type_resolver parameter on the create_engine function and the __init__ and cook method of the Engine class. This parameter allow to override the default type resolver provided by Tartiflette to resolve the concrete type of abstract type fields (more detail here)
    • A new GraphQLInputField type has been implemented to more clearly distinguish the difference between an argument and an input field and be closer to the GraphQL specification
    • The parsing of the same query and its validation is now cached to improve performance when the same query is played regularly
    • ISSUE-278 - Support for extend syntax in SDL

    Changed

    • Bump the libgraphqlparser submodule to latest master commits so we can now support triple quoted strings as litteral values for parameters. Note: It's important to have at least bison in version >= 3 on your system before installing tartiflette.

    • Bump the lark-parser package to version 0.7.3

    • ISSUE-121 - The grammar used by the lark parser used in the SDL parsing has been updated to conform to the June 2018 GraphQL specification

    • ISSUE-121 - Both SDL and query parsing does now focus only on the parsing part and returns an instance of DocumentNode that can be treated generically

    • Built-in Tartiflette SDL parts (introspection types, directives, scalars...) have been modified to add descriptions on the various built-in types in order to make the introspection queries more complete

    • In order to solve the performance issues related to the execution of introspection requests, we proceeded to a big work of pre-compute of the various fields of introspection at the initialization of the Tartiflette engine in order to answer as quickly as possible to the introspection queries

    • In order to improve performance and thus reduce the execution time of queries, the majority of coercion algorithms are now pre-computed for each type, scalar, directive, etc. at engine initialization

    • The values of the variables are now computed before executing the requested operation (as specified in the GraphQL specification). This allows us to execute the requested operation only if variables coercion doesn't return any errors.

      Note: it is important to note that the computing of values of the variables includes the entire coercion process as well as the execution of potential directives applied on the scalar or input fields of the variable type:

      • directives will only be executed once when calculating the value of each variable and no longer at each occurrence of the variable in the query
      • if a directive returns an error at coercion time of a variable, the whole operation will not be executed, when previously, only the field where the variable is used would have been in error
    • ISSUE-216 - An important work on error handling has been done. Error messages are now more explicit and relevant. In addition, the keys path and locations in error items should now also be more relevant and accurate than before

    • The error_coercer parameter now expects an asynchronous function instead of a synchronous function (see the migration guide for more information)

    • The shape of the info parameter accessible in particular in the resolvers and some hooks of directives has been completely changed (see the migration guide for more information)

    • The @Subscription decorator no longer has its specific default resolver. This means that the messages returned by @Subscription will no longer be wrapped with the field name as before (when the field doesn't have a dedicated @Resolver) (see the migration guide for more information)

    • We now make a strong distinction between non-supplied values and null values. This is to avoid passing the null value when a value has not been supplied (especially for arguments) (see the migration guide for more information)

    • The signature of the following directives hook methods has been changed to on_argument_execution, on_post_input_coercion and on_pre_output_coercion (see the migration guide for more information)

    • Scalar definition now requires the implementation of a third parse_literal method (see the migration guide for more information or documentation on scalar)

    Fixed

    • The type of the __Type.kind field has been fixed for __TypeKind! instead of __TypeKind in the built-in introspection SDL
    • The optional argument includeDeprecated defined on the __Type.enumValues and __Type.fields introspection fields is now functional and allows to filter the result respectively to the deprecations of enum values or fields
    • The directives applied to input fields are now calling the on_post_input_coercion directive hook instead of on_argument_execution
    • The coercion of the arguments is fully functional and the directives applied to the arguments of another directives are now well triggered and computed
    • In some specific and complex cases, the @if and @skip built-in directives could behave unexpectedly and undesirably, their behaviour has been fixed
    • ISSUE-277 - Abstract types are better handled especially the interfaces that are now executed and resolved correctly when using fragment and the __typename field
    • Bakes schema objects after extensions baking - ISSUE-292 thrown by @remorses
    • Code Smells seen by sonarcloud.io
    Source code(tar.gz)
    Source code(zip)
  • 0.12.5(Aug 30, 2019)

  • 0.12.4(Aug 8, 2019)

  • 0.12.3(Jul 23, 2019)

  • 0.12.2(Jul 18, 2019)

  • 0.12.1(Jul 17, 2019)

    [0.12.1] - 2019-07-17

    Changed

    • Update pytest from 5.0.0 to 5.0.1

    Fixed

    • ISSUE-263 - Now variables typed as [any!] are correctly validated

      Note that this is a non complete fix, [[[any!]]!] like type will fail validation (but who uses this ?) it will be completly fixed in 1.0.0 with the completly rewrote parsing/validation/execution algo, stay tuned :p

    Source code(tar.gz)
    Source code(zip)
  • 0.12.0(Jul 3, 2019)

    [0.12.0] - 2019-07-03

    Added

    • SchemaRegistry.clean() class method, it's wipping the registry of every known schema. (thanks @florimondmanca)

    Changed

    • engine.cook() is now a no-op if the engine has already been cooked.
    • Updated dev dependancies
    • Update import using isort.
    • Public classes should be import through from tartiflette import <>

    Beware that you'll may have to change your imports

    Before:

    from tartiflette.directive import Directive
    from tartiflette.resolver import Resolver
    from tartiflette.scalar import Scalar
    from tartiflette.subscription import Subscription
    

    After:

    from tartiflette import (
        Directive, Resolver, Scalar, Subscription
    )
    
    • GrapQLError is marked as deprecated in favor of TartifletteError, importable through from tartiflette import TartifletteError
    • @non_introspectable is removed in favor of @nonIntrospectable as advertised in 0.11.x

    Fixed

    Source code(tar.gz)
    Source code(zip)
  • 0.11.2(Jun 28, 2019)

  • 0.11.1(Jun 18, 2019)

    [0.11.1] - 2019-06-18

    Fixed

    • Documentation: Fix a docstring for create_engine() API.
    • Engine: Don't display the warning when using an Engine() well formated.
    Source code(tar.gz)
    Source code(zip)
  • 0.11.0(Jun 17, 2019)

    [0.11.0] - 2019-06-17

    Added

    • ISSUE-228 - Now you can provide configuration to an imported module through the Engine() creation API. The modules Engine() parameter now accepts dict values like {"name": "python.path.to.your.module", "config": {"a_key": "a_value"}}. So you can mix string values with this new dict value in the modules list to be imported.

    Before :

    from tartiflette import Engine
    
    e = Engine(
        sdl="type aType { aField: String } type Query { anotherField: aType}",
        schema_name="a_schema_name",
        modules=["path.to.a.module", "path.to.another.module"]
    )
    

    After:

    from tartiflette import create_engine
    
    e = await create_engine(
        sdl="type aType { aField: String } type Query { anotherField: aType}",
        schema_name="a_schema_name",
        modules=["path.to.a.module", { "name": "path.to.another.module", "config": {"api_key": "deadc0ffee"}}, "another.module"]
    )
    

    Changed

    • @non_introspectable directive is now renamed to @nonIntrospectable, @non_introspectable is deprecated, will be removed in 0.12.0
    • Engine creation is now done through the create_engine factory, which is async.
    • exclude_builtins_scalars parameters is removed as it is no more necessary. Now the engine only add missing scalar/directives

    All these news features are documentated on the website: https://tartiflette.io

    Source code(tar.gz)
    Source code(zip)
  • 0.10.2(Jun 12, 2019)

  • 0.10.1(Jun 11, 2019)

  • 0.10.0(May 14, 2019)

    [0.10.0] - 2019-05-13

    Added

    • ISSUE-223 Now support directives on Scalar, Object, InputObject, Enum, EnumValue.

    Two new static methods have been added to the OnExecutionDirective class

    @staticmethod
    async def on_pre_output_coercion(directive_args, next_directive, value, field_definition, ctx, info):
        return await next_directive(value, field_definition, ctx, info)
    
    @staticmethod
    async def on_post_input_coercion(directive_args, next_directive, value, argument_definition, ctx, info):
        return await next_directive(value, argument_definition, ctx, info)
    

    Warning: Please test your resolvers/SDL with this lib before going for it. The execution alorigthm changed, it may still have bugs lingering

    Have a look at the updated Tatiflette documentation for more details.

    Changed

    • Introspection directives are now async.

    Warning: This is an API breaking change for your directives. Built-ins have been updated

    Before:

    @staticmethod
    def on_introspection(
        directive_args: Dict[str, Any],
        next_directive: Callable,
        introspected_element: Any,
        ctx: Optional[Dict[str, Any]],
        info: "Info",
    ) -> Any:
        """
        Hook allowing you to alterate the introspection behavior for an
        element.
        :param directive_args: arguments passed to the directive
        :param next_directive: next directive to call
        :param introspected_element: current introspected element
        :param ctx: context passed to the query execution
        :param info: information related to the execution & field resolve
        :return: Any
        """
        # pylint: disable=unused-argument
        return next_directive(introspected_element, ctx, info)
    

    Now:

    @staticmethod
    async def on_introspection(
        directive_args: Dict[str, Any],
        next_directive: Callable,
        introspected_element: Any,
        ctx: Optional[Dict[str, Any]],
        info: "Info",
    ) -> Any:
        """
        Hook allowing you to alterate the introspection behavior for an
        element.
        :param directive_args: arguments passed to the directive
        :param next_directive: next directive to call
        :param introspected_element: current introspected element
        :param ctx: context passed to the query execution
        :param info: information related to the execution & field resolve
        :return: Any
        """
        # pylint: disable=unused-argument
        return await next_directive(introspected_element, ctx, info)
    

    Fixed

    • Built-in deprecated introspection directive weren't calling its next directive properly.
    Source code(tar.gz)
    Source code(zip)
  • 0.9.0(Apr 30, 2019)

    [0.9.0] - 2019-04-30

    Changed

    The error_coercer signature changed from Callable[[Exception]] -> dict to Callable[[Exception], dict] -> dict.

    The main idea of this change is to avoid calling the coerce_value ourselves within our own error_coercer implementation, as this call can be executed by the engine.

    Here are 2 examples of a custom error_coercer, the first one with the old way and the second one with the new way.

    Old way

    from myvender import CustomException
    from tartiflette.engine import Engine
    from tartiflette.resolver.factory import default_error_coercer
    
    def my_custom_error_coercer(exception):
        error = default_error_coercer(exception)
    
        if isinstance(exception, CustomException):
            error["customfield"] = "blabla"
    
        return error
    
    engine = Engine(
        # parameters ...
        error_coercer=my_custom_error_coercer
    )
    

    New way

    from myvender import CustomException
    from tartiflette.engine import Engine
    from tartiflette.resolver.factory import default_error_coercer
    
    def my_custom_error_coercer(exception, error):
        if isinstance(exception, CustomException):
            error["customfield"] = "blabla"
    
        return error
    
    engine = Engine(
        # parameters ...
        error_coercer=my_custom_error_coercer
    )
    
    Source code(tar.gz)
    Source code(zip)
  • 0.8.9(Apr 23, 2019)

  • 0.8.8(Apr 19, 2019)

    [0.8.8] - 2019-04-19

    Added

    • Documentation about Error Handling.

    Changed

    • Exception: the root Exception is now TartifletteError instead of GraphQLError
    • TartifletteError: the TartifletteError exception is available from the tartiflette module.
    • Directive: the @Directive decorator is now available from the tartiflette module instead of tartiflette.directive. e.g: from tartiflette import Directive
    Source code(tar.gz)
    Source code(zip)
  • 0.8.7(Apr 19, 2019)

  • 0.8.6(Apr 17, 2019)

Owner
tartiflette
GraphQL Python Engine built for python 3.6+, by dailymotion
tartiflette
A Python 3.6+ port of the GraphQL.js reference implementation of GraphQL.

GraphQL-core 3 GraphQL-core 3 is a Python 3.6+ port of GraphQL.js, the JavaScript reference implementation for GraphQL, a query language for APIs crea

GraphQL Python 458 Dec 13, 2022
MGE-GraphQL is a Python library for building GraphQL mutations fast and easily

MGE-GraphQL Introduction MGE-GraphQL is a Python library for building GraphQL mutations fast and easily. Data Validations: A similar data validation w

MGE Software 4 Apr 23, 2022
GraphQL security auditing script with a focus on performing batch GraphQL queries and mutations

BatchQL BatchQL is a GraphQL security auditing script with a focus on performing batch GraphQL queries and mutations. This script is not complex, and

Assetnote 267 Dec 24, 2022
A Django GraphQL Starter that uses graphene and graphene_django to interface GraphQL.

Django GraphQL Starter GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data... According to the doc

0101 Solutions 1 Jan 10, 2022
tartiflette-aiohttp is a wrapper of aiohttp which includes the Tartiflette GraphQL Engine, do not hesitate to take a look of the Tartiflette project.

tartiflette-aiohttp is a wrapper of aiohttp which includes the Tartiflette GraphQL Engine. You can take a look at the Tartiflette API documentation. U

tartiflette 60 Nov 8, 2022
ASGI support for the Tartiflette GraphQL engine

tartiflette-asgi is a wrapper that provides ASGI support for the Tartiflette Python GraphQL engine. It is ideal for serving a GraphQL API over HTTP, o

tartiflette 99 Dec 27, 2022
GraphQL is a query language and execution engine tied to any backend service.

GraphQL The GraphQL specification is edited in the markdown files found in /spec the latest release of which is published at https://graphql.github.io

GraphQL 14k Jan 1, 2023
tartiflette-aiohttp is a wrapper of aiohttp which includes the Tartiflette GraphQL Engine, do not hesitate to take a look of the Tartiflette project.

tartiflette-aiohttp is a wrapper of aiohttp which includes the Tartiflette GraphQL Engine. You can take a look at the Tartiflette API documentation. U

tartiflette 60 Nov 8, 2022
ASGI support for the Tartiflette GraphQL engine

tartiflette-asgi is a wrapper that provides ASGI support for the Tartiflette Python GraphQL engine. It is ideal for serving a GraphQL API over HTTP, o

tartiflette 99 Dec 27, 2022
graphw00f is Server Engine Fingerprinting utility for software security professionals looking to learn more about what technology is behind a given GraphQL endpoint.

graphw00f - GraphQL Server Fingerprinting graphw00f (inspired by wafw00f) is the GraphQL fingerprinting tool for GQL endpoints. Table of Contents How

Dolev Farhi 282 Jan 4, 2023
GraphQL framework for Python

Graphene ?? Join the community on Slack We are looking for contributors! Please check the ROADMAP to see how you can help ❤️ The below readme is the d

GraphQL Python 7.5k Jan 1, 2023
GraphQL framework for Python

Graphene ?? Join the community on Slack We are looking for contributors! Please check the ROADMAP to see how you can help ❤️ The below readme is the d

GraphQL Python 7.5k Jan 1, 2023
Ariadne is a Python library for implementing GraphQL servers using schema-first approach.

Ariadne Ariadne is a Python library for implementing GraphQL servers. Schema-first: Ariadne enables Python developers to use schema-first approach to

Mirumee Labs 1.9k Jan 1, 2023
A new GraphQL library for Python 🍓

Strawberry GraphQL Python GraphQL library based on dataclasses Installation ( Quick Start ) The quick start method provides a server and CLI to get go

Strawberry GraphQL 2.8k Jan 1, 2023
A python graphql api, which serves ECB currency rates from last 90 days.

Exchange Rate Api using GraphQL Get Code git pull https://github.com/alaturqua/exchangerate-graphql.git Create .env file with following content and s

Isa 1 Nov 4, 2021
This is a simple Python that will parse instanceStats GraphQL Query into a CSV

GraphQL Python Labs - by Gabs the CSE Table of Contents About The Project Getting Started Prerequisites Installation and Usage Roadmap Contributing Li

Gabriel (Gabs) Cerioni 1 Oct 27, 2021
Pygitstats - a package that allows you to use the GitHub GraphQL API with ease in your Python programs

Pygitstats - a package that allows you to use the GitHub GraphQL API with ease in your Python programs

Dillon Barnes 4 Mar 29, 2022
Graphql-codegen library - a pure python implementation

turms DEVELOPMENT Inspiration Turms is a pure python implementation of the awesome graphql-codegen library, following a simliar extensible design. It

Johannes Roos 22 Dec 23, 2022
Integrate GraphQL into your Django project.

Graphene-Django A Django integration for Graphene. ?? Join the community on Slack Documentation Visit the documentation to get started! Quickstart For

GraphQL Python 4k Dec 31, 2022