├── .alexignore
├── .alexrc
├── .codecov.yml
├── .coveragerc
├── .devcontainer
├── Dockerfile
├── devcontainer.json
└── post-install.sh
├── .dockerignore
├── .editorconfig
├── .github
├── bot-action
│ ├── Dockerfile
│ ├── action.yml
│ └── main.py
├── dependabot.yml
├── logo.png
├── pyproject.toml
├── release-check-action
│ ├── Dockerfile
│ ├── check.py
│ ├── config.py
│ └── release.py
└── workflows
│ ├── codeflash.yaml
│ ├── codeql-analysis.yml
│ ├── federation-compatibility.yml
│ ├── invite-contributors.yml
│ ├── issue-manager.yml
│ ├── ok-to-preview.yml
│ ├── pre-release-pr.yml
│ ├── release-check.yml
│ ├── release.yml
│ ├── slash-commands.yml
│ └── test.yml
├── .gitignore
├── .gitpod.yml
├── .pre-commit-config.yaml
├── .prettierrc
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── docs
├── README.md
├── _test.md
├── breaking-changes.md
├── breaking-changes
│ ├── 0.146.0.md
│ ├── 0.159.0.md
│ ├── 0.169.0.md
│ ├── 0.180.0.md
│ ├── 0.213.0.md
│ ├── 0.217.0.md
│ ├── 0.233.0.md
│ ├── 0.236.0.md
│ ├── 0.240.0.md
│ ├── 0.243.0.md
│ ├── 0.249.0.md
│ ├── 0.251.0.md
│ └── 0.268.0.md
├── codegen
│ ├── query-codegen.md
│ └── schema-codegen.md
├── concepts
│ ├── async.md
│ └── typings.md
├── editors
│ ├── mypy.md
│ ├── pylance.png
│ └── vscode.md
├── errors.md
├── errors
│ ├── _template.md
│ ├── conflicting-arguments.md
│ ├── duplicated-type-name.md
│ ├── invalid-argument-type.md
│ ├── invalid-type-for-union-merge.md
│ ├── invalid-union-type.md
│ ├── missing-arguments-annotations.md
│ ├── missing-field-annotation.md
│ ├── missing-return-annotation.md
│ ├── node-id-annotation.md
│ ├── object-is-not-an-enum.md
│ ├── object-is-not-class.md
│ ├── permission-fail-silently-requires-optional.md
│ ├── private-strawberry-field.md
│ ├── relay-wrong-annotation.md
│ ├── relay-wrong-resolver-annotation.md
│ ├── scalar-already-registered.md
│ ├── unresolved-field-type.md
│ └── unsupported-type.md
├── extensions.md
├── extensions
│ ├── _template.md
│ ├── add-validation-rules.md
│ ├── apollo-tracing.md
│ ├── datadog.md
│ ├── disable-validation.md
│ ├── mask-errors.md
│ ├── max-aliases-limiter.md
│ ├── max-tokens-limiter.md
│ ├── opentelemetry.md
│ ├── parser-cache.md
│ ├── pyinstrument.md
│ ├── query-depth-limiter.md
│ ├── sentry-tracing.md
│ └── validation-cache.md
├── faq.md
├── federation
│ ├── custom_directives.md
│ ├── entities.md
│ ├── entity-interfaces.md
│ └── introduction.md
├── general
│ ├── multipart-subscriptions.md
│ ├── mutations.md
│ ├── queries.md
│ ├── schema-basics.md
│ ├── subscriptions.md
│ ├── upgrades.md
│ └── why.md
├── guides
│ ├── accessing-parent-data.md
│ ├── authentication.md
│ ├── convert-to-dictionary.md
│ ├── custom-extensions.md
│ ├── dataloaders.md
│ ├── errors.md
│ ├── federation-v1.md
│ ├── federation.md
│ ├── field-extensions.md
│ ├── file-upload.md
│ ├── pagination
│ │ ├── connections.md
│ │ ├── cursor-based.md
│ │ ├── offset-based.md
│ │ └── overview.md
│ ├── permissions.md
│ ├── relay.md
│ ├── schema-export.md
│ ├── server.md
│ └── tools.md
├── images
│ ├── index-query-example.png
│ ├── index-server.png
│ └── subscriptions-count-websocket.png
├── index.md
├── integrations
│ ├── aiohttp.md
│ ├── asgi.md
│ ├── chalice.md
│ ├── channels.md
│ ├── creating-an-integration.md
│ ├── django.md
│ ├── fastapi.md
│ ├── flask.md
│ ├── index.md
│ ├── litestar.md
│ ├── pydantic.md
│ ├── quart.md
│ ├── sanic.md
│ └── starlette.md
├── operations
│ ├── deployment.md
│ ├── testing.md
│ └── tracing.md
└── types
│ ├── enums.md
│ ├── exceptions.md
│ ├── generics.md
│ ├── input-types.md
│ ├── interfaces.md
│ ├── lazy.md
│ ├── object-types.md
│ ├── operation-directives.md
│ ├── private.md
│ ├── resolvers.md
│ ├── scalars.md
│ ├── schema-configurations.md
│ ├── schema-directives.md
│ ├── schema.md
│ └── union.md
├── federation-compatibility
├── Dockerfile
├── docker-compose.yml
└── schema.py
├── mypy.ini
├── noxfile.py
├── poetry.lock
├── pyproject.toml
├── setup.py
├── strawberry
├── __init__.py
├── __main__.py
├── aiohttp
│ ├── __init__.py
│ ├── test
│ │ ├── __init__.py
│ │ └── client.py
│ └── views.py
├── annotation.py
├── asgi
│ ├── __init__.py
│ └── test
│ │ ├── __init__.py
│ │ └── client.py
├── chalice
│ ├── __init__.py
│ └── views.py
├── channels
│ ├── __init__.py
│ ├── handlers
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── http_handler.py
│ │ └── ws_handler.py
│ ├── router.py
│ └── testing.py
├── cli
│ ├── __init__.py
│ ├── app.py
│ ├── commands
│ │ ├── __init__.py
│ │ ├── codegen.py
│ │ ├── export_schema.py
│ │ ├── schema_codegen.py
│ │ ├── server.py
│ │ └── upgrade
│ │ │ ├── __init__.py
│ │ │ ├── _fake_progress.py
│ │ │ └── _run_codemod.py
│ ├── constants.py
│ ├── debug_server.py
│ └── utils
│ │ ├── __init__.py
│ │ └── load_schema.py
├── codegen
│ ├── __init__.py
│ ├── exceptions.py
│ ├── plugins
│ │ ├── __init__.py
│ │ ├── print_operation.py
│ │ ├── python.py
│ │ └── typescript.py
│ ├── query_codegen.py
│ └── types.py
├── codemods
│ ├── __init__.py
│ ├── annotated_unions.py
│ └── update_imports.py
├── dataloader.py
├── directive.py
├── django
│ ├── __init__.py
│ ├── apps.py
│ ├── context.py
│ ├── test
│ │ ├── __init__.py
│ │ └── client.py
│ └── views.py
├── exceptions
│ ├── __init__.py
│ ├── conflicting_arguments.py
│ ├── duplicated_type_name.py
│ ├── exception.py
│ ├── exception_source.py
│ ├── handler.py
│ ├── invalid_argument_type.py
│ ├── invalid_union_type.py
│ ├── missing_arguments_annotations.py
│ ├── missing_dependencies.py
│ ├── missing_field_annotation.py
│ ├── missing_return_annotation.py
│ ├── object_is_not_a_class.py
│ ├── object_is_not_an_enum.py
│ ├── permission_fail_silently_requires_optional.py
│ ├── private_strawberry_field.py
│ ├── scalar_already_registered.py
│ ├── syntax.py
│ ├── unresolved_field_type.py
│ └── utils
│ │ ├── __init__.py
│ │ └── source_finder.py
├── experimental
│ ├── __init__.py
│ └── pydantic
│ │ ├── __init__.py
│ │ ├── _compat.py
│ │ ├── conversion.py
│ │ ├── conversion_types.py
│ │ ├── error_type.py
│ │ ├── exceptions.py
│ │ ├── fields.py
│ │ ├── object_type.py
│ │ └── utils.py
├── ext
│ ├── LICENSE
│ ├── __init__.py
│ ├── dataclasses
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── __init__.py
│ │ └── dataclasses.py
│ └── mypy_plugin.py
├── extensions
│ ├── __init__.py
│ ├── add_validation_rules.py
│ ├── base_extension.py
│ ├── context.py
│ ├── directives.py
│ ├── disable_validation.py
│ ├── field_extension.py
│ ├── mask_errors.py
│ ├── max_aliases.py
│ ├── max_tokens.py
│ ├── parser_cache.py
│ ├── pyinstrument.py
│ ├── query_depth_limiter.py
│ ├── runner.py
│ ├── tracing
│ │ ├── __init__.py
│ │ ├── apollo.py
│ │ ├── datadog.py
│ │ ├── opentelemetry.py
│ │ └── utils.py
│ ├── utils.py
│ └── validation_cache.py
├── fastapi
│ ├── __init__.py
│ ├── context.py
│ └── router.py
├── federation
│ ├── __init__.py
│ ├── argument.py
│ ├── enum.py
│ ├── field.py
│ ├── mutation.py
│ ├── object_type.py
│ ├── scalar.py
│ ├── schema.py
│ ├── schema_directive.py
│ ├── schema_directives.py
│ ├── types.py
│ └── union.py
├── field_extensions
│ ├── __init__.py
│ └── input_mutation.py
├── file_uploads
│ ├── __init__.py
│ ├── scalars.py
│ └── utils.py
├── flask
│ ├── __init__.py
│ └── views.py
├── http
│ ├── __init__.py
│ ├── async_base_view.py
│ ├── base.py
│ ├── exceptions.py
│ ├── ides.py
│ ├── parse_content_type.py
│ ├── sync_base_view.py
│ ├── temporal_response.py
│ ├── types.py
│ └── typevars.py
├── litestar
│ ├── __init__.py
│ └── controller.py
├── parent.py
├── permission.py
├── printer
│ ├── __init__.py
│ ├── ast_from_value.py
│ └── printer.py
├── py.typed
├── quart
│ ├── __init__.py
│ └── views.py
├── relay
│ ├── __init__.py
│ ├── exceptions.py
│ ├── fields.py
│ ├── types.py
│ └── utils.py
├── resolvers.py
├── sanic
│ ├── __init__.py
│ ├── context.py
│ ├── utils.py
│ └── views.py
├── scalars.py
├── schema
│ ├── __init__.py
│ ├── base.py
│ ├── compat.py
│ ├── config.py
│ ├── exceptions.py
│ ├── name_converter.py
│ ├── schema.py
│ ├── schema_converter.py
│ ├── types
│ │ ├── __init__.py
│ │ ├── base_scalars.py
│ │ ├── concrete_type.py
│ │ └── scalar.py
│ └── validation_rules
│ │ ├── __init__.py
│ │ └── one_of.py
├── schema_codegen
│ └── __init__.py
├── schema_directive.py
├── schema_directives.py
├── static
│ ├── apollo-sandbox.html
│ ├── graphiql.html
│ └── pathfinder.html
├── subscriptions
│ ├── __init__.py
│ └── protocols
│ │ ├── __init__.py
│ │ ├── graphql_transport_ws
│ │ ├── __init__.py
│ │ ├── handlers.py
│ │ └── types.py
│ │ └── graphql_ws
│ │ ├── __init__.py
│ │ ├── handlers.py
│ │ └── types.py
├── test
│ ├── __init__.py
│ └── client.py
├── tools
│ ├── __init__.py
│ ├── create_type.py
│ └── merge_types.py
├── types
│ ├── __init__.py
│ ├── arguments.py
│ ├── auto.py
│ ├── base.py
│ ├── cast.py
│ ├── enum.py
│ ├── execution.py
│ ├── field.py
│ ├── fields
│ │ ├── __init__.py
│ │ └── resolver.py
│ ├── graphql.py
│ ├── info.py
│ ├── lazy_type.py
│ ├── maybe.py
│ ├── mutation.py
│ ├── nodes.py
│ ├── object_type.py
│ ├── private.py
│ ├── scalar.py
│ ├── type_resolver.py
│ ├── union.py
│ └── unset.py
└── utils
│ ├── __init__.py
│ ├── aio.py
│ ├── await_maybe.py
│ ├── dataclasses.py
│ ├── debug.py
│ ├── deprecations.py
│ ├── graphql_lexer.py
│ ├── importer.py
│ ├── inspect.py
│ ├── logging.py
│ ├── operation.py
│ ├── str_converters.py
│ └── typing.py
└── tests
├── __init__.py
├── a.py
├── asgi
├── __init__.py
└── test_async.py
├── b.py
├── benchmarks
├── __init__.py
├── api.py
├── queries
│ ├── items.graphql
│ ├── many_fields.graphql
│ ├── many_fields_directives.graphql
│ └── simple.graphql
├── schema.py
├── test_complex_schema.py
├── test_execute.py
├── test_execute_sync.py
├── test_execute_with_extensions.py
├── test_generic_input.py
└── test_subscriptions.py
├── c.py
├── channels
├── __init__.py
├── test_layers.py
├── test_router.py
└── test_testing.py
├── cli
├── __init__.py
├── conftest.py
├── fixtures
│ ├── __init__.py
│ └── unions.py
├── snapshots
│ ├── unions.py
│ ├── unions_py38.py
│ └── unions_typing_extension.py
├── test_codegen.py
├── test_export_schema.py
├── test_schema_codegen.py
├── test_server.py
└── test_upgrade.py
├── codegen
├── __init__.py
├── conftest.py
├── lazy_type.py
├── queries
│ ├── alias.graphql
│ ├── basic.graphql
│ ├── custom_scalar.graphql
│ ├── enum.graphql
│ ├── fragment.graphql
│ ├── generic_types.graphql
│ ├── interface.graphql
│ ├── interface_fragments.graphql
│ ├── interface_fragments_with_spread.graphql
│ ├── interface_single_fragment.graphql
│ ├── multiple_types.graphql
│ ├── multiple_types_optional.graphql
│ ├── mutation-fragment.graphql
│ ├── mutation.graphql
│ ├── mutation_with_object.graphql
│ ├── nullable_list_of_non_scalars.graphql
│ ├── optional_and_lists.graphql
│ ├── union.graphql
│ ├── union_return.graphql
│ ├── union_with_one_type.graphql
│ ├── union_with_typename.graphql
│ ├── union_with_typename_and_fragment.graphql
│ ├── variables.graphql
│ └── with_directives.graphql
├── snapshots
│ ├── python
│ │ ├── alias.py
│ │ ├── basic.py
│ │ ├── custom_scalar.py
│ │ ├── enum.py
│ │ ├── fragment.py
│ │ ├── generic_types.py
│ │ ├── interface.py
│ │ ├── interface_fragments.py
│ │ ├── interface_fragments_with_spread.py
│ │ ├── interface_single_fragment.py
│ │ ├── multiple_types.py
│ │ ├── multiple_types_optional.py
│ │ ├── mutation-fragment.py
│ │ ├── mutation.py
│ │ ├── mutation_with_object.py
│ │ ├── nullable_list_of_non_scalars.py
│ │ ├── optional_and_lists.py
│ │ ├── union.py
│ │ ├── union_return.py
│ │ ├── union_with_one_type.py
│ │ ├── union_with_typename.py
│ │ ├── union_with_typename_and_fragment.py
│ │ ├── variables.py
│ │ └── with_directives.py
│ └── typescript
│ │ ├── alias.ts
│ │ ├── basic.ts
│ │ ├── custom_scalar.ts
│ │ ├── enum.ts
│ │ ├── fragment.ts
│ │ ├── generic_types.ts
│ │ ├── interface.ts
│ │ ├── interface_fragments.ts
│ │ ├── interface_fragments_with_spread.ts
│ │ ├── interface_single_fragment.ts
│ │ ├── multiple_types.ts
│ │ ├── multiple_types_optional.ts
│ │ ├── mutation-fragment.ts
│ │ ├── mutation.ts
│ │ ├── mutation_with_object.ts
│ │ ├── nullable_list_of_non_scalars.ts
│ │ ├── optional_and_lists.ts
│ │ ├── union.ts
│ │ ├── union_return.ts
│ │ ├── union_with_one_type.ts
│ │ ├── union_with_typename.ts
│ │ ├── union_with_typename_and_fragment.ts
│ │ ├── variables.ts
│ │ └── with_directives.ts
├── test_print_operation.py
└── test_query_codegen.py
├── codemods
├── __init__.py
├── test_annotated_unions.py
├── test_annotated_unions_pipe.py
└── test_imports.py
├── conftest.py
├── d.py
├── django
├── __init__.py
├── app
│ ├── __init__.py
│ ├── models.py
│ └── urls.py
├── conftest.py
├── django_settings.py
├── test_dataloaders.py
└── test_extensions.py
├── enums
├── __init__.py
└── test_enum.py
├── exceptions
├── __init__.py
├── classes
│ ├── __init__.py
│ └── test_exception_class_missing_optional_dependencies_error.py
├── test_exception_handler.py
├── test_exception_source.py
└── test_threading_exception_handler.py
├── experimental
├── __init__.py
└── pydantic
│ ├── __init__.py
│ ├── schema
│ ├── __init__.py
│ ├── test_1_and_2.py
│ ├── test_basic.py
│ ├── test_computed.py
│ ├── test_defaults.py
│ ├── test_federation.py
│ ├── test_forward_reference.py
│ └── test_mutation.py
│ ├── test_basic.py
│ ├── test_conversion.py
│ ├── test_error_type.py
│ ├── test_fields.py
│ └── utils.py
├── extensions
├── __init__.py
├── test_custom_objects_for_setting_attribute.py
├── test_pyinstrument.py
└── tracing
│ ├── __init__.py
│ └── test_opentelemetry.py
├── fastapi
├── __init__.py
├── app.py
├── test_async.py
├── test_context.py
├── test_openapi.py
└── test_router.py
├── federation
├── __init__.py
├── printer
│ ├── __init__.py
│ ├── test_additional_directives.py
│ ├── test_authenticated.py
│ ├── test_compose_directive.py
│ ├── test_entities.py
│ ├── test_inaccessible.py
│ ├── test_interface.py
│ ├── test_interface_object.py
│ ├── test_keys.py
│ ├── test_link.py
│ ├── test_one_of.py
│ ├── test_override.py
│ ├── test_policy.py
│ ├── test_provides.py
│ ├── test_requires.py
│ ├── test_requires_scopes.py
│ ├── test_shareable.py
│ └── test_tag.py
├── test_entities.py
├── test_schema.py
└── test_types.py
├── fields
├── __init__.py
├── test_arguments.py
├── test_field_basics.py
├── test_field_defaults.py
├── test_field_descriptions.py
├── test_field_exceptions.py
├── test_field_names.py
├── test_permissions.py
└── test_resolvers.py
├── file_uploads
├── __init__.py
└── test_utils.py
├── fixtures
├── __init__.py
└── sample_package
│ ├── __init__.py
│ └── sample_module.py
├── http
├── __init__.py
├── clients
│ ├── __init__.py
│ ├── aiohttp.py
│ ├── asgi.py
│ ├── async_django.py
│ ├── async_flask.py
│ ├── base.py
│ ├── chalice.py
│ ├── channels.py
│ ├── django.py
│ ├── fastapi.py
│ ├── flask.py
│ ├── litestar.py
│ ├── quart.py
│ └── sanic.py
├── conftest.py
├── context.py
├── test_async_base_view.py
├── test_graphql_ide.py
├── test_http.py
├── test_multipart_subscription.py
├── test_mutation.py
├── test_parse_content_type.py
├── test_process_result.py
├── test_query.py
├── test_query_via_get.py
└── test_upload.py
├── litestar
├── __init__.py
├── app.py
├── conftest.py
├── test_context.py
├── test_response_headers.py
└── test_response_status.py
├── objects
├── __init__.py
├── generics
│ ├── __init__.py
│ ├── test_generic_objects.py
│ └── test_names.py
├── test_inheritance.py
├── test_interfaces.py
├── test_object_instantiation.py
└── test_types.py
├── plugins
├── __init__.py
└── strawberry_exceptions.py
├── python_312
├── __init__.py
├── test_generic_objects.py
├── test_generics_schema.py
├── test_inspect.py
└── test_python_generics.py
├── relay
├── __init__.py
├── schema.py
├── schema_future_annotations.py
├── snapshots
│ ├── schema.gql
│ └── schema_future_annotations.gql
├── test_connection.py
├── test_custom_edge.py
├── test_exceptions.py
├── test_fields.py
├── test_id.py
├── test_schema.py
├── test_types.py
└── test_utils.py
├── sanic
├── __init__.py
└── test_file_upload.py
├── schema
├── __init__.py
├── extensions
│ ├── __init__.py
│ ├── schema_extensions
│ │ ├── __init__.py
│ │ ├── conftest.py
│ │ ├── test_extensions.py
│ │ └── test_subscription.py
│ ├── test_apollo.py
│ ├── test_datadog.py
│ ├── test_field_extensions.py
│ ├── test_imports.py
│ ├── test_input_mutation.py
│ ├── test_input_mutation_federation.py
│ ├── test_input_mutation_future.py
│ ├── test_mask_errors.py
│ ├── test_max_aliases.py
│ ├── test_max_tokens.py
│ ├── test_opentelemetry.py
│ ├── test_parser_cache.py
│ ├── test_query_depth_limiter.py
│ └── test_validation_cache.py
├── test_annotated
│ ├── __init__.py
│ ├── type_a.py
│ └── type_b.py
├── test_arguments.py
├── test_basic.py
├── test_camel_casing.py
├── test_config.py
├── test_custom_scalar.py
├── test_dataloaders.py
├── test_directives.py
├── test_duplicated_types.py
├── test_enum.py
├── test_execution.py
├── test_execution_errors.py
├── test_extensions.py
├── test_fields.py
├── test_generics.py
├── test_generics_nested.py
├── test_get_extensions.py
├── test_info.py
├── test_input.py
├── test_interface.py
├── test_lazy
│ ├── __init__.py
│ ├── schema.py
│ ├── test_lazy.py
│ ├── test_lazy_generic.py
│ ├── type_a.py
│ ├── type_b.py
│ ├── type_c.py
│ └── type_d.py
├── test_lazy_types
│ ├── __init__.py
│ ├── test_cyclic.py
│ ├── test_lazy_enums.py
│ ├── type_a.py
│ └── type_b.py
├── test_list.py
├── test_maybe.py
├── test_mutation.py
├── test_name_converter.py
├── test_one_of.py
├── test_permission.py
├── test_private_field.py
├── test_pydantic.py
├── test_resolvers.py
├── test_scalars.py
├── test_schema_generation.py
├── test_schema_hooks.py
├── test_subscription.py
├── test_union.py
├── test_union_deprecated.py
├── test_unresolved_fields.py
└── types
│ ├── __init__.py
│ ├── test_date.py
│ ├── test_datetime.py
│ ├── test_decimal.py
│ ├── test_time.py
│ └── test_uuid.py
├── schema_codegen
├── __init__.py
├── snapshots
│ └── long_descriptions.py
├── test_descriptions.py
├── test_enum.py
├── test_federation.py
├── test_input_types.py
├── test_names.py
├── test_order.py
├── test_root_types_extend.py
├── test_scalar.py
├── test_schema.py
├── test_types.py
└── test_union.py
├── test
├── __init__.py
├── conftest.py
└── test_client.py
├── test_aio.py
├── test_auto.py
├── test_dataloaders.py
├── test_deprecations.py
├── test_forward_references.py
├── test_info.py
├── test_inspect.py
├── test_printer
├── __init__.py
├── test_basic.py
├── test_one_of.py
└── test_schema_directives.py
├── test_repr.py
├── test_type.py
├── tools
├── __init__.py
├── test_create_type.py
└── test_merge_types.py
├── typecheckers
├── __init__.py
├── test_auto.py
├── test_directives.py
├── test_enum.py
├── test_fastapi.py
├── test_federation.py
├── test_federation_fields.py
├── test_federation_params.py
├── test_fields.py
├── test_fields_input.py
├── test_fields_keyword.py
├── test_fields_resolver.py
├── test_fields_resolver_async.py
├── test_info.py
├── test_interface.py
├── test_maybe.py
├── test_params.py
├── test_private.py
├── test_relay.py
├── test_scalars.py
├── test_type.py
├── test_union.py
└── utils
│ ├── __init__.py
│ ├── marks.py
│ ├── mypy.py
│ ├── pyright.py
│ ├── result.py
│ └── typecheck.py
├── types
├── __init__.py
├── cross_module_resolvers
│ ├── README.md
│ ├── __init__.py
│ ├── a_mod.py
│ ├── b_mod.py
│ ├── c_mod.py
│ ├── test_cross_module_resolvers.py
│ └── x_mod.py
├── resolving
│ ├── __init__.py
│ ├── test_enums.py
│ ├── test_forward_references.py
│ ├── test_generics.py
│ ├── test_lists.py
│ ├── test_literals.py
│ ├── test_optionals.py
│ ├── test_string_annotations.py
│ ├── test_union_pipe.py
│ ├── test_unions.py
│ └── test_unions_deprecated.py
├── test_annotation.py
├── test_argument_types.py
├── test_cast.py
├── test_convert_to_dictionary.py
├── test_deferred_annotations.py
├── test_execution.py
├── test_field_types.py
├── test_lazy_types.py
├── test_lazy_types_future_annotations.py
├── test_object_types.py
├── test_parent_type.py
├── test_parent_type_future_annotations.py
└── test_resolver_types.py
├── utils
├── __init__.py
├── test_arguments_converter.py
├── test_get_first_operation.py
├── test_importer.py
├── test_inspect.py
├── test_logging.py
├── test_pretty_print.py
├── test_typing.py
└── test_typing_forward_refs.py
├── views
├── __init__.py
└── schema.py
└── websockets
├── __init__.py
├── conftest.py
├── test_graphql_transport_ws.py
├── test_graphql_ws.py
├── test_websockets.py
└── views.py
/.alexignore:
--------------------------------------------------------------------------------
1 | CHANGELOG.md
2 | TWEET.md
3 |
--------------------------------------------------------------------------------
/.alexrc:
--------------------------------------------------------------------------------
1 | {
2 | "allow": [
3 | "black",
4 | "hook",
5 | "hooks",
6 | "failure",
7 | "period",
8 | "execute",
9 | "executed",
10 | "executes",
11 | "execution",
12 | "reject",
13 | "special",
14 | "primitive",
15 | "invalid",
16 | "failed"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/.codecov.yml:
--------------------------------------------------------------------------------
1 | codecov:
2 | notify:
3 | require_ci_to_pass: yes
4 |
5 | coverage:
6 | precision: 2
7 | round: down
8 | range: "70...100"
9 |
10 | status:
11 | project: yes
12 | patch: yes
13 | changes: no
14 |
15 | comment:
16 | layout: "header, diff"
17 | behavior: default
18 | require_changes: no
19 |
20 | ignore:
21 | - "strawberry/ext/mypy_plugin.py"
22 | - "setup.py"
23 | - "strawberry/experimental/pydantic/conversion_types.py"
24 |
--------------------------------------------------------------------------------
/.coveragerc:
--------------------------------------------------------------------------------
1 | # .coveragerc to control coverage.py
2 | [run]
3 | branch = True
4 |
5 | [report]
6 | # Regexes for lines to exclude from consideration
7 | exclude_lines =
8 | # Have to re-enable the standard pragma
9 | pragma: no cover
10 |
11 | # Don't complain about missing debug-only code:
12 | def __repr__
13 | if self\.debug
14 |
15 | # Don't complain if tests don't hit defensive assertion code:
16 | raise AssertionError
17 | raise NotImplementedError
18 | raise UnsupportedTypeError
19 |
20 | # Don't complain if non-runnable code isn't run:
21 | if 0:
22 | if __name__ == .__main__.:
23 |
24 | # Don't complain about abstract methods, they aren't run:
25 | @(abc\.)?abstractmethod
26 |
27 | # Don't complain about TYPE_CHECKING
28 | if TYPE_CHECKING:
29 |
30 | @overload
31 |
32 | # Those are not supposed to be hit
33 | assert_never\(\w+\)
34 |
35 | ignore_errors = True
36 |
37 | omit =
38 | ./.venv/**
39 | noxfile.py
40 |
41 | [html]
42 | directory = coverage_html_report
43 |
--------------------------------------------------------------------------------
/.devcontainer/Dockerfile:
--------------------------------------------------------------------------------
1 | ARG VARIANT=3.13
2 | FROM mcr.microsoft.com/devcontainers/python:${VARIANT}
3 |
4 | RUN pip3 install poetry pre-commit
5 | RUN poetry config virtualenvs.in-project true
6 |
--------------------------------------------------------------------------------
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Strawberry GraphQL",
3 | "build": {
4 | "dockerfile": "Dockerfile"
5 | },
6 |
7 | "customizations": {
8 | "vscode": {
9 | "settings": {
10 | "terminal.integrated.shell.linux": "/bin/bash",
11 | "python.pythonPath": "/usr/local/bin/python",
12 | "python.linting.enabled": false,
13 | "python.linting.pylintEnabled": false,
14 | "ruff.enable": true,
15 | "ruff.organizeImports": true,
16 | "[python]": {
17 | "editor.formatOnSave": true,
18 | "editor.defaultFormatter": "charliermarsh.ruff",
19 | "editor.codeActionsOnSave": {
20 | "source.organizeImports.ruff": true
21 | }
22 | }
23 | },
24 | "extensions": [
25 | "ms-python.python",
26 | "ms-python.vscode-pylance",
27 | "charliermarsh.ruff",
28 | "eamodio.gitlens"
29 | ]
30 | }
31 | },
32 |
33 | "postCreateCommand": "sh ./.devcontainer/post-install.sh"
34 | }
35 |
--------------------------------------------------------------------------------
/.devcontainer/post-install.sh:
--------------------------------------------------------------------------------
1 | poetry install --with dev,integrations
2 | pre-commit install --install-hooks
3 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | .git
2 | .github
3 | .benchmarks
4 | .devcotainer
5 | .venv
6 | .mypy_cache
7 | .nox
8 | .ruff_cache
9 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 |
7 | [*.py]
8 | indent_size = 4
9 | trim_trailing_whitespace = true
10 | max_line_length = 88
11 | insert_final_newline = true
12 |
13 | [*.yml]
14 | indent_size = 2
15 | insert_final_newline = true
16 |
--------------------------------------------------------------------------------
/.github/bot-action/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.9-alpine
2 |
3 | RUN pip install httpx==0.18.2
4 |
5 |
6 | COPY . /action
7 |
8 | ENTRYPOINT ["python", "/action/main.py"]
9 |
--------------------------------------------------------------------------------
/.github/bot-action/action.yml:
--------------------------------------------------------------------------------
1 | name: 'Strawberry Bot Action'
2 | inputs:
3 | pr_number:
4 | required: true
5 | status:
6 | required: true
7 | change_type:
8 | required: true
9 | changelog_base64:
10 | required: true
11 | tweet:
12 | required: false
13 | release_card_url:
14 | required: false
15 | runs:
16 | using: 'docker'
17 | image: 'Dockerfile'
18 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: pip
4 | directory: "/"
5 | schedule:
6 | interval: daily
7 | open-pull-requests-limit: 10
8 | groups:
9 | all-dependencies:
10 | patterns:
11 | - "*"
12 | allow:
13 | - dependency-type: direct
14 |
--------------------------------------------------------------------------------
/.github/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/.github/logo.png
--------------------------------------------------------------------------------
/.github/pyproject.toml:
--------------------------------------------------------------------------------
1 | [tool.ruff]
2 | extend = "../pyproject.toml"
3 |
4 | [tool.ruff.lint]
5 | extend-ignore = ["T201"]
6 |
--------------------------------------------------------------------------------
/.github/release-check-action/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM docker.io/python:3.8-alpine
2 |
3 | RUN pip install httpx==0.7.*
4 |
5 | COPY . /action
6 |
7 | ENTRYPOINT ["python", "/action/check.py"]
8 |
--------------------------------------------------------------------------------
/.github/release-check-action/check.py:
--------------------------------------------------------------------------------
1 | import base64
2 | import pathlib
3 |
4 | from config import GITHUB_WORKSPACE, RELEASE_FILE_PATH
5 | from release import InvalidReleaseFileError, get_release_info
6 |
7 | release_file = pathlib.Path(GITHUB_WORKSPACE) / RELEASE_FILE_PATH
8 |
9 | release_info = None
10 | status = "MISSING"
11 |
12 | if not release_file.exists():
13 | status = "MISSING"
14 | else:
15 | try:
16 | info = get_release_info(release_file)
17 | release_info = {
18 | "changeType": info.change_type.name,
19 | "changelog": info.changelog,
20 | }
21 |
22 | status = "OK"
23 | except InvalidReleaseFileError:
24 | status = "INVALID"
25 |
26 |
27 | print(f"Status is {status}")
28 | print(f"::set-output name=release_status::{status}")
29 |
30 | if release_info:
31 | changelog = release_info["changelog"]
32 | encoded_changelog = base64.b64encode(changelog.encode("utf-8")).decode("ascii")
33 |
34 | print(f"::set-output name=changelog::{encoded_changelog}")
35 | print(f"::set-output name=change_type::{info.change_type.name}")
36 | else:
37 | print('::set-output name=changelog::""')
38 |
--------------------------------------------------------------------------------
/.github/release-check-action/config.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | RELEASE_FILE_PATH = "RELEASE.md"
4 | GITHUB_SHA = os.environ["GITHUB_SHA"]
5 | GITHUB_EVENT_PATH = os.environ["GITHUB_EVENT_PATH"]
6 | GITHUB_WORKSPACE = os.environ["GITHUB_WORKSPACE"]
7 | API_URL = "https://strawberry-bot-r3o3etjz6a-ew.a.run.app/graphql"
8 |
--------------------------------------------------------------------------------
/.github/release-check-action/release.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import dataclasses
4 | import re
5 | from enum import Enum
6 | from typing import TYPE_CHECKING
7 |
8 | if TYPE_CHECKING:
9 | from pathlib import Path
10 |
11 |
12 | RELEASE_TYPE_REGEX = re.compile(r"^[Rr]elease [Tt]ype: (major|minor|patch)$")
13 |
14 |
15 | class InvalidReleaseFileError(Exception):
16 | pass
17 |
18 |
19 | class ChangeType(Enum):
20 | MAJOR = "major"
21 | MINOR = "minor"
22 | PATCH = "patch"
23 |
24 |
25 | @dataclasses.dataclass
26 | class ReleaseInfo:
27 | change_type: ChangeType
28 | changelog: str
29 |
30 |
31 | # TODO: remove duplication when we migrate our deployer to GitHub Actions
32 | def get_release_info(file_path: Path) -> ReleaseInfo:
33 | with file_path.open("r") as f:
34 | line = f.readline()
35 | match = RELEASE_TYPE_REGEX.match(line)
36 |
37 | if not match:
38 | raise InvalidReleaseFileError
39 |
40 | change_type_key = match.group(1)
41 | change_type = ChangeType[change_type_key.upper()]
42 | changelog = "".join(f.readlines()).strip()
43 |
44 | return ReleaseInfo(change_type, changelog)
45 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | name: 🔐 CodeQL
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | pull_request:
7 | branches: [main]
8 | schedule:
9 | - cron: '0 18 * * 5'
10 |
11 | jobs:
12 | analyze:
13 | runs-on: ubuntu-latest
14 |
15 | permissions:
16 | security-events: write
17 | actions: read
18 | contents: read
19 |
20 | steps:
21 | - name: Checkout repository
22 | uses: actions/checkout@v3
23 |
24 | - name: Initialize CodeQL
25 | uses: github/codeql-action/init@v2
26 | with:
27 | languages: python
28 |
29 | - name: Perform CodeQL Analysis
30 | uses: github/codeql-action/analyze@v2
31 |
--------------------------------------------------------------------------------
/.github/workflows/invite-contributors.yml:
--------------------------------------------------------------------------------
1 | name: 👥 Invite contributors
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | invite-contributor:
10 | name: Invite contributors
11 | runs-on: ubuntu-latest
12 |
13 | steps:
14 | - name: Invite contributors
15 | uses: strawberry-graphql/invite-to-org-action@v4
16 | with:
17 | organisation: "strawberry-graphql"
18 | comment: |
19 | Thanks for contributing to Strawberry! 🎉 You've been invited to join
20 | the Strawberry GraphQL organisation 😊
21 |
22 | You can also request a free sticker by filling this form: https://forms.gle/dmnfQUPoY5gZbVT67
23 |
24 | And don't forget to join our discord server: https://strawberry.rocks/discord 🔥
25 | team-slug: "strawberry-contributors"
26 | github-token: ${{ secrets.BOT_TOKEN }}
27 |
--------------------------------------------------------------------------------
/.github/workflows/issue-manager.yml:
--------------------------------------------------------------------------------
1 | name: Issue Manager
2 |
3 | on:
4 | schedule:
5 | - cron: "0 0 * * *"
6 | issue_comment:
7 | types:
8 | - created
9 | issues:
10 | types:
11 | - labeled
12 | pull_request_target:
13 | types:
14 | - labeled
15 | workflow_dispatch:
16 |
17 | jobs:
18 | issue-manager:
19 | runs-on: ubuntu-latest
20 | steps:
21 | - uses: tiangolo/issue-manager@0.5.0
22 | with:
23 | token: ${{ secrets.GITHUB_TOKEN }}
24 | config: >
25 | {
26 | "info-needed": {
27 | "delay": "P14D",
28 | "message": "Hi, this issue requires extra info to be actionable. We're closing this issue because it has not been actionable for a while now. Feel free to provide the requested information and we'll happily open it again! 😊"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/.github/workflows/slash-commands.yml:
--------------------------------------------------------------------------------
1 | name: 💬 Slash Command Dispatch
2 |
3 | on:
4 | issue_comment:
5 | types: [created]
6 |
7 | jobs:
8 | slashCommandDispatch:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Slash Command Dispatch
12 | uses: peter-evans/slash-command-dispatch@v2
13 | with:
14 | token: ${{ secrets.BOT_TOKEN }}
15 | reaction-token: ${{ secrets.BOT_TOKEN }}
16 | permission: admin
17 | commands: |
18 | pre-release
19 |
--------------------------------------------------------------------------------
/.gitpod.yml:
--------------------------------------------------------------------------------
1 | tasks:
2 | - before: |
3 | curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python -
4 | source $HOME/.poetry/env
5 | pip install pre-commit
6 | init: |
7 | poetry install
8 | pre-commit install --install-hooks
9 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "proseWrap": "always",
3 | "printWidth": 80
4 | }
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Patrick Arminio
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/docs/breaking-changes.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: List of breaking changes and deprecations
3 | ---
4 |
5 | # List of breaking changes and deprecations
6 |
7 | - [Version 0.268.0 - 10 May 2025](./breaking-changes/0.268.0.md)
8 | - [Version 0.249.0 - 18 November 2024](./breaking-changes/0.249.0.md)
9 | - [Version 0.243.0 - 25 September 2024](./breaking-changes/0.243.0.md)
10 | - [Version 0.240.0 - 10 September 2024](./breaking-changes/0.240.0.md)
11 | - [Version 0.236.0 - 17 July 2024](./breaking-changes/0.236.0.md)
12 | - [Version 0.233.0 - 29 May 2024](./breaking-changes/0.233.0.md)
13 | - [Version 0.217.0 - 18 December 2023](./breaking-changes/0.217.0.md)
14 | - [Version 0.213.0 - 8 November 2023](./breaking-changes/0.213.0.md)
15 | - [Version 0.180.0 - 31 May 2023](./breaking-changes/0.180.0.md)
16 | - [Version 0.169.0 - 5 April 2023](./breaking-changes/0.169.0.md)
17 | - [Version 0.159.0 - 22 February 2023](./breaking-changes/0.159.0.md)
18 | - [Version 0.146.0 - 5 December 2022](./breaking-changes/0.146.0.md)
19 |
--------------------------------------------------------------------------------
/docs/breaking-changes/0.169.0.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 0.169.0 Breaking changes
3 | slug: breaking-changes/0.169.0
4 | ---
5 |
6 | # v0.169.0 Introduces a couple of breaking changes in the HTTP integrations
7 |
8 | ## Flask
9 |
10 | Both `get_root_value` and `get_context` now receive the request as a parameter.
11 |
12 | If you're customizing these methods you can change the signature to:
13 |
14 | ```python
15 | def get_root_value(self, request: Request) -> Any: ...
16 |
17 |
18 | def get_context(self, request: Request, response: Response) -> Any: ...
19 | ```
20 |
21 | The same is true for the async version of the view.
22 |
23 | ## Sanic
24 |
25 | The `get_root_value` method now receives the request as a parameter and it is
26 | async.
27 |
28 | If you're customizing this method you can change the signature to:
29 |
30 | ```python
31 | async def get_root_value(self, request: Request) -> Any: ...
32 | ```
33 |
--------------------------------------------------------------------------------
/docs/breaking-changes/0.180.0.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 0.180.0 Breaking changes
3 | slug: breaking-changes/0.180.0
4 | ---
5 |
6 | # v0.180.0 introduces a breaking change for the Django Channels HTTP integration
7 |
8 | The context object is now a `dict`. This means that you should access the
9 | context value using the `["key"]` syntax instead of the `.key` syntax.
10 |
11 | For the HTTP integration, there is also no `ws` key anymore and `request` is a
12 | custom request object containing the full request instead of a
13 | `GraphQLHTTPConsumer` instance. If you need to access the `GraphQLHTTPConsumer`
14 | instance in a HTTP connection, you can access it via
15 | `info.context["request"].consumer`.
16 |
17 | For the WebSockets integration, the context keys did not change, e.g. the values
18 | for `info.context["ws"]`, `info.context["request"]` and
19 | `info.context["connection_params"]` are the same as before.
20 |
21 | If you still want to use the `.key` syntax, you can override `get_context()` to
22 | return a custom dataclass there. See the Channels integration documentation for
23 | an example.
24 |
--------------------------------------------------------------------------------
/docs/breaking-changes/0.213.0.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 0.213.0 Deprecation
3 | slug: breaking-changes/0.213.0
4 | ---
5 |
6 | # v0.213.0 introduces a deprecation for `graphiql` parameter
7 |
8 | All HTTP integration now will use `graphql_ide` instead of `graphiql` parameter.
9 | If you're not using the `graphiql` parameter, you can safely ignore this
10 | deprecation.
11 |
12 | If you're using the `graphiql` parameter, you should change it to `graphql_ide`
13 | instead.
14 |
15 | Here's an example of the changes:
16 |
17 | ```diff
18 | -graphql_app = GraphQLRouter(schema, graphiql=True)
19 | +graphql_app = GraphQLRouter(schema, graphql_ide="graphiql")
20 |
21 | -graphql_app = GraphQLRouter(schema, graphiql=False)
22 | +graphql_app = GraphQLRouter(schema, graphql_ide=None)
23 | ```
24 |
--------------------------------------------------------------------------------
/docs/breaking-changes/0.217.0.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 0.217 Breaking Changes
3 | slug: breaking-changes/0.217.0
4 | ---
5 |
6 | # v0.217.0 changes how kwargs are passed to `has_permission` method
7 |
8 | Previously the `kwargs` argument keys for the `has_permission` method were using
9 | camel casing (depending on your schema configuration), now they will always
10 | follow the python name defined in your resolvers.
11 |
12 | ```python
13 | class IsAuthorized(BasePermission):
14 | message = "User is not authorized"
15 |
16 | def has_permission(
17 | self, source, info, **kwargs: typing.Any
18 | ) -> bool: # pragma: no cover
19 | # kwargs will have a key called "a_key"
20 | # instead of `aKey`
21 |
22 | return False
23 |
24 |
25 | @strawberry.type
26 | class Query:
27 | @strawberry.field(permission_classes=[IsAuthorized])
28 | def name(self, a_key: str) -> str: # pragma: no cover
29 | return "Erik"
30 | ```
31 |
--------------------------------------------------------------------------------
/docs/breaking-changes/0.233.0.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 0.233.0 Breaking Changes
3 | slug: breaking-changes/0.233.0
4 | ---
5 |
6 | # v0.233.0 changes the `info` argument in `resolve_reference` in Federation
7 |
8 | In this release we have updated the `info` object passed to the
9 | `resolve_reference` function in Federation to be a `strawberry.Info` object
10 | instead of the one coming from GraphQL-core.
11 |
12 | If you need to access the original `info` object you can do so by accessing the
13 | `_raw_info` attribute.
14 |
15 | ```python
16 | import strawberry
17 |
18 |
19 | @strawberry.federation.type(keys=["upc"])
20 | class Product:
21 | upc: str
22 |
23 | @classmethod
24 | def resolve_reference(cls, info: strawberry.Info, upc: str) -> "Product":
25 | # Access the original info object
26 | original_info = info._raw_info
27 |
28 | return Product(upc=upc)
29 | ```
30 |
--------------------------------------------------------------------------------
/docs/breaking-changes/0.236.0.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 0.236.0 Breaking Changes
3 | slug: breaking-changes/0.236.0
4 | ---
5 |
6 | # v0.236.0 changes some of the imports
7 |
8 | This release changes the location of some files in the codebase, this is to make
9 | the codebase more organized and easier to navigate.
10 |
11 | Technically most of these changes should not affect you, but if you were
12 | importing some of the files directly you will need to update the imports.
13 |
14 | We created a codemod to help you with that, feel free to try and submit bugs if
15 | we missed something.
16 |
17 | ```bash
18 | strawberry upgrade update-imports
19 | ```
20 |
--------------------------------------------------------------------------------
/docs/breaking-changes/0.240.0.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 0.240.0 Breaking Changes
3 | slug: breaking-changes/0.240.0
4 | ---
5 |
6 | # v0.240.0 updates `Schema.subscribe`'s signature
7 |
8 | In order to support schema extensions in subscriptions and errors that can be
9 | raised before the execution of the subscription, we had to update the signature
10 | of `Schema.subscribe`.
11 |
12 | Previously it was:
13 |
14 | ```python
15 | async def subscribe(
16 | self,
17 | query: str,
18 | variable_values: Optional[Dict[str, Any]] = None,
19 | context_value: Optional[Any] = None,
20 | root_value: Optional[Any] = None,
21 | operation_name: Optional[str] = None,
22 | ) -> Union[AsyncIterator[GraphQLExecutionResult], GraphQLExecutionResult]:
23 | ```
24 |
25 | Now it is:
26 |
27 | ```python
28 | async def subscribe(
29 | self,
30 | query: Optional[str],
31 | variable_values: Optional[Dict[str, Any]] = None,
32 | context_value: Optional[Any] = None,
33 | root_value: Optional[Any] = None,
34 | operation_name: Optional[str] = None,
35 | ) -> Union[AsyncGenerator[ExecutionResult, None], PreExecutionError]:
36 | ```
37 |
--------------------------------------------------------------------------------
/docs/breaking-changes/0.249.0.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 0.249.0 Breaking Changes
3 | slug: breaking-changes/0.249.0
4 | ---
5 |
6 | # v0.249.0 Breaking Changes
7 |
8 | After a year-long deprecation period, the `SentryTracingExtension` has been
9 | removed in favor of the official Sentry SDK integration.
10 |
11 | To migrate, remove the `SentryTracingExtension` from your Strawberry schema and
12 | then follow the
13 | [official Sentry SDK integration guide](https://docs.sentry.io/platforms/python/integrations/strawberry/).
14 |
--------------------------------------------------------------------------------
/docs/breaking-changes/0.251.0.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 0.251.0 Breaking Changes
3 | slug: breaking-changes/0.251.0
4 | ---
5 |
6 | # v0.251.0 Breaking Changes
7 |
8 | We slightly changed the signature of the `encode_json` method used to customize
9 | the JSON encoder used by our HTTP views.
10 |
11 | Originally, the method was only meant to encode HTTP response data. Starting
12 | with this release, it's also used to encode WebSocket messages.
13 |
14 | Previously, the method signature was:
15 |
16 | ```python
17 | def encode_json(self, response_data: GraphQLHTTPResponse) -> str: ...
18 | ```
19 |
20 | To upgrade your code, change the method signature to the following and make sure
21 | your method can handle the same inputs as the built-in `json.dumps` method:
22 |
23 | ```python
24 | def encode_json(self, data: object) -> str: ...
25 | ```
26 |
--------------------------------------------------------------------------------
/docs/codegen/schema-codegen.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Schema codegen
3 | ---
4 |
5 | # Schema codegen
6 |
7 | Strawberry supports code generation from SDL files.
8 |
9 | Let's assume we have the following SDL file:
10 |
11 | ```graphql
12 | type Query {
13 | user: User
14 | }
15 |
16 | type User {
17 | id: ID!
18 | name: String!
19 | }
20 | ```
21 |
22 | by running the following command:
23 |
24 | ```shell
25 | strawberry schema-codegen schema.graphql
26 | ```
27 |
28 | we'll get the following output:
29 |
30 | ```python
31 | import strawberry
32 |
33 |
34 | @strawberry.type
35 | class Query:
36 | user: User | None
37 |
38 |
39 | @strawberry.type
40 | class User:
41 | id: strawberry.ID
42 | name: str
43 |
44 |
45 | schema = strawberry.Schema(query=Query)
46 | ```
47 |
--------------------------------------------------------------------------------
/docs/concepts/async.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Async
3 | ---
4 |
5 | # Async
6 |
7 | Async is a concurrent programming design that has been supported in Python since
8 | version 3.4. To learn more about async in Python refer to
9 | [Real Python’s Async walkthrough](https://realpython.com/async-io-python/).
10 |
11 | Strawberry supports both async and non async resolvers, so you can mix and match
12 | them in your code. Here’s an example of an async resolver:
13 |
14 | ```python
15 | import asyncio
16 | import strawberry
17 |
18 |
19 | async def resolve_hello(root) -> str:
20 | await asyncio.sleep(1)
21 |
22 | return "Hello world"
23 |
24 |
25 | @strawberry.type
26 | class Query:
27 | hello: str = strawberry.field(resolver=resolve_hello)
28 |
29 |
30 | schema = strawberry.Schema(Query)
31 | ```
32 |
--------------------------------------------------------------------------------
/docs/editors/mypy.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Mypy
3 | ---
4 |
5 | # Mypy
6 |
7 | Strawberry comes with support for
8 | [Mypy](https://mypy.readthedocs.io/en/stable/), a popular static type checker
9 | for Python.
10 |
11 | This guide will explain how to configure Mypy to work with Strawberry.
12 |
13 | ## Install Mypy
14 |
15 | The first thing we need to do is to install
16 | [Mypy](https://mypy.readthedocs.io/en/stable/), this is the tool that will
17 | perform the type checking.
18 |
19 | Once the tool is installed, we need to configure it to enable type checking and
20 | use the Strawberry plugin. To do so we need to create a `mypy.ini` file in the
21 | root of our project and add the following settings:
22 |
23 | ```ini
24 | [mypy]
25 | plugins = strawberry.ext.mypy_plugin
26 | ```
27 |
28 | You can also configure Mypy inside the `pyproject.toml` file, like so:
29 |
30 | ```toml
31 | [tool.mypy]
32 | plugins = ["strawberry.ext.mypy_plugin"]
33 | ```
34 |
35 | Once you have configured the settings, you can run `mypy` and you should be
36 | getting type checking errors.
37 |
--------------------------------------------------------------------------------
/docs/editors/pylance.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/docs/editors/pylance.png
--------------------------------------------------------------------------------
/docs/errors/_template.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Some Error
3 | ---
4 |
5 | # Some Error Error
6 |
7 | ## Description
8 |
9 | This error is thrown when ... for example the following code will throw this
10 | error:
11 |
12 | ```python
13 | import strawberry
14 |
15 | schema = strawberry.Schema(query=Query)
16 | ```
17 |
--------------------------------------------------------------------------------
/docs/errors/conflicting-arguments.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Conflicting Arguments Error
3 | ---
4 |
5 | # Conflicting Arguments Error
6 |
7 | ## Description
8 |
9 | This error is thrown when you define a resolver with multiple arguments that
10 | conflict with each other, like "self", "root", or any arguments annotated with
11 | strawberry.Parent.
12 |
13 | For example the following code will throw this error:
14 |
15 | ```python
16 | import strawberry
17 |
18 |
19 | @strawberry.type
20 | class Query:
21 | @strawberry.field
22 | def hello(
23 | self, root, parent: strawberry.Parent[str]
24 | ) -> str: # <-- self, root, and parent all identify the same input
25 | return f"hello world"
26 | ```
27 |
--------------------------------------------------------------------------------
/docs/errors/invalid-argument-type.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Invalid Argument Type Error
3 | ---
4 |
5 | # Invalid Argument Type Error
6 |
7 | ## Description
8 |
9 | This error is thrown when an argument is of the wrong type, it usually happens
10 | when passing **unions** or **interfaces** as an argument, for example the
11 | following code will throw this error:
12 |
13 | ```python
14 | import strawberry
15 |
16 | from typing import Union, Annotated
17 |
18 |
19 | @strawberry.type
20 | class TypeA:
21 | id: strawberry.ID
22 |
23 |
24 | ExampleUnion = Annotated[Union[TypeA], strawberry.union("ExampleUnion")]
25 |
26 |
27 | @strawberry.type
28 | class Query:
29 | @strawberry.field
30 | def example(self, data: Example) -> str:
31 | return "this is an example"
32 |
33 |
34 | schema = strawberry.Schema(query=Query)
35 | ```
36 |
37 | ## Using union types as arguments
38 |
39 | The latest [GraphQL specification](https://spec.graphql.org/October2021/)
40 | doesn't allow using unions as arguments. There's currently an
41 | [RFC for adding a `oneOf` directive](https://github.com/graphql/graphql-spec/pull/825)
42 | that might work for your use case, but it's not yet implemented in the spec and
43 | Strawberry
44 |
--------------------------------------------------------------------------------
/docs/errors/missing-arguments-annotations.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Missing arguments annotation Error
3 | ---
4 |
5 | # Missing arguments annotation Error
6 |
7 | ## Description
8 |
9 | This error is thrown when an argument is missing an annotation, for example the
10 | following code will throw this error:
11 |
12 | ```python
13 | import strawberry
14 |
15 |
16 | @strawberry.type
17 | class Query:
18 | @strawberry.field
19 | def hello(self, name) -> str: # <-- note name here is missing an annotation
20 | return f"hello {name}"
21 |
22 |
23 | schema = strawberry.Schema(query=Query)
24 | ```
25 |
26 | This happens because Strawberry needs to know the type of every argument to be
27 | able to generate the correct GraphQL type.
28 |
29 | ## How to fix this error
30 |
31 | You can fix this error by adding an annotation to the argument, for example, the
32 | following code will fix this error:
33 |
34 | ```python
35 | import strawberry
36 |
37 |
38 | @strawberry.type
39 | class Query:
40 | @strawberry.field
41 | def hello(self, name: str) -> str:
42 | return f"hello {name}"
43 |
44 |
45 | schema = strawberry.Schema(query=Query)
46 | ```
47 |
--------------------------------------------------------------------------------
/docs/errors/missing-field-annotation.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Missing field annotation Error
3 | ---
4 |
5 | # Missing field annotation Error
6 |
7 | ## Description
8 |
9 | This error is thrown when a field on a class is missing an annotation, for
10 | example the following code will throw this error:
11 |
12 | ```python
13 | import strawberry
14 |
15 |
16 | @strawberry.type
17 | class Query:
18 | name: str
19 | age = strawberry.field(
20 | name="ageInYears"
21 | ) # note that here we don't have a type for this field
22 |
23 |
24 | schema = strawberry.Schema(query=Query)
25 | ```
26 |
27 | This happens because Strawberry needs to know the type of every field for a type
28 | to be able to generate the correct GraphQL type.
29 |
30 | ## How to fix this error
31 |
32 | You can fix this error by adding an annotation to the field, for example, the
33 | following code will fix this error:
34 |
35 | ```python
36 | import strawberry
37 |
38 |
39 | @strawberry.type
40 | class Query:
41 | name: str
42 | age: int = strawberry.field(name="ageInYears")
43 |
44 |
45 | schema = strawberry.Schema(query=Query)
46 | ```
47 |
--------------------------------------------------------------------------------
/docs/errors/missing-return-annotation.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Missing return annotation Error
3 | ---
4 |
5 | # Missing return annotation Error
6 |
7 | ## Description
8 |
9 | This error is thrown when a resolver and it's corresponding field don't have a
10 | return annotation, for example the following code will throw this error:
11 |
12 | ```python
13 | import strawberry
14 |
15 |
16 | @strawberry.type
17 | class Query:
18 | @strawberry.field
19 | def example(self):
20 | return "this is an example"
21 |
22 |
23 | schema = strawberry.Schema(query=Query)
24 | ```
25 |
26 | This happens because Strawberry needs to know the return type of the resolver to
27 | be able to generate the correct GraphQL type.
28 |
29 | ## How to fix this error
30 |
31 | You can fix this error by adding a return annotation to the resolver, for
32 | example, the following code will fix this error:
33 |
34 | ```python
35 | import strawberry
36 |
37 |
38 | @strawberry.type
39 | class Query:
40 | @strawberry.field
41 | def example(self) -> str:
42 | return "this is an example"
43 |
44 |
45 | schema = strawberry.Schema(query=Query)
46 | ```
47 |
--------------------------------------------------------------------------------
/docs/errors/object-is-not-an-enum.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Object is not an Enum Error
3 | ---
4 |
5 | # Object is not an Enum Error
6 |
7 | ## Description
8 |
9 | This error is thrown when applying `@strawberry.enum` to a non-enum object, for
10 | example the following code will throw this error:
11 |
12 | ```python
13 | import strawberry
14 |
15 |
16 | # note the lack of @strawberry.enum here:
17 | class NotAnEnum:
18 | A = "A"
19 |
20 |
21 | @strawberry.type
22 | class Query:
23 | field: NotAnEnum
24 |
25 |
26 | schema = strawberry.Schema(query=Query)
27 | ```
28 |
29 | This happens because Strawberry expects all enums to be subclasses of `Enum`.
30 |
31 | ## How to fix this error
32 |
33 | You can fix this error by making sure the class you're applying
34 | `@strawberry.enum` to is a subclass of `Enum`. For example, the following code
35 | will fix this error:
36 |
37 | ```python
38 | import strawberry
39 |
40 |
41 | @strawberry.enum
42 | class NotAnEnum:
43 | A = "A"
44 |
45 |
46 | @strawberry.type
47 | class Query:
48 | field: NotAnEnum
49 |
50 |
51 | schema = strawberry.Schema(query=Query)
52 | ```
53 |
--------------------------------------------------------------------------------
/docs/errors/object-is-not-class.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Object is not an Class Error
3 | ---
4 |
5 | # Object is not an Class Error
6 |
7 | ## Description
8 |
9 | This error is thrown when applying `@strawberry.type/interface/input` to a
10 | non-class object, for example the following code will throw this error:
11 |
12 | ```python
13 | import strawberry
14 |
15 |
16 | @strawberry.type
17 | def a_function(): ...
18 |
19 |
20 | @strawberry.type
21 | class Query:
22 | field: a_function
23 |
24 |
25 | schema = strawberry.Schema(query=Query)
26 | ```
27 |
28 | This happens because Strawberry expects all enums to be subclasses of `Enum`.
29 |
30 | ## How to fix this error
31 |
32 | You can fix this error by making sure the class you're applying
33 | `@strawberry.type/interface/input` to is a class. For example, the following
34 | code will fix this error:
35 |
36 | ```python
37 | import strawberry
38 |
39 |
40 | @strawberry.type
41 | class AFunction:
42 | field: int
43 |
44 |
45 | @strawberry.type
46 | class Query:
47 | field: AFunction
48 |
49 |
50 | schema = strawberry.Schema(query=Query)
51 | ```
52 |
--------------------------------------------------------------------------------
/docs/errors/unsupported-type.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Unsupported Type Error
3 | ---
4 |
5 | # Unsupported Type Error
6 |
7 | ## Description
8 |
9 | This error is thrown when trying to convert arguments with a type that
10 | Strawberry doesn't know about. It shouldn't happen with normal usage of
11 | Strawberry.
12 |
--------------------------------------------------------------------------------
/docs/extensions.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Extensions
3 | ---
4 |
5 | # Extensions
6 |
7 | Extensions allow you, as an application developer, to customise the GraphQL
8 | execution flow based on your needs. Strawberry provides multiple built in
9 | extensions that allow you extend the capability of your GraphQL server.
10 |
11 | If you can't find what you need here you can also build your own custom
12 | extension based on a standard interface. More details
13 | [here](/docs/guides/custom-extensions).
14 |
15 |
16 |
--------------------------------------------------------------------------------
/docs/extensions/_template.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Extension Name
3 | summary: A summary of the extension.
4 | tags: comma,separated,list,of,tags
5 | ---
6 |
7 | # `ExtensionName`
8 |
9 | This extension does ...
10 |
11 | ## Usage example:
12 |
13 | ```python
14 | import strawberry
15 | from strawberry.extensions import ExtensionName
16 |
17 | schema = strawberry.Schema(
18 | Query,
19 | extensions=[
20 | ExtensionName(),
21 | ],
22 | )
23 | ```
24 |
25 | ## API reference:
26 |
27 | ```python
28 | class ExtensionName(an_argument=None): ...
29 | ```
30 |
31 | #### `an_argument: Optional[str] = None`
32 |
33 | Description of the argument.
34 |
35 | ## More examples:
36 |
37 |
38 | Using `an_argument`
39 |
40 | ```python
41 | import strawberry
42 | from strawberry.extensions import ValidationCache
43 |
44 | schema = strawberry.Schema(
45 | Query,
46 | extensions=[
47 | ExtensionName(an_argument="something"),
48 | ],
49 | )
50 | ```
51 |
52 |
53 |
--------------------------------------------------------------------------------
/docs/extensions/apollo-tracing.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Apollo Tracing
3 | summary: Add Apollo tracing to your GraphQL server.
4 | tags: tracing
5 | ---
6 |
7 | # `ApolloTracingExtension`
8 |
9 | This extension adds
10 | [tracing information](https://github.com/apollographql/apollo-tracing) to your
11 | response for [Apollo Engine](https://www.apollographql.com/platform/).
12 |
13 | ## Usage example:
14 |
15 | ```python
16 | import strawberry
17 | from strawberry.extensions.tracing import ApolloTracingExtension
18 |
19 | schema = strawberry.Schema(
20 | Query,
21 | extensions=[
22 | ApolloTracingExtension,
23 | ],
24 | )
25 | ```
26 |
27 |
28 |
29 | If you are not running in an Async context then you'll need to use the sync
30 | version:
31 |
32 | ```python
33 | import strawberry
34 | from strawberry.extensions.tracing import ApolloTracingExtensionSync
35 |
36 | schema = strawberry.Schema(
37 | Query,
38 | extensions=[
39 | ApolloTracingExtensionSync,
40 | ],
41 | )
42 | ```
43 |
44 |
45 |
46 | ## API reference:
47 |
48 | _No arguments_
49 |
--------------------------------------------------------------------------------
/docs/extensions/disable-validation.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Disable Validation
3 | summary: Disable all query validation.
4 | tags: performance,validation
5 | ---
6 |
7 | # `DisableValidation`
8 |
9 | This extensions disables all query validation. This can be useful to improve
10 | performance in some specific cases, for example when dealing with internal APIs
11 | where queries can be trusted.
12 |
13 |
14 |
15 | Only do this if you know what you are doing! Disabling validation breaks the
16 | safety of having typed schema. If you are trying to improve performance you
17 | might want to consider using the [ValidationCache](./validation-cache) instead.
18 |
19 |
20 |
21 | ## Usage example:
22 |
23 | ```python
24 | import strawberry
25 | from strawberry.extensions import DisableValidation
26 |
27 | schema = strawberry.Schema(
28 | Query,
29 | extensions=[
30 | DisableValidation(),
31 | ],
32 | )
33 | ```
34 |
35 | ## API reference:
36 |
37 | _No arguments_
38 |
--------------------------------------------------------------------------------
/docs/extensions/max-aliases-limiter.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Max Aliases Limiter
3 | summary:
4 | Add a validator to limit the maximum number of aliases in a GraphQL document.
5 | tags: security
6 | ---
7 |
8 | # `MaxAliasesLimiter`
9 |
10 | This extension adds a validator to limit the maximum number of aliases in a
11 | GraphQL document.
12 |
13 | ## Usage example:
14 |
15 | ```python
16 | import strawberry
17 | from strawberry.extensions import MaxAliasesLimiter
18 |
19 | schema = strawberry.Schema(
20 | Query,
21 | extensions=[
22 | MaxAliasesLimiter(max_alias_count=15),
23 | ],
24 | )
25 | ```
26 |
27 | ## API reference:
28 |
29 | ```python
30 | class MaxAliasesLimiter(max_alias_count): ...
31 | ```
32 |
33 | #### `max_alias_count: int`
34 |
35 | The maximum allowed number of aliases in a GraphQL document.
36 |
--------------------------------------------------------------------------------
/docs/extensions/max-tokens-limiter.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Max Tokens Limiter
3 | summary:
4 | Add a validator to limit the maximum number of tokens in a GraphQL document.
5 | tags: security
6 | ---
7 |
8 | # `MaxTokensLimiter`
9 |
10 | This extension adds a validator to limit the maximum number of tokens in a
11 | GraphQL document sent to the server.
12 |
13 | ## Usage example:
14 |
15 | ```python
16 | import strawberry
17 | from strawberry.extensions import MaxTokensLimiter
18 |
19 | schema = strawberry.Schema(
20 | Query,
21 | extensions=[
22 | MaxTokensLimiter(max_token_count=1000),
23 | ],
24 | )
25 | ```
26 |
27 | With the above configuration, if a client sends a query with more than 1000
28 | tokens, the server will respond with an error message.
29 |
30 | ## API reference:
31 |
32 | ```python
33 | class MaxTokensLimiter(max_token_count): ...
34 | ```
35 |
36 | #### `max_token_count: int`
37 |
38 | The maximum allowed number of tokens in a GraphQL document.
39 |
40 | The following things are counted as tokens:
41 |
42 | - various brackets: "{", "}", "(", ")"
43 | - colon :
44 | - words
45 |
46 | Not counted:
47 |
48 | - quotes
49 |
--------------------------------------------------------------------------------
/docs/extensions/pyinstrument.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: PyInstrument
3 | summary: Easily Instrument your Schema
4 | tags: instrumentation,profiling
5 | ---
6 |
7 | # `PyInstrument`
8 |
9 | This extension allows you to instrument your schema and inspect the call stack.
10 |
11 | ## Usage example:
12 |
13 | ```python
14 | import strawberry
15 | from strawberry.extensions import pyinstrument
16 |
17 | schema = strawberry.Schema(
18 | Query,
19 | extensions=[
20 | pyinstrument.PyInstrument(report_path="pyinstrument.html"),
21 | ],
22 | )
23 | ```
24 |
25 | ## API reference:
26 |
27 | ```python
28 | class PyInstrument(report_Path=Path("pyinstrument.html")): ...
29 | ```
30 |
31 | #### `report_path: Path = Path("pyinstrument.html")`
32 |
33 | Path to write the HTML PyInstrument report
34 |
--------------------------------------------------------------------------------
/docs/extensions/sentry-tracing.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Sentry Tracing
3 | summary: Add Sentry tracing to your GraphQL server.
4 | tags: tracing
5 | ---
6 |
7 | # `SentryTracingExtension`
8 |
9 |
10 |
11 | As of Sentry 1.32.0, Strawberry is officially supported by the Sentry SDK.
12 | Therefore, Strawberry's `SentryTracingExtension` has been deprecated in version
13 | 0.210.0 and finally removed with Strawberry 0.249.0 in favor of the official
14 | Sentry SDK integration.
15 |
16 | For more details, please refer to the
17 | [documentation for the official Sentry Strawberry integration](https://docs.sentry.io/platforms/python/integrations/strawberry/).
18 |
19 |
20 |
--------------------------------------------------------------------------------
/docs/federation/custom_directives.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Exposing directives on the supergraph (Apollo Federation)
3 | ---
4 |
5 | # Exposing directives on the supergraph (Apollo Federation)
6 |
7 | By default (most)
8 | [schema directives are hidden from the supergraph schema](https://www.apollographql.com/docs/federation/federated-types/federated-directives/#composedirective).
9 | If you need to expose a directive to the supergraph, you can use the `compose`
10 | parameter on the `@strawberry.federation.schema_directives` decorator, here's an
11 | example:
12 |
13 | ```python
14 | import strawberry
15 |
16 |
17 | @strawberry.federation.schema_directive(
18 | locations=[Location.OBJECT], name="cacheControl", compose=True
19 | )
20 | class CacheControl:
21 | max_age: int
22 | ```
23 |
24 | This will create a `cacheControl` directive and it will also use
25 | [`@composeDirective`](https://www.apollographql.com/docs/federation/federated-types/federated-directives/#composedirective)
26 | on the schema to make sure it is included in the supergraph schema.
27 |
--------------------------------------------------------------------------------
/docs/federation/introduction.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Apollo Federation
3 | ---
4 |
5 | # Apollo Federation
6 |
7 | Strawberry supports
8 | [Apollo Federation](https://www.apollographql.com/docs/federation/) out of the
9 | box, that means that you can create services using Strawberry and federate them
10 | via Apollo Gateway or Apollo Router.
11 |
12 | Strawberry is a schema first library, to use Apollo Federation you need to add
13 | directives to your schema, types and fields. Strawberry has built support for
14 | directives, but it also provide shortcuts for Apollo Federation.
15 |
16 | All shortcuts live under the `strawberry.federation` module. For example if you
17 | want to create an
18 | [Entity](https://www.apollographql.com/docs/federation/entities) you can do:
19 |
20 | ```python
21 | @strawberry.federation.type(keys=["id"])
22 | class Book:
23 | id: strawberry.ID
24 | title: str
25 | ```
26 |
27 | And strawberry will automatically add the right directives to the type and
28 | schema.
29 |
30 | # Getting started
31 |
32 | If you want to get started with Apollo Federation, you can use our
33 | [Apollo Federation guide](../guides/federation.md).
34 |
--------------------------------------------------------------------------------
/docs/general/multipart-subscriptions.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Multipart subscriptions
3 | ---
4 |
5 | # Multipart subscriptions
6 |
7 | Strawberry supports subscription over multipart responses. This is an
8 | [alternative protocol](https://www.apollographql.com/docs/router/executing-operations/subscription-multipart-protocol/)
9 | created by [Apollo](https://www.apollographql.com/) to support subscriptions
10 | over HTTP, and it is supported by default by Apollo Client.
11 |
12 | # Support
13 |
14 | We support multipart subscriptions out of the box in the following HTTP
15 | libraries:
16 |
17 | - Django (only in the Async view)
18 | - ASGI
19 | - Litestar
20 | - FastAPI
21 | - AioHTTP
22 | - Quart
23 |
24 | # Usage
25 |
26 | Multipart subscriptions are automatically enabled when using Subscription, so no
27 | additional configuration is required.
28 |
--------------------------------------------------------------------------------
/docs/general/upgrades.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Upgrading Strawberry
3 | ---
4 |
5 | # Upgrading Strawberry
6 |
7 |
8 |
9 | We try to keep Strawberry as backwards compatible as possible, but sometimes we
10 | need to make updates to the public API. While we try to deprecate APIs before
11 | removing them, we also want to make it as easy as possible to upgrade to the
12 | latest version of Strawberry.
13 |
14 | For this reason we provide a CLI command that can automatically upgrade your
15 | codebase to use the updated APIs.
16 |
17 | At the moment we only support updating unions to use the new syntax with
18 | annotated, but in future we plan to add more commands to help with upgrading.
19 |
20 | Here's how you can use the command to upgrade your codebase:
21 |
22 | ```shell
23 | strawberry upgrade annotated-union .
24 | ```
25 |
--------------------------------------------------------------------------------
/docs/guides/convert-to-dictionary.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Convert to Dictionary
3 | ---
4 |
5 | # Convert to Dictionary
6 |
7 | Strawberry provides a utility function to convert a Strawberry object to a
8 | dictionary.
9 |
10 | You can use `strawberry.asdict(...)` function:
11 |
12 | ```python
13 | @strawberry.type
14 | class User:
15 | name: str
16 | age: int
17 |
18 |
19 | # should be {"name": "Lorem", "age": 25}
20 | user_dict = strawberry.asdict(User(name="Lorem", age=25))
21 | ```
22 |
--------------------------------------------------------------------------------
/docs/guides/schema-export.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Schema export
3 | ---
4 |
5 | # Schema export
6 |
7 | Sometimes IDE plugins and code generation tools require you to provide a GraphQL
8 | schema definition.
9 |
10 | Strawberry provides a command to export your schema definition. The exported
11 | schema will be described in the GraphQL schema definition language (SDL).
12 |
13 | To use the command line tools, you have to ensure Strawberry was installed with
14 | `strawberry-graphql[cli]`.
15 |
16 | You can export your schema using the following command:
17 |
18 | ```bash
19 | strawberry export-schema package.module:schema
20 | ```
21 |
22 | where `schema` is the name of a Strawberry schema symbol or a callable symbol
23 | that returns a Strawberry schema and `package.module` is the qualified name of
24 | the module containing the symbol. The symbol name defaults to `schema` if not
25 | specified.
26 |
27 | In order to store the exported schema in a file, pipes or redirection can be
28 | utilized:
29 |
30 | ```bash
31 | strawberry export-schema package.module:schema > schema.graphql
32 | ```
33 |
34 | Alternatively, the `--output` option can be used:
35 |
36 | ```bash
37 | strawberry export-schema package.module:schema --output schema.graphql
38 | ```
39 |
--------------------------------------------------------------------------------
/docs/images/index-query-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/docs/images/index-query-example.png
--------------------------------------------------------------------------------
/docs/images/index-server.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/docs/images/index-server.png
--------------------------------------------------------------------------------
/docs/images/subscriptions-count-websocket.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/docs/images/subscriptions-count-websocket.png
--------------------------------------------------------------------------------
/docs/integrations/starlette.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Starlette
3 | ---
4 |
5 | # Starlette
6 |
7 | Strawberry provides support for [Starlette](https://www.starlette.io/) with the
8 | ASGI integration.
9 |
10 | See below example for integrating Starlette with Strawberry:
11 |
12 | ```python
13 | from starlette.applications import Starlette
14 | from strawberry.asgi import GraphQL
15 |
16 | from api.schema import schema
17 |
18 | graphql_app = GraphQL(schema)
19 |
20 | app = Starlette()
21 | app.add_route("/graphql", graphql_app)
22 | app.add_websocket_route("/graphql", graphql_app)
23 | ```
24 |
25 | For more information about Strawberry ASGI refer to
26 | [the documentation on ASGI](./asgi.md)
27 |
--------------------------------------------------------------------------------
/federation-compatibility/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.10-slim
2 | WORKDIR /web
3 |
4 | RUN apt update && apt install -y gcc python3-dev
5 | RUN pip install poetry
6 |
7 | COPY strawberry ./strawberry
8 | COPY pyproject.toml ./
9 | COPY poetry.lock ./
10 | COPY README.md ./
11 |
12 | RUN poetry install --with integrations
13 |
14 | COPY federation-compatibility/schema.py ./
15 |
16 | EXPOSE 4001
17 |
18 | CMD poetry run strawberry server -p 4001 -h 0.0.0.0 schema:schema
19 |
--------------------------------------------------------------------------------
/federation-compatibility/docker-compose.yml:
--------------------------------------------------------------------------------
1 | services:
2 | products:
3 | build:
4 | context: .
5 | dockerfile: federation-compatibility/Dockerfile
6 | ports:
7 | - 4001:4001
8 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # we use poetry for our build, but this file seems to be required
4 | # in order to get GitHub dependencies graph to work
5 |
6 | import setuptools
7 |
8 | if __name__ == "__main__":
9 | setuptools.setup(name="strawberry-graphql")
10 |
--------------------------------------------------------------------------------
/strawberry/__main__.py:
--------------------------------------------------------------------------------
1 | from .cli import run
2 |
3 | if __name__ == "__main__":
4 | run()
5 |
--------------------------------------------------------------------------------
/strawberry/aiohttp/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/aiohttp/__init__.py
--------------------------------------------------------------------------------
/strawberry/aiohttp/test/__init__.py:
--------------------------------------------------------------------------------
1 | from .client import GraphQLTestClient
2 |
3 | __all__ = ["GraphQLTestClient"]
4 |
--------------------------------------------------------------------------------
/strawberry/asgi/test/__init__.py:
--------------------------------------------------------------------------------
1 | from .client import GraphQLTestClient
2 |
3 | __all__ = ["GraphQLTestClient"]
4 |
--------------------------------------------------------------------------------
/strawberry/chalice/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/chalice/__init__.py
--------------------------------------------------------------------------------
/strawberry/channels/__init__.py:
--------------------------------------------------------------------------------
1 | from .handlers.base import ChannelsConsumer
2 | from .handlers.http_handler import (
3 | ChannelsRequest,
4 | GraphQLHTTPConsumer,
5 | SyncGraphQLHTTPConsumer,
6 | )
7 | from .handlers.ws_handler import GraphQLWSConsumer
8 | from .router import GraphQLProtocolTypeRouter
9 |
10 | __all__ = [
11 | "ChannelsConsumer",
12 | "ChannelsRequest",
13 | "GraphQLHTTPConsumer",
14 | "GraphQLProtocolTypeRouter",
15 | "GraphQLWSConsumer",
16 | "SyncGraphQLHTTPConsumer",
17 | ]
18 |
--------------------------------------------------------------------------------
/strawberry/channels/handlers/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/channels/handlers/__init__.py
--------------------------------------------------------------------------------
/strawberry/cli/__init__.py:
--------------------------------------------------------------------------------
1 | try:
2 | from .app import app
3 | from .commands.codegen import codegen as codegen # noqa: PLC0414
4 | from .commands.export_schema import export_schema as export_schema # noqa: PLC0414
5 | from .commands.schema_codegen import (
6 | schema_codegen as schema_codegen, # noqa: PLC0414
7 | )
8 | from .commands.server import server as server # noqa: PLC0414
9 | from .commands.upgrade import upgrade as upgrade # noqa: PLC0414
10 |
11 | def run() -> None:
12 | app()
13 |
14 | except ModuleNotFoundError as exc:
15 | from strawberry.exceptions import MissingOptionalDependenciesError
16 |
17 | raise MissingOptionalDependenciesError(extras=["cli"]) from exc
18 |
--------------------------------------------------------------------------------
/strawberry/cli/app.py:
--------------------------------------------------------------------------------
1 | import typer
2 |
3 | app = typer.Typer(no_args_is_help=True)
4 |
--------------------------------------------------------------------------------
/strawberry/cli/commands/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/cli/commands/__init__.py
--------------------------------------------------------------------------------
/strawberry/cli/commands/export_schema.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 |
3 | import typer
4 |
5 | from strawberry.cli.app import app
6 | from strawberry.cli.utils import load_schema
7 | from strawberry.printer import print_schema
8 |
9 |
10 | @app.command(help="Exports the schema")
11 | def export_schema(
12 | schema: str,
13 | app_dir: str = typer.Option(
14 | ".",
15 | "--app-dir",
16 | show_default=True,
17 | help=(
18 | "Look for the module in the specified directory, by adding this to the "
19 | "PYTHONPATH. Defaults to the current working directory. "
20 | "Works the same as `--app-dir` in uvicorn."
21 | ),
22 | ),
23 | output: Path = typer.Option(
24 | None,
25 | "--output",
26 | "-o",
27 | help="File to save the exported schema. If not provided, prints to console.",
28 | ),
29 | ) -> None:
30 | schema_symbol = load_schema(schema, app_dir)
31 |
32 | schema_text = print_schema(schema_symbol)
33 |
34 | if output:
35 | Path(output).write_text(schema_text)
36 | typer.echo(f"Schema exported to {output}")
37 | else:
38 | print(schema_text) # noqa: T201
39 |
--------------------------------------------------------------------------------
/strawberry/cli/commands/schema_codegen.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 | from typing import Optional
3 |
4 | import typer
5 |
6 | from strawberry.cli.app import app
7 | from strawberry.schema_codegen import codegen
8 |
9 |
10 | @app.command(help="Generate code from a query")
11 | def schema_codegen(
12 | schema: Path = typer.Argument(exists=True),
13 | output: Optional[Path] = typer.Option(
14 | None,
15 | "-o",
16 | "--output",
17 | file_okay=True,
18 | dir_okay=False,
19 | writable=True,
20 | resolve_path=True,
21 | ),
22 | ) -> None:
23 | generated_output = codegen(schema.read_text())
24 |
25 | if output is None:
26 | typer.echo(generated_output)
27 | return
28 |
29 | output.parent.mkdir(parents=True, exist_ok=True)
30 | output.write_text(generated_output)
31 |
32 | typer.echo(f"Code generated at `{output.name}`")
33 |
--------------------------------------------------------------------------------
/strawberry/cli/commands/upgrade/_fake_progress.py:
--------------------------------------------------------------------------------
1 | from typing import Any
2 |
3 | from rich.progress import TaskID
4 |
5 |
6 | class FakeProgress:
7 | """A fake progress bar that does nothing.
8 |
9 | This is used when the user has only one file to process.
10 | """
11 |
12 | def advance(self, task_id: TaskID) -> None:
13 | pass
14 |
15 | def add_task(self, *args: Any, **kwargs: Any) -> TaskID:
16 | return TaskID(0)
17 |
18 | def __enter__(self) -> "FakeProgress":
19 | return self
20 |
21 | def __exit__(self, *args: object, **kwargs: Any) -> None:
22 | pass
23 |
--------------------------------------------------------------------------------
/strawberry/cli/constants.py:
--------------------------------------------------------------------------------
1 | DEBUG_SERVER_SCHEMA_ENV_VAR_KEY = "STRAWBERRY_DEBUG_SERVER_SCHEMA"
2 | DEBUG_SERVER_LOG_OPERATIONS = "STRAWBERRY_DEBUG_SERVER_LOG_OPERATIONS"
3 |
--------------------------------------------------------------------------------
/strawberry/cli/debug_server.py:
--------------------------------------------------------------------------------
1 | import os
2 | from typing import Any
3 |
4 | from starlette.applications import Starlette
5 | from starlette.middleware.cors import CORSMiddleware
6 |
7 | from strawberry import Schema
8 | from strawberry.asgi import GraphQL
9 | from strawberry.cli.constants import (
10 | DEBUG_SERVER_LOG_OPERATIONS,
11 | DEBUG_SERVER_SCHEMA_ENV_VAR_KEY,
12 | )
13 | from strawberry.utils.importer import import_module_symbol
14 |
15 | app = Starlette(debug=True)
16 | app.add_middleware(
17 | CORSMiddleware, allow_headers=["*"], allow_origins=["*"], allow_methods=["*"]
18 | )
19 |
20 | schema_import_string = os.environ[DEBUG_SERVER_SCHEMA_ENV_VAR_KEY]
21 | schema_symbol = import_module_symbol(schema_import_string, default_symbol_name="schema")
22 | log_operations = os.environ[DEBUG_SERVER_LOG_OPERATIONS] == "True"
23 |
24 | assert isinstance(schema_symbol, Schema)
25 | graphql_app = GraphQL[Any, Any](schema_symbol, debug=log_operations)
26 |
27 | paths = ["/", "/graphql"]
28 | for path in paths:
29 | app.add_route(path, graphql_app) # type: ignore
30 | app.add_websocket_route(path, graphql_app) # type: ignore
31 |
--------------------------------------------------------------------------------
/strawberry/cli/utils/__init__.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 | import rich
4 | import typer
5 |
6 | from strawberry import Schema
7 | from strawberry.utils.importer import import_module_symbol
8 |
9 |
10 | def load_schema(schema: str, app_dir: str) -> Schema:
11 | sys.path.insert(0, app_dir)
12 |
13 | try:
14 | schema_symbol = import_module_symbol(schema, default_symbol_name="schema")
15 | except (ImportError, AttributeError) as exc:
16 | message = str(exc)
17 |
18 | rich.print(f"[red]Error: {message}")
19 | raise typer.Exit(2) # noqa: B904
20 |
21 | if callable(schema_symbol):
22 | try:
23 | schema_symbol = schema_symbol()
24 | except Exception as exc: # noqa: BLE001
25 | message = f"Error invoking schema_symbol: {exc}"
26 | rich.print(f"[red]Error: {message}")
27 | raise typer.Exit(2) # noqa: B904
28 |
29 | if not isinstance(schema_symbol, Schema):
30 | message = "The `schema` must be an instance of strawberry.Schema"
31 | rich.print(f"[red]Error: {message}")
32 | raise typer.Exit(2)
33 |
34 | return schema_symbol
35 |
--------------------------------------------------------------------------------
/strawberry/cli/utils/load_schema.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/cli/utils/load_schema.py
--------------------------------------------------------------------------------
/strawberry/codegen/__init__.py:
--------------------------------------------------------------------------------
1 | from .query_codegen import (
2 | CodegenFile,
3 | CodegenResult,
4 | ConsolePlugin,
5 | QueryCodegen,
6 | QueryCodegenPlugin,
7 | )
8 |
9 | __all__ = [
10 | "CodegenFile",
11 | "CodegenResult",
12 | "ConsolePlugin",
13 | "QueryCodegen",
14 | "QueryCodegenPlugin",
15 | ]
16 |
--------------------------------------------------------------------------------
/strawberry/codegen/exceptions.py:
--------------------------------------------------------------------------------
1 | class CodegenError(Exception):
2 | pass
3 |
4 |
5 | class NoOperationProvidedError(CodegenError):
6 | pass
7 |
8 |
9 | class NoOperationNameProvidedError(CodegenError):
10 | pass
11 |
12 |
13 | class MultipleOperationsProvidedError(CodegenError):
14 | pass
15 |
16 |
17 | __all__ = [
18 | "CodegenError",
19 | "MultipleOperationsProvidedError",
20 | "NoOperationNameProvidedError",
21 | "NoOperationProvidedError",
22 | ]
23 |
--------------------------------------------------------------------------------
/strawberry/codegen/plugins/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/codegen/plugins/__init__.py
--------------------------------------------------------------------------------
/strawberry/codemods/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/codemods/__init__.py
--------------------------------------------------------------------------------
/strawberry/django/__init__.py:
--------------------------------------------------------------------------------
1 | from typing import Any
2 |
3 | try:
4 | # import modules and objects from external strawberry-graphql-django
5 | # package so that it can be used through strawberry.django namespace
6 | from strawberry_django import * # noqa: F403
7 | except ModuleNotFoundError:
8 | import importlib
9 |
10 | def __getattr__(name: str) -> Any:
11 | # try to import submodule and raise exception only if it does not exist
12 | import_symbol = f"{__name__}.{name}"
13 | try:
14 | return importlib.import_module(import_symbol)
15 | except ModuleNotFoundError as e:
16 | raise AttributeError(
17 | f"Attempted import of {import_symbol} failed. Make sure to install the"
18 | "'strawberry-graphql-django' package to use the Strawberry Django "
19 | "extension API."
20 | ) from e
21 |
--------------------------------------------------------------------------------
/strawberry/django/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig # pragma: no cover
2 |
3 |
4 | class StrawberryConfig(AppConfig): # pragma: no cover
5 | name = "strawberry"
6 |
--------------------------------------------------------------------------------
/strawberry/django/context.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from dataclasses import dataclass
4 | from typing import TYPE_CHECKING, Any
5 |
6 | if TYPE_CHECKING:
7 | from django.http import HttpRequest, HttpResponse
8 |
9 |
10 | @dataclass
11 | class StrawberryDjangoContext:
12 | request: HttpRequest
13 | response: HttpResponse
14 |
15 | def __getitem__(self, key: str) -> Any:
16 | # __getitem__ override needed to avoid issues for who's
17 | # using info.context["request"]
18 | return super().__getattribute__(key)
19 |
20 | def get(self, key: str) -> Any:
21 | """Enable .get notation for accessing the request."""
22 | return super().__getattribute__(key)
23 |
24 |
25 | __all__ = ["StrawberryDjangoContext"]
26 |
--------------------------------------------------------------------------------
/strawberry/django/test/__init__.py:
--------------------------------------------------------------------------------
1 | from .client import GraphQLTestClient
2 |
3 | __all__ = ["GraphQLTestClient"]
4 |
--------------------------------------------------------------------------------
/strawberry/django/test/client.py:
--------------------------------------------------------------------------------
1 | from typing import Any, Optional
2 |
3 | from strawberry.test import BaseGraphQLTestClient
4 |
5 |
6 | class GraphQLTestClient(BaseGraphQLTestClient):
7 | def request(
8 | self,
9 | body: dict[str, object],
10 | headers: Optional[dict[str, object]] = None,
11 | files: Optional[dict[str, object]] = None,
12 | ) -> Any:
13 | if files:
14 | return self._client.post(
15 | self.url, data=body, format="multipart", headers=headers
16 | )
17 |
18 | return self._client.post(
19 | self.url, data=body, content_type="application/json", headers=headers
20 | )
21 |
22 |
23 | __all__ = ["GraphQLTestClient"]
24 |
--------------------------------------------------------------------------------
/strawberry/exceptions/exception_source.py:
--------------------------------------------------------------------------------
1 | from dataclasses import dataclass
2 | from pathlib import Path
3 |
4 |
5 | @dataclass
6 | class ExceptionSource:
7 | path: Path
8 | code: str
9 | start_line: int
10 | end_line: int
11 | error_line: int
12 | error_column: int
13 | error_column_end: int
14 |
15 | @property
16 | def path_relative_to_cwd(self) -> Path:
17 | if self.path.is_absolute():
18 | return self.path.relative_to(Path.cwd())
19 |
20 | return self.path
21 |
--------------------------------------------------------------------------------
/strawberry/exceptions/missing_dependencies.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from typing import Optional
4 |
5 |
6 | class MissingOptionalDependenciesError(Exception):
7 | """Some optional dependencies that are required for a particular task are missing."""
8 |
9 | def __init__(
10 | self,
11 | *,
12 | packages: Optional[list[str]] = None,
13 | extras: Optional[list[str]] = None,
14 | ) -> None:
15 | """Initialize the error.
16 |
17 | Args:
18 | packages: List of packages that are required but missing.
19 | extras: List of extras that are required but missing.
20 | """
21 | packages = packages or []
22 |
23 | if extras:
24 | packages.append(f"'strawberry-graphql[{','.join(extras)}]'")
25 |
26 | hint = f" (hint: pip install {' '.join(packages)})" if packages else ""
27 |
28 | self.message = f"Some optional dependencies are missing{hint}"
29 |
--------------------------------------------------------------------------------
/strawberry/exceptions/utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/exceptions/utils/__init__.py
--------------------------------------------------------------------------------
/strawberry/experimental/__init__.py:
--------------------------------------------------------------------------------
1 | try:
2 | from . import pydantic
3 | except ModuleNotFoundError:
4 | pass
5 | else:
6 | __all__ = ["pydantic"]
7 |
--------------------------------------------------------------------------------
/strawberry/experimental/pydantic/__init__.py:
--------------------------------------------------------------------------------
1 | from .error_type import error_type
2 | from .exceptions import UnregisteredTypeException
3 | from .object_type import input, interface, type # noqa: A004
4 |
5 | __all__ = [
6 | "UnregisteredTypeException",
7 | "error_type",
8 | "input",
9 | "interface",
10 | "type",
11 | ]
12 |
--------------------------------------------------------------------------------
/strawberry/experimental/pydantic/conversion_types.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from typing import TYPE_CHECKING, Any, Optional, TypeVar
4 | from typing_extensions import Protocol
5 |
6 | from pydantic import BaseModel
7 |
8 | if TYPE_CHECKING:
9 | from strawberry.types.base import StrawberryObjectDefinition
10 |
11 |
12 | PydanticModel = TypeVar("PydanticModel", bound=BaseModel)
13 |
14 |
15 | class StrawberryTypeFromPydantic(Protocol[PydanticModel]):
16 | """This class does not exist in runtime.
17 |
18 | It only makes the methods below visible for IDEs.
19 | """
20 |
21 | def __init__(self, **kwargs: Any) -> None: ...
22 |
23 | @staticmethod
24 | def from_pydantic(
25 | instance: PydanticModel, extra: Optional[dict[str, Any]] = None
26 | ) -> StrawberryTypeFromPydantic[PydanticModel]: ...
27 |
28 | def to_pydantic(self, **kwargs: Any) -> PydanticModel: ...
29 |
30 | @property
31 | def __strawberry_definition__(self) -> StrawberryObjectDefinition: ...
32 |
33 | @property
34 | def _pydantic_type(self) -> type[PydanticModel]: ...
35 |
--------------------------------------------------------------------------------
/strawberry/ext/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/ext/__init__.py
--------------------------------------------------------------------------------
/strawberry/ext/dataclasses/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/ext/dataclasses/__init__.py
--------------------------------------------------------------------------------
/strawberry/extensions/disable_validation.py:
--------------------------------------------------------------------------------
1 | from collections.abc import Iterator
2 |
3 | from strawberry.extensions.base_extension import SchemaExtension
4 |
5 |
6 | class DisableValidation(SchemaExtension):
7 | """Disable query validation.
8 |
9 | Example:
10 |
11 | ```python
12 | import strawberry
13 | from strawberry.extensions import DisableValidation
14 |
15 | schema = strawberry.Schema(
16 | Query,
17 | extensions=[
18 | DisableValidation,
19 | ],
20 | )
21 | ```
22 | """
23 |
24 | def __init__(self) -> None:
25 | # There aren't any arguments to this extension yet but we might add
26 | # some in the future
27 | pass
28 |
29 | def on_operation(self) -> Iterator[None]:
30 | self.execution_context.validation_rules = () # remove all validation_rules
31 | yield
32 |
33 |
34 | __all__ = ["DisableValidation"]
35 |
--------------------------------------------------------------------------------
/strawberry/extensions/pyinstrument.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from pathlib import Path
4 | from typing import TYPE_CHECKING
5 |
6 | from pyinstrument import Profiler
7 |
8 | from strawberry.extensions.base_extension import SchemaExtension
9 |
10 | if TYPE_CHECKING:
11 | from collections.abc import Iterator
12 |
13 |
14 | class PyInstrument(SchemaExtension):
15 | """Extension to profile the execution time of resolvers using PyInstrument."""
16 |
17 | def __init__(
18 | self,
19 | report_path: Path = Path("pyinstrument.html"),
20 | ) -> None:
21 | self._report_path = report_path
22 |
23 | def on_operation(self) -> Iterator[None]:
24 | profiler = Profiler()
25 | profiler.start()
26 |
27 | yield
28 |
29 | profiler.stop()
30 |
31 | self._report_path.write_text(profiler.output_html())
32 |
33 |
34 | __all__ = ["PyInstrument"]
35 |
--------------------------------------------------------------------------------
/strawberry/extensions/tracing/utils.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from typing import TYPE_CHECKING, Any, Callable
4 |
5 | from strawberry.extensions.utils import is_introspection_field
6 | from strawberry.resolvers import is_default_resolver
7 |
8 | if TYPE_CHECKING:
9 | from graphql import GraphQLResolveInfo
10 |
11 |
12 | def should_skip_tracing(resolver: Callable[..., Any], info: GraphQLResolveInfo) -> bool:
13 | if info.field_name not in info.parent_type.fields:
14 | return True
15 | resolver = info.parent_type.fields[info.field_name].resolve
16 | return (
17 | is_introspection_field(info)
18 | or is_default_resolver(resolver)
19 | or resolver is None
20 | )
21 |
22 |
23 | __all__ = ["should_skip_tracing"]
24 |
--------------------------------------------------------------------------------
/strawberry/extensions/utils.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from typing import TYPE_CHECKING, Union
4 |
5 | if TYPE_CHECKING:
6 | from graphql import GraphQLResolveInfo
7 |
8 |
9 | def is_introspection_key(key: Union[str, int]) -> bool:
10 | # from: https://spec.graphql.org/June2018/#sec-Schema
11 | # > All types and directives defined within a schema must not have a name which
12 | # > begins with "__" (two underscores), as this is used exclusively
13 | # > by GraphQL`s introspection system.
14 |
15 | return str(key).startswith("__")
16 |
17 |
18 | def is_introspection_field(info: GraphQLResolveInfo) -> bool:
19 | path = info.path
20 |
21 | while path:
22 | if is_introspection_key(path.key):
23 | return True
24 | path = path.prev
25 |
26 | return False
27 |
28 |
29 | def get_path_from_info(info: GraphQLResolveInfo) -> list[str]:
30 | path = info.path
31 | elements = []
32 |
33 | while path:
34 | elements.append(path.key)
35 | path = path.prev
36 |
37 | return elements[::-1]
38 |
39 |
40 | __all__ = ["get_path_from_info", "is_introspection_field", "is_introspection_key"]
41 |
--------------------------------------------------------------------------------
/strawberry/fastapi/__init__.py:
--------------------------------------------------------------------------------
1 | from strawberry.fastapi.context import BaseContext
2 | from strawberry.fastapi.router import GraphQLRouter
3 |
4 | __all__ = ["BaseContext", "GraphQLRouter"]
5 |
--------------------------------------------------------------------------------
/strawberry/fastapi/context.py:
--------------------------------------------------------------------------------
1 | from typing import Any, Optional, Union
2 |
3 | from starlette.background import BackgroundTasks
4 | from starlette.requests import Request
5 | from starlette.responses import Response
6 | from starlette.websockets import WebSocket
7 |
8 | CustomContext = Union["BaseContext", dict[str, Any]]
9 | MergedContext = Union[
10 | "BaseContext", dict[str, Union[Any, BackgroundTasks, Request, Response, WebSocket]]
11 | ]
12 |
13 |
14 | class BaseContext:
15 | connection_params: Optional[Any] = None
16 |
17 | def __init__(self) -> None:
18 | self.request: Optional[Union[Request, WebSocket]] = None
19 | self.background_tasks: Optional[BackgroundTasks] = None
20 | self.response: Optional[Response] = None
21 |
22 |
23 | __all__ = ["BaseContext"]
24 |
--------------------------------------------------------------------------------
/strawberry/federation/__init__.py:
--------------------------------------------------------------------------------
1 | from .argument import argument
2 | from .enum import enum, enum_value
3 | from .field import field
4 | from .mutation import mutation
5 | from .object_type import input, interface, interface_object, type # noqa: A004
6 | from .scalar import scalar
7 | from .schema import Schema
8 | from .schema_directive import schema_directive
9 | from .union import union
10 |
11 | __all__ = [
12 | "Schema",
13 | "argument",
14 | "enum",
15 | "enum_value",
16 | "field",
17 | "input",
18 | "interface",
19 | "interface_object",
20 | "mutation",
21 | "scalar",
22 | "schema_directive",
23 | "type",
24 | "union",
25 | ]
26 |
--------------------------------------------------------------------------------
/strawberry/federation/argument.py:
--------------------------------------------------------------------------------
1 | from collections.abc import Iterable
2 | from typing import Optional
3 |
4 | from strawberry.types.arguments import StrawberryArgumentAnnotation
5 |
6 |
7 | def argument(
8 | description: Optional[str] = None,
9 | name: Optional[str] = None,
10 | deprecation_reason: Optional[str] = None,
11 | directives: Iterable[object] = (),
12 | inaccessible: bool = False,
13 | tags: Optional[Iterable[str]] = (),
14 | ) -> StrawberryArgumentAnnotation:
15 | from strawberry.federation.schema_directives import Inaccessible, Tag
16 |
17 | directives = list(directives)
18 |
19 | if inaccessible:
20 | directives.append(Inaccessible())
21 |
22 | if tags:
23 | directives.extend(Tag(name=tag) for tag in tags)
24 |
25 | return StrawberryArgumentAnnotation(
26 | description=description,
27 | name=name,
28 | deprecation_reason=deprecation_reason,
29 | directives=directives,
30 | )
31 |
32 |
33 | __all__ = ["argument"]
34 |
--------------------------------------------------------------------------------
/strawberry/federation/mutation.py:
--------------------------------------------------------------------------------
1 | from .field import field
2 |
3 | mutation = field
4 |
5 | __all__ = ["mutation"]
6 |
--------------------------------------------------------------------------------
/strawberry/federation/types.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 |
3 | from strawberry.types.enum import enum
4 | from strawberry.types.scalar import scalar
5 |
6 | FieldSet = scalar(str, name="_FieldSet")
7 |
8 | LinkImport = scalar(object, name="link__Import")
9 |
10 |
11 | @enum(name="link__Purpose")
12 | class LinkPurpose(Enum):
13 | SECURITY = "SECURITY"
14 | EXECUTION = "EXECUTION"
15 |
16 |
17 | __all__ = ["FieldSet", "LinkImport", "LinkPurpose"]
18 |
--------------------------------------------------------------------------------
/strawberry/field_extensions/__init__.py:
--------------------------------------------------------------------------------
1 | from .input_mutation import InputMutationExtension
2 |
3 | __all__ = [
4 | "InputMutationExtension",
5 | ]
6 |
--------------------------------------------------------------------------------
/strawberry/file_uploads/__init__.py:
--------------------------------------------------------------------------------
1 | from .scalars import Upload
2 |
3 | __all__ = ["Upload"]
4 |
--------------------------------------------------------------------------------
/strawberry/file_uploads/scalars.py:
--------------------------------------------------------------------------------
1 | from typing import NewType
2 |
3 | from strawberry.types.scalar import scalar
4 |
5 | Upload = scalar(NewType("Upload", bytes), parse_value=lambda x: x)
6 |
7 | __all__ = ["Upload"]
8 |
--------------------------------------------------------------------------------
/strawberry/flask/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/flask/__init__.py
--------------------------------------------------------------------------------
/strawberry/http/exceptions.py:
--------------------------------------------------------------------------------
1 | class HTTPException(Exception):
2 | def __init__(self, status_code: int, reason: str) -> None:
3 | self.status_code = status_code
4 | self.reason = reason
5 |
6 |
7 | class NonTextMessageReceived(Exception):
8 | pass
9 |
10 |
11 | class NonJsonMessageReceived(Exception):
12 | pass
13 |
14 |
15 | class WebSocketDisconnected(Exception):
16 | pass
17 |
18 |
19 | __all__ = ["HTTPException"]
20 |
--------------------------------------------------------------------------------
/strawberry/http/ides.py:
--------------------------------------------------------------------------------
1 | import pathlib
2 | from typing import Optional
3 | from typing_extensions import Literal
4 |
5 | GraphQL_IDE = Literal["graphiql", "apollo-sandbox", "pathfinder"]
6 |
7 |
8 | def get_graphql_ide_html(
9 | graphql_ide: Optional[GraphQL_IDE] = "graphiql",
10 | ) -> str:
11 | here = pathlib.Path(__file__).parents[1]
12 |
13 | if graphql_ide == "apollo-sandbox":
14 | path = here / "static/apollo-sandbox.html"
15 | elif graphql_ide == "pathfinder":
16 | path = here / "static/pathfinder.html"
17 | else:
18 | path = here / "static/graphiql.html"
19 |
20 | return path.read_text(encoding="utf-8")
21 |
22 |
23 | __all__ = ["GraphQL_IDE", "get_graphql_ide_html"]
24 |
--------------------------------------------------------------------------------
/strawberry/http/parse_content_type.py:
--------------------------------------------------------------------------------
1 | from email.message import Message
2 |
3 |
4 | def parse_content_type(content_type: str) -> tuple[str, dict[str, str]]:
5 | """Parse a content type header into a mime-type and a dictionary of parameters."""
6 | email = Message()
7 | email["content-type"] = content_type
8 |
9 | params = email.get_params()
10 |
11 | assert params
12 |
13 | mime_type, _ = params.pop(0)
14 |
15 | return mime_type, dict(params)
16 |
--------------------------------------------------------------------------------
/strawberry/http/temporal_response.py:
--------------------------------------------------------------------------------
1 | from dataclasses import dataclass, field
2 |
3 |
4 | @dataclass
5 | class TemporalResponse:
6 | status_code: int = 200
7 | headers: dict[str, str] = field(default_factory=dict)
8 |
9 |
10 | __all__ = ["TemporalResponse"]
11 |
--------------------------------------------------------------------------------
/strawberry/http/types.py:
--------------------------------------------------------------------------------
1 | from collections.abc import Mapping
2 | from typing import Any, Optional
3 | from typing_extensions import Literal, TypedDict
4 |
5 | HTTPMethod = Literal[
6 | "GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "TRACE"
7 | ]
8 |
9 | QueryParams = Mapping[str, Optional[str]]
10 |
11 |
12 | class FormData(TypedDict):
13 | files: Mapping[str, Any]
14 | form: Mapping[str, Any]
15 |
16 |
17 | __all__ = ["FormData", "HTTPMethod", "QueryParams"]
18 |
--------------------------------------------------------------------------------
/strawberry/http/typevars.py:
--------------------------------------------------------------------------------
1 | from typing_extensions import TypeVar
2 |
3 | Request = TypeVar("Request", contravariant=True)
4 | Response = TypeVar("Response")
5 | SubResponse = TypeVar("SubResponse")
6 | WebSocketRequest = TypeVar("WebSocketRequest")
7 | WebSocketResponse = TypeVar("WebSocketResponse")
8 | Context = TypeVar("Context", default=None)
9 | RootValue = TypeVar("RootValue", default=None)
10 |
11 |
12 | __all__ = [
13 | "Context",
14 | "Request",
15 | "Response",
16 | "RootValue",
17 | "SubResponse",
18 | "WebSocketRequest",
19 | "WebSocketResponse",
20 | ]
21 |
--------------------------------------------------------------------------------
/strawberry/litestar/__init__.py:
--------------------------------------------------------------------------------
1 | from .controller import (
2 | BaseContext,
3 | HTTPContextType,
4 | WebSocketContextType,
5 | make_graphql_controller,
6 | )
7 |
8 | __all__ = [
9 | "BaseContext",
10 | "HTTPContextType",
11 | "WebSocketContextType",
12 | "make_graphql_controller",
13 | ]
14 |
--------------------------------------------------------------------------------
/strawberry/printer/__init__.py:
--------------------------------------------------------------------------------
1 | from .printer import print_schema
2 |
3 | __all__ = ["print_schema"]
4 |
--------------------------------------------------------------------------------
/strawberry/py.typed:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/py.typed
--------------------------------------------------------------------------------
/strawberry/quart/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/quart/__init__.py
--------------------------------------------------------------------------------
/strawberry/relay/__init__.py:
--------------------------------------------------------------------------------
1 | from .fields import ConnectionExtension, NodeExtension, connection, node
2 | from .types import (
3 | Connection,
4 | Edge,
5 | GlobalID,
6 | GlobalIDValueError,
7 | ListConnection,
8 | Node,
9 | NodeID,
10 | NodeType,
11 | PageInfo,
12 | )
13 | from .utils import from_base64, to_base64
14 |
15 | __all__ = [
16 | "Connection",
17 | "ConnectionExtension",
18 | "Edge",
19 | "GlobalID",
20 | "GlobalIDValueError",
21 | "ListConnection",
22 | "Node",
23 | "NodeExtension",
24 | "NodeID",
25 | "NodeType",
26 | "PageInfo",
27 | "connection",
28 | "from_base64",
29 | "node",
30 | "to_base64",
31 | ]
32 |
--------------------------------------------------------------------------------
/strawberry/resolvers.py:
--------------------------------------------------------------------------------
1 | from typing import Any, Callable
2 |
3 |
4 | def is_default_resolver(func: Callable[..., Any]) -> bool:
5 | """Check whether the function is a default resolver or a user provided one."""
6 | return getattr(func, "_is_default", False)
7 |
8 |
9 | __all__ = ["is_default_resolver"]
10 |
--------------------------------------------------------------------------------
/strawberry/sanic/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/sanic/__init__.py
--------------------------------------------------------------------------------
/strawberry/sanic/context.py:
--------------------------------------------------------------------------------
1 | import warnings
2 | from typing_extensions import TypedDict
3 |
4 | from sanic.request import Request
5 | from strawberry.http.temporal_response import TemporalResponse
6 |
7 |
8 | class StrawberrySanicContext(TypedDict):
9 | request: Request
10 | response: TemporalResponse
11 |
12 | # see https://github.com/python/mypy/issues/13066 for the type ignore
13 | def __getattr__(self, key: str) -> object: # type: ignore
14 | # a warning will be raised because this is not supported anymore
15 | # but we need to keep it for backwards compatibility
16 |
17 | warnings.warn(
18 | "Accessing context attributes via the dot notation is deprecated, "
19 | "please use context.get('key') or context['key'] instead",
20 | DeprecationWarning,
21 | stacklevel=2,
22 | )
23 |
24 | return super().__getitem__(key)
25 |
26 |
27 | __all__ = ["StrawberrySanicContext"]
28 |
--------------------------------------------------------------------------------
/strawberry/sanic/utils.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from typing import TYPE_CHECKING, Any, Optional, Union, cast
4 |
5 | if TYPE_CHECKING:
6 | from sanic.request import File, Request
7 |
8 |
9 | def convert_request_to_files_dict(request: Request) -> dict[str, Any]:
10 | """Converts the request.files dictionary to a dictionary of sanic Request objects.
11 |
12 | `request.files` has the following format, even if only a single file is uploaded:
13 |
14 | ```python
15 | {
16 | "textFile": [
17 | sanic.request.File(type="text/plain", body=b"strawberry", name="textFile.txt")
18 | ]
19 | }
20 | ```
21 |
22 | Note that the dictionary entries are lists.
23 | """
24 | request_files = cast("Optional[dict[str, list[File]]]", request.files)
25 |
26 | if not request_files:
27 | return {}
28 |
29 | files_dict: dict[str, Union[File, list[File]]] = {}
30 |
31 | for field_name, file_list in request_files.items():
32 | assert len(file_list) == 1
33 |
34 | files_dict[field_name] = file_list[0]
35 |
36 | return files_dict
37 |
38 |
39 | __all__ = ["convert_request_to_files_dict"]
40 |
--------------------------------------------------------------------------------
/strawberry/schema/__init__.py:
--------------------------------------------------------------------------------
1 | from .base import BaseSchema
2 | from .schema import Schema
3 |
4 | __all__ = ["BaseSchema", "Schema"]
5 |
--------------------------------------------------------------------------------
/strawberry/schema/config.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from dataclasses import InitVar, dataclass, field
4 | from typing import Any, Callable
5 |
6 | from strawberry.types.info import Info
7 |
8 | from .name_converter import NameConverter
9 |
10 |
11 | @dataclass
12 | class StrawberryConfig:
13 | auto_camel_case: InitVar[bool] = None # pyright: reportGeneralTypeIssues=false
14 | name_converter: NameConverter = field(default_factory=NameConverter)
15 | default_resolver: Callable[[Any, str], object] = getattr
16 | relay_max_results: int = 100
17 | relay_use_legacy_global_id: bool = False
18 | disable_field_suggestions: bool = False
19 | info_class: type[Info] = Info
20 |
21 | def __post_init__(
22 | self,
23 | auto_camel_case: bool,
24 | ) -> None:
25 | if auto_camel_case is not None:
26 | self.name_converter.auto_camel_case = auto_camel_case
27 |
28 | if not issubclass(self.info_class, Info):
29 | raise TypeError("`info_class` must be a subclass of strawberry.Info")
30 |
31 |
32 | __all__ = ["StrawberryConfig"]
33 |
--------------------------------------------------------------------------------
/strawberry/schema/exceptions.py:
--------------------------------------------------------------------------------
1 | from strawberry.types.graphql import OperationType
2 |
3 |
4 | class CannotGetOperationTypeError(Exception):
5 | """Internal error raised when we cannot get the operation type from a GraphQL document."""
6 |
7 |
8 | class InvalidOperationTypeError(Exception):
9 | def __init__(self, operation_type: OperationType) -> None:
10 | self.operation_type = operation_type
11 |
12 | def as_http_error_reason(self, method: str) -> str:
13 | operation_type = {
14 | OperationType.QUERY: "queries",
15 | OperationType.MUTATION: "mutations",
16 | OperationType.SUBSCRIPTION: "subscriptions",
17 | }[self.operation_type]
18 |
19 | return f"{operation_type} are not allowed when using {method}"
20 |
21 |
22 | __all__ = [
23 | "CannotGetOperationTypeError",
24 | "InvalidOperationTypeError",
25 | ]
26 |
--------------------------------------------------------------------------------
/strawberry/schema/types/__init__.py:
--------------------------------------------------------------------------------
1 | from .concrete_type import ConcreteType
2 |
3 | __all__ = ["ConcreteType"]
4 |
--------------------------------------------------------------------------------
/strawberry/schema/types/concrete_type.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import dataclasses
4 | from typing import TYPE_CHECKING, Union
5 |
6 | from graphql import GraphQLField, GraphQLInputField, GraphQLType
7 |
8 | if TYPE_CHECKING:
9 | from strawberry.types.base import StrawberryObjectDefinition
10 | from strawberry.types.enum import EnumDefinition
11 | from strawberry.types.scalar import ScalarDefinition
12 | from strawberry.types.union import StrawberryUnion
13 |
14 | Field = Union[GraphQLInputField, GraphQLField]
15 |
16 |
17 | @dataclasses.dataclass
18 | class ConcreteType:
19 | definition: Union[
20 | StrawberryObjectDefinition, EnumDefinition, ScalarDefinition, StrawberryUnion
21 | ]
22 | implementation: GraphQLType
23 |
24 |
25 | TypeMap = dict[str, ConcreteType]
26 |
27 |
28 | __all__ = ["ConcreteType", "Field", "GraphQLType", "TypeMap"]
29 |
--------------------------------------------------------------------------------
/strawberry/schema/validation_rules/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/schema/validation_rules/__init__.py
--------------------------------------------------------------------------------
/strawberry/schema_directives.py:
--------------------------------------------------------------------------------
1 | from strawberry.schema_directive import Location, schema_directive
2 |
3 |
4 | @schema_directive(locations=[Location.INPUT_OBJECT], name="oneOf")
5 | class OneOf: ...
6 |
7 |
8 | __all__ = ["OneOf"]
9 |
--------------------------------------------------------------------------------
/strawberry/static/apollo-sandbox.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Strawberry Apollo Sandbox
5 |
13 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/strawberry/subscriptions/__init__.py:
--------------------------------------------------------------------------------
1 | GRAPHQL_TRANSPORT_WS_PROTOCOL = "graphql-transport-ws"
2 | GRAPHQL_WS_PROTOCOL = "graphql-ws"
3 |
4 |
5 | __all__ = [
6 | "GRAPHQL_TRANSPORT_WS_PROTOCOL",
7 | "GRAPHQL_WS_PROTOCOL",
8 | ]
9 |
--------------------------------------------------------------------------------
/strawberry/subscriptions/protocols/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/subscriptions/protocols/__init__.py
--------------------------------------------------------------------------------
/strawberry/subscriptions/protocols/graphql_transport_ws/__init__.py:
--------------------------------------------------------------------------------
1 | # Code 4406 is "Subprotocol not acceptable"
2 | WS_4406_PROTOCOL_NOT_ACCEPTABLE = 4406
3 |
4 |
5 | __all__ = [
6 | "WS_4406_PROTOCOL_NOT_ACCEPTABLE",
7 | ]
8 |
--------------------------------------------------------------------------------
/strawberry/subscriptions/protocols/graphql_ws/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/subscriptions/protocols/graphql_ws/__init__.py
--------------------------------------------------------------------------------
/strawberry/test/__init__.py:
--------------------------------------------------------------------------------
1 | from .client import BaseGraphQLTestClient, Body, Response
2 |
3 | __all__ = ["BaseGraphQLTestClient", "Body", "Response"]
4 |
--------------------------------------------------------------------------------
/strawberry/tools/__init__.py:
--------------------------------------------------------------------------------
1 | from .create_type import create_type
2 | from .merge_types import merge_types
3 |
4 | __all__ = [
5 | "create_type",
6 | "merge_types",
7 | ]
8 |
--------------------------------------------------------------------------------
/strawberry/tools/merge_types.py:
--------------------------------------------------------------------------------
1 | import warnings
2 | from collections import Counter
3 | from itertools import chain
4 |
5 | import strawberry
6 | from strawberry.types.base import has_object_definition
7 |
8 |
9 | def merge_types(name: str, types: tuple[type, ...]) -> type:
10 | """Merge multiple Strawberry types into one.
11 |
12 | For example, given two queries `A` and `B`, one can merge them into a
13 | super type as follows:
14 |
15 | merge_types("SuperQuery", (B, A))
16 |
17 | This is essentially the same as:
18 |
19 | class SuperQuery(B, A):
20 | ...
21 | """
22 | if not types:
23 | raise ValueError("Can't merge types if none are supplied")
24 |
25 | fields = chain(
26 | *(t.__strawberry_definition__.fields for t in types if has_object_definition(t))
27 | )
28 | counter = Counter(f.name for f in fields)
29 | dupes = [f for f, c in counter.most_common() if c > 1]
30 | if dupes:
31 | warnings.warn(
32 | "{} has overridden fields: {}".format(name, ", ".join(dupes)), stacklevel=2
33 | )
34 |
35 | return strawberry.type(type(name, types, {}))
36 |
37 |
38 | __all__ = ["merge_types"]
39 |
--------------------------------------------------------------------------------
/strawberry/types/__init__.py:
--------------------------------------------------------------------------------
1 | from .base import get_object_definition, has_object_definition
2 | from .execution import ExecutionContext, ExecutionResult, SubscriptionExecutionResult
3 | from .info import Info
4 |
5 | __all__ = [
6 | "ExecutionContext",
7 | "ExecutionResult",
8 | "Info",
9 | "Info",
10 | "SubscriptionExecutionResult",
11 | "get_object_definition",
12 | "has_object_definition",
13 | ]
14 |
--------------------------------------------------------------------------------
/strawberry/types/cast.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from typing import Any, TypeVar, overload
4 |
5 | _T = TypeVar("_T", bound=object)
6 |
7 | TYPE_CAST_ATTRIBUTE = "__as_strawberry_type__"
8 |
9 |
10 | @overload
11 | def cast(type_: type, obj: None) -> None: ...
12 |
13 |
14 | @overload
15 | def cast(type_: type, obj: _T) -> _T: ...
16 |
17 |
18 | def cast(type_: type, obj: _T | None) -> _T | None:
19 | """Cast an object to given type.
20 |
21 | This is used to mark an object as a cast object, so that the type can be
22 | picked up when resolving unions/interfaces in case of ambiguity, which can
23 | happen when returning an alike object instead of an instance of the type
24 | (e.g. returning a Django, Pydantic or SQLAlchemy object)
25 | """
26 | if obj is None:
27 | return None
28 |
29 | setattr(obj, TYPE_CAST_ATTRIBUTE, type_)
30 | return obj
31 |
32 |
33 | def get_strawberry_type_cast(obj: Any) -> type | None:
34 | """Get the type of a cast object."""
35 | return getattr(obj, TYPE_CAST_ATTRIBUTE, None)
36 |
--------------------------------------------------------------------------------
/strawberry/types/fields/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/types/fields/__init__.py
--------------------------------------------------------------------------------
/strawberry/types/graphql.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import enum
4 | from typing import TYPE_CHECKING
5 |
6 | if TYPE_CHECKING:
7 | from strawberry.http.types import HTTPMethod
8 |
9 |
10 | class OperationType(enum.Enum):
11 | QUERY = "query"
12 | MUTATION = "mutation"
13 | SUBSCRIPTION = "subscription"
14 |
15 | @staticmethod
16 | def from_http(method: HTTPMethod) -> set[OperationType]:
17 | if method == "GET":
18 | return {
19 | OperationType.QUERY,
20 | # subscriptions are supported via GET in the multipart protocol
21 | OperationType.SUBSCRIPTION,
22 | }
23 |
24 | if method == "POST":
25 | return {
26 | OperationType.QUERY,
27 | OperationType.MUTATION,
28 | OperationType.SUBSCRIPTION,
29 | }
30 |
31 | raise ValueError(f"Unsupported HTTP method: {method}") # pragma: no cover
32 |
33 |
34 | __all__ = ["OperationType"]
35 |
--------------------------------------------------------------------------------
/strawberry/types/private.py:
--------------------------------------------------------------------------------
1 | from typing import Annotated, TypeVar
2 |
3 | from strawberry.utils.typing import type_has_annotation
4 |
5 |
6 | class StrawberryPrivate: ...
7 |
8 |
9 | T = TypeVar("T")
10 |
11 | Private = Annotated[T, StrawberryPrivate()]
12 | """Represents a field that won't be exposed in the GraphQL schema.
13 |
14 | Example:
15 |
16 | ```python
17 | import strawberry
18 |
19 |
20 | @strawberry.type
21 | class User:
22 | name: str
23 | age: strawberry.Private[int]
24 | ```
25 | """
26 |
27 |
28 | def is_private(type_: object) -> bool:
29 | return type_has_annotation(type_, StrawberryPrivate)
30 |
31 |
32 | __all__ = ["Private", "is_private"]
33 |
--------------------------------------------------------------------------------
/strawberry/utils/__init__.py:
--------------------------------------------------------------------------------
1 | from graphql.version import VersionInfo, version_info
2 |
3 | IS_GQL_33 = version_info >= VersionInfo.from_str("3.3.0a0")
4 | IS_GQL_32 = not IS_GQL_33
5 |
--------------------------------------------------------------------------------
/strawberry/utils/await_maybe.py:
--------------------------------------------------------------------------------
1 | import inspect
2 | from collections.abc import AsyncIterator, Awaitable, Iterator
3 | from typing import TypeVar, Union
4 |
5 | T = TypeVar("T")
6 |
7 | AwaitableOrValue = Union[Awaitable[T], T]
8 | AsyncIteratorOrIterator = Union[AsyncIterator[T], Iterator[T]]
9 |
10 |
11 | async def await_maybe(value: AwaitableOrValue[T]) -> T:
12 | if inspect.isawaitable(value):
13 | return await value
14 |
15 | return value
16 |
17 |
18 | __all__ = ["AsyncIteratorOrIterator", "AwaitableOrValue", "await_maybe"]
19 |
--------------------------------------------------------------------------------
/strawberry/utils/dataclasses.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import sys
4 | from dataclasses import ( # type: ignore
5 | _FIELD,
6 | _FIELD_INITVAR,
7 | _FIELDS,
8 | _POST_INIT_NAME,
9 | _set_new_attribute,
10 | )
11 | from typing import Any
12 |
13 | from strawberry.ext.dataclasses.dataclasses import dataclass_init_fn
14 |
15 |
16 | def add_custom_init_fn(cls: Any) -> None:
17 | fields = [
18 | f
19 | for f in getattr(cls, _FIELDS).values()
20 | if f._field_type in (_FIELD, _FIELD_INITVAR)
21 | ]
22 | globals_ = sys.modules[cls.__module__].__dict__
23 |
24 | _set_new_attribute(
25 | cls,
26 | "__init__",
27 | dataclass_init_fn(
28 | fields=fields,
29 | frozen=False,
30 | has_post_init=hasattr(cls, _POST_INIT_NAME),
31 | self_name="__dataclass_self__" if "self" in fields else "self",
32 | globals_=globals_,
33 | ),
34 | )
35 |
36 |
37 | __all__ = ["add_custom_init_fn"]
38 |
--------------------------------------------------------------------------------
/strawberry/utils/deprecations.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import warnings
4 | from typing import Any, Optional
5 |
6 |
7 | class DEPRECATION_MESSAGES: # noqa: N801
8 | _TYPE_DEFINITION = (
9 | "_type_definition is deprecated, use __strawberry_definition__ instead"
10 | )
11 |
12 |
13 | class DeprecatedDescriptor:
14 | def __init__(self, msg: str, alias: object, attr_name: str) -> None:
15 | self.msg = msg
16 | self.alias = alias
17 | self.attr_name = attr_name
18 |
19 | def warn(self) -> None:
20 | warnings.warn(self.msg, stacklevel=2)
21 |
22 | def __get__(self, obj: Optional[object], type: Optional[type] = None) -> Any:
23 | self.warn()
24 | return self.alias
25 |
26 | def inject(self, klass: type) -> None:
27 | setattr(klass, self.attr_name, self)
28 |
29 |
30 | __all__ = ["DEPRECATION_MESSAGES", "DeprecatedDescriptor"]
31 |
--------------------------------------------------------------------------------
/strawberry/utils/importer.py:
--------------------------------------------------------------------------------
1 | import importlib
2 | from typing import Optional
3 |
4 |
5 | def import_module_symbol(
6 | selector: str, default_symbol_name: Optional[str] = None
7 | ) -> object:
8 | if ":" in selector:
9 | module_name, symbol_name = selector.split(":", 1)
10 | elif default_symbol_name:
11 | module_name, symbol_name = selector, default_symbol_name
12 | else:
13 | raise ValueError("Selector does not include a symbol name")
14 |
15 | module = importlib.import_module(module_name)
16 | symbol = module
17 |
18 | for attribute_name in symbol_name.split("."):
19 | symbol = getattr(symbol, attribute_name)
20 |
21 | return symbol
22 |
23 |
24 | __all__ = ["import_module_symbol"]
25 |
--------------------------------------------------------------------------------
/strawberry/utils/logging.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import logging
4 | from typing import TYPE_CHECKING, Any, Optional
5 |
6 | if TYPE_CHECKING:
7 | from typing import Final
8 |
9 | from graphql.error import GraphQLError
10 |
11 | from strawberry.types import ExecutionContext
12 |
13 |
14 | class StrawberryLogger:
15 | logger: Final[logging.Logger] = logging.getLogger("strawberry.execution")
16 |
17 | @classmethod
18 | def error(
19 | cls,
20 | error: GraphQLError,
21 | execution_context: Optional[ExecutionContext] = None,
22 | # https://www.python.org/dev/peps/pep-0484/#arbitrary-argument-lists-and-default-argument-values
23 | **logger_kwargs: Any,
24 | ) -> None:
25 | cls.logger.error(error, exc_info=error.original_error, **logger_kwargs)
26 |
27 |
28 | __all__ = ["StrawberryLogger"]
29 |
--------------------------------------------------------------------------------
/strawberry/utils/str_converters.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 |
4 | # Adapted from this response in Stackoverflow
5 | # http://stackoverflow.com/a/19053800/1072990
6 | def to_camel_case(snake_str: str) -> str:
7 | components = snake_str.split("_")
8 | # We capitalize the first letter of each component except the first one
9 | # with the 'capitalize' method and join them together.
10 | return components[0] + "".join(x.capitalize() if x else "_" for x in components[1:])
11 |
12 |
13 | TO_KEBAB_CASE_RE = re.compile("((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))")
14 |
15 |
16 | def to_kebab_case(name: str) -> str:
17 | return TO_KEBAB_CASE_RE.sub(r"-\1", name).lower()
18 |
19 |
20 | def capitalize_first(name: str) -> str:
21 | return name[0].upper() + name[1:]
22 |
23 |
24 | def to_snake_case(name: str) -> str:
25 | name = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", name)
26 | return re.sub("([a-z0-9])([A-Z])", r"\1_\2", name).lower()
27 |
28 |
29 | __all__ = ["capitalize_first", "to_camel_case", "to_kebab_case", "to_snake_case"]
30 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/__init__.py
--------------------------------------------------------------------------------
/tests/a.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from typing import TYPE_CHECKING, Annotated, Optional
4 |
5 | import strawberry
6 |
7 | if TYPE_CHECKING:
8 | from tests.b import B
9 |
10 |
11 | @strawberry.type
12 | class A:
13 | id: strawberry.ID
14 |
15 | @strawberry.field
16 | async def b(self) -> Annotated[B, strawberry.lazy("tests.b")]:
17 | from tests.b import B
18 |
19 | return B(id=self.id)
20 |
21 | @strawberry.field
22 | async def optional_b(self) -> Annotated[B, strawberry.lazy("tests.b")] | None:
23 | from tests.b import B
24 |
25 | return B(id=self.id)
26 |
27 | @strawberry.field
28 | async def optional_b2(self) -> Optional[Annotated[B, strawberry.lazy("tests.b")]]:
29 | from tests.b import B
30 |
31 | return B(id=self.id)
32 |
--------------------------------------------------------------------------------
/tests/asgi/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/asgi/__init__.py
--------------------------------------------------------------------------------
/tests/asgi/test_async.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from typing import TYPE_CHECKING, Optional
4 |
5 | import pytest
6 |
7 | import strawberry
8 |
9 | if TYPE_CHECKING:
10 | from starlette.testclient import TestClient
11 |
12 |
13 | @pytest.fixture
14 | def test_client() -> TestClient:
15 | from starlette.testclient import TestClient
16 |
17 | from strawberry.asgi import GraphQL
18 |
19 | @strawberry.type
20 | class Query:
21 | @strawberry.field
22 | async def hello(self, name: Optional[str] = None) -> str:
23 | return f"Hello {name or 'world'}"
24 |
25 | async_schema = strawberry.Schema(Query)
26 | app = GraphQL[None, None](async_schema)
27 | return TestClient(app)
28 |
29 |
30 | def test_simple_query(test_client: TestClient):
31 | response = test_client.post("/", json={"query": "{ hello }"})
32 |
33 | assert response.json() == {"data": {"hello": "Hello world"}}
34 |
--------------------------------------------------------------------------------
/tests/b.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from typing import TYPE_CHECKING, Annotated, Optional
4 |
5 | import strawberry
6 |
7 | if TYPE_CHECKING:
8 | from tests.a import A
9 |
10 |
11 | @strawberry.type
12 | class B:
13 | id: strawberry.ID
14 |
15 | @strawberry.field
16 | async def a(self) -> Annotated[A, strawberry.lazy("tests.a"), object()]:
17 | from tests.a import A
18 |
19 | return A(id=self.id)
20 |
21 | @strawberry.field
22 | async def a_list(
23 | self,
24 | ) -> list[Annotated[A, strawberry.lazy("tests.a")]]: # pragma: no cover
25 | from tests.a import A
26 |
27 | return [A(id=self.id)]
28 |
29 | @strawberry.field
30 | async def optional_a(
31 | self,
32 | ) -> Annotated[A, strawberry.lazy("tests.a"), object()] | None:
33 | from tests.a import A
34 |
35 | return A(id=self.id)
36 |
37 | @strawberry.field
38 | async def optional_a2(
39 | self,
40 | ) -> Optional[Annotated[A, strawberry.lazy("tests.a"), object()]]:
41 | from tests.a import A
42 |
43 | return A(id=self.id)
44 |
--------------------------------------------------------------------------------
/tests/benchmarks/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/benchmarks/__init__.py
--------------------------------------------------------------------------------
/tests/benchmarks/queries/items.graphql:
--------------------------------------------------------------------------------
1 | query Items($count: Int!) {
2 | items(count: $count) {
3 | name
4 | index
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/tests/benchmarks/queries/many_fields.graphql:
--------------------------------------------------------------------------------
1 | {
2 | people {
3 | age
4 | description
5 | address
6 | name
7 | propA
8 | propB
9 | propC
10 | propD
11 | propE
12 | propF
13 | propG
14 | propH
15 | propI
16 | propJ
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/tests/benchmarks/queries/many_fields_directives.graphql:
--------------------------------------------------------------------------------
1 | {
2 | people {
3 | age
4 | description
5 | address
6 | name
7 | propA
8 | propB
9 | propC
10 | propD
11 | propE
12 | propF
13 | propG @uppercase
14 | propH @uppercase
15 | propI
16 | propJ
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/tests/benchmarks/queries/simple.graphql:
--------------------------------------------------------------------------------
1 | {
2 | hello
3 | }
4 |
--------------------------------------------------------------------------------
/tests/c.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import strawberry
4 |
5 |
6 | @strawberry.type
7 | class C:
8 | id: strawberry.ID
9 |
--------------------------------------------------------------------------------
/tests/channels/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/channels/__init__.py
--------------------------------------------------------------------------------
/tests/cli/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/cli/__init__.py
--------------------------------------------------------------------------------
/tests/cli/conftest.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import sys
4 | from typing import TYPE_CHECKING
5 |
6 | import pytest
7 | from pytest_mock import MockFixture
8 | from typer.testing import CliRunner
9 |
10 | if TYPE_CHECKING:
11 | from starlette.testclient import TestClient
12 | from typer import Typer
13 |
14 |
15 | @pytest.fixture
16 | def cli_runner(mocker: MockFixture) -> CliRunner:
17 | # Mock of uvicorn.run
18 | uvicorn_run_patch = mocker.patch("uvicorn.run")
19 | uvicorn_run_patch.return_value = True
20 | return CliRunner()
21 |
22 |
23 | @pytest.fixture
24 | def debug_server_client(mocker: MockFixture) -> TestClient:
25 | from starlette.testclient import TestClient
26 |
27 | schema_import_path = "tests.fixtures.sample_package.sample_module"
28 | mocker.patch.object(sys, "argv", ["strawberry", "server", schema_import_path])
29 |
30 | from strawberry.cli.debug_server import app
31 |
32 | return TestClient(app)
33 |
34 |
35 | @pytest.fixture
36 | def cli_app() -> Typer:
37 | from strawberry.cli.app import app
38 |
39 | return app
40 |
--------------------------------------------------------------------------------
/tests/cli/fixtures/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/cli/fixtures/__init__.py
--------------------------------------------------------------------------------
/tests/cli/fixtures/unions.py:
--------------------------------------------------------------------------------
1 | import strawberry
2 |
3 | # create a few types and then a union type
4 |
5 |
6 | @strawberry.type
7 | class Foo:
8 | a: str
9 |
10 |
11 | @strawberry.type
12 | class Bar:
13 | b: str
14 |
15 |
16 | @strawberry.type
17 | class Baz:
18 | c: str
19 |
20 |
21 | @strawberry.type
22 | class Qux:
23 | d: str
24 |
25 |
26 | # this is the union type
27 |
28 | Union1 = strawberry.union(name="Union1", types=(Foo, Bar, Baz, Qux))
29 | Union2 = strawberry.union(name="Union2", types=(Baz, Qux))
30 |
--------------------------------------------------------------------------------
/tests/cli/snapshots/unions.py:
--------------------------------------------------------------------------------
1 | import strawberry
2 | from typing import Annotated
3 |
4 | # create a few types and then a union type
5 |
6 |
7 | @strawberry.type
8 | class Foo:
9 | a: str
10 |
11 |
12 | @strawberry.type
13 | class Bar:
14 | b: str
15 |
16 |
17 | @strawberry.type
18 | class Baz:
19 | c: str
20 |
21 |
22 | @strawberry.type
23 | class Qux:
24 | d: str
25 |
26 |
27 | # this is the union type
28 |
29 | Union1 = Annotated[Foo | Bar | Baz | Qux, strawberry.union(name="Union1")]
30 | Union2 = Annotated[Baz | Qux, strawberry.union(name="Union2")]
31 |
--------------------------------------------------------------------------------
/tests/cli/snapshots/unions_py38.py:
--------------------------------------------------------------------------------
1 | import strawberry
2 | from typing import Annotated, Union
3 |
4 | # create a few types and then a union type
5 |
6 |
7 | @strawberry.type
8 | class Foo:
9 | a: str
10 |
11 |
12 | @strawberry.type
13 | class Bar:
14 | b: str
15 |
16 |
17 | @strawberry.type
18 | class Baz:
19 | c: str
20 |
21 |
22 | @strawberry.type
23 | class Qux:
24 | d: str
25 |
26 |
27 | # this is the union type
28 |
29 | Union1 = Annotated[Union[Foo, Bar, Baz, Qux], strawberry.union(name="Union1")]
30 | Union2 = Annotated[Union[Baz, Qux], strawberry.union(name="Union2")]
31 |
--------------------------------------------------------------------------------
/tests/cli/snapshots/unions_typing_extension.py:
--------------------------------------------------------------------------------
1 | import strawberry
2 | from typing_extensions import Annotated
3 |
4 | # create a few types and then a union type
5 |
6 |
7 | @strawberry.type
8 | class Foo:
9 | a: str
10 |
11 |
12 | @strawberry.type
13 | class Bar:
14 | b: str
15 |
16 |
17 | @strawberry.type
18 | class Baz:
19 | c: str
20 |
21 |
22 | @strawberry.type
23 | class Qux:
24 | d: str
25 |
26 |
27 | # this is the union type
28 |
29 | Union1 = Annotated[Foo | Bar | Baz | Qux, strawberry.union(name="Union1")]
30 | Union2 = Annotated[Baz | Qux, strawberry.union(name="Union2")]
31 |
--------------------------------------------------------------------------------
/tests/codegen/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/codegen/__init__.py
--------------------------------------------------------------------------------
/tests/codegen/lazy_type.py:
--------------------------------------------------------------------------------
1 | import strawberry
2 |
3 |
4 | @strawberry.type
5 | class LaziestType:
6 | something: bool
7 |
--------------------------------------------------------------------------------
/tests/codegen/queries/alias.graphql:
--------------------------------------------------------------------------------
1 | query OperationName {
2 | id
3 | second_id: id
4 | a_float: float
5 | lazy {
6 | lazy: something
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/tests/codegen/queries/basic.graphql:
--------------------------------------------------------------------------------
1 | query OperationName {
2 | id
3 | integer
4 | float
5 | boolean
6 | uuid
7 | date
8 | datetime
9 | time
10 | decimal
11 | lazy {
12 | something
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/tests/codegen/queries/custom_scalar.graphql:
--------------------------------------------------------------------------------
1 | query OperationName {
2 | json
3 | }
4 |
--------------------------------------------------------------------------------
/tests/codegen/queries/enum.graphql:
--------------------------------------------------------------------------------
1 | query OperationName {
2 | enum
3 | }
4 |
--------------------------------------------------------------------------------
/tests/codegen/queries/fragment.graphql:
--------------------------------------------------------------------------------
1 | fragment PersonName on Person {
2 | name
3 | }
4 |
5 | query OperationName {
6 | person {
7 | ...PersonName
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/tests/codegen/queries/generic_types.graphql:
--------------------------------------------------------------------------------
1 | query ListLifeGeneric {
2 | listLife {
3 | items1 {
4 | name
5 | age
6 | }
7 | items2 {
8 | name
9 | age
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/tests/codegen/queries/interface.graphql:
--------------------------------------------------------------------------------
1 | query OperationName {
2 | interface {
3 | id
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/tests/codegen/queries/interface_fragments.graphql:
--------------------------------------------------------------------------------
1 | query OperationName {
2 | interface {
3 | id
4 | ... on BlogPost {
5 | title
6 | }
7 | ... on Image {
8 | url
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/tests/codegen/queries/interface_fragments_with_spread.graphql:
--------------------------------------------------------------------------------
1 | fragment PartialBlogPost on BlogPost {
2 | title
3 | }
4 |
5 | query OperationName {
6 | interface {
7 | id
8 | ... on BlogPost {
9 | ...PartialBlogPost
10 | }
11 | ... on Image {
12 | url
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/tests/codegen/queries/interface_single_fragment.graphql:
--------------------------------------------------------------------------------
1 | query OperationName {
2 | interface {
3 | id
4 | ... on BlogPost {
5 | title
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/tests/codegen/queries/multiple_types.graphql:
--------------------------------------------------------------------------------
1 | query OperationName {
2 | person {
3 | name
4 | }
5 | listOfPeople {
6 | name
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/tests/codegen/queries/multiple_types_optional.graphql:
--------------------------------------------------------------------------------
1 | query OperationName {
2 | optionalPerson {
3 | name
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/tests/codegen/queries/mutation-fragment.graphql:
--------------------------------------------------------------------------------
1 | fragment IdFragment on BlogPost {
2 | id
3 | }
4 |
5 | mutation addBook($input: String!) {
6 | addBook(input: $input) {
7 | ...IdFragment
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/tests/codegen/queries/mutation.graphql:
--------------------------------------------------------------------------------
1 | mutation addBook($input: String!) {
2 | addBook(input: $input) {
3 | id
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/tests/codegen/queries/mutation_with_object.graphql:
--------------------------------------------------------------------------------
1 | mutation AddBlogPosts($input: [BlogPostInput!]!) {
2 | addBlogPosts(input: {posts: $input}) {
3 | posts {
4 | title
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/tests/codegen/queries/nullable_list_of_non_scalars.graphql:
--------------------------------------------------------------------------------
1 | query OperationName {
2 | optionalListOfPeople {
3 | name
4 | age
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/tests/codegen/queries/optional_and_lists.graphql:
--------------------------------------------------------------------------------
1 | query OperationName {
2 | optionalInt
3 | listOfInt
4 | listOfOptionalInt
5 | optionalListOfOptionalInt
6 | }
7 |
--------------------------------------------------------------------------------
/tests/codegen/queries/union.graphql:
--------------------------------------------------------------------------------
1 | query OperationName {
2 | union {
3 | ... on Animal {
4 | age
5 | }
6 | ... on Person {
7 | name
8 | }
9 | }
10 | optionalUnion {
11 | ... on Animal {
12 | age
13 | }
14 | ... on Person {
15 | name
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/tests/codegen/queries/union_return.graphql:
--------------------------------------------------------------------------------
1 | query OperationName {
2 | getPersonOrAnimal {
3 | ... on Person {
4 | name
5 | age
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/tests/codegen/queries/union_with_one_type.graphql:
--------------------------------------------------------------------------------
1 | query OperationName {
2 | union {
3 | ... on Animal {
4 | age
5 | name
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/tests/codegen/queries/union_with_typename.graphql:
--------------------------------------------------------------------------------
1 | query OperationName {
2 | __typename
3 | union {
4 | ... on Animal {
5 | age
6 | }
7 | ... on Person {
8 | name
9 | }
10 | }
11 | optionalUnion {
12 | ... on Animal {
13 | age
14 | }
15 | ... on Person {
16 | name
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/tests/codegen/queries/union_with_typename_and_fragment.graphql:
--------------------------------------------------------------------------------
1 | fragment AnimalProjection on Animal {
2 | age
3 | }
4 |
5 | query OperationName {
6 | __typename
7 | union {
8 | ... on Animal {
9 | ...AnimalProjection
10 | }
11 | ... on Person {
12 | name
13 | }
14 | }
15 | optionalUnion {
16 | ... on Animal {
17 | ...AnimalProjection
18 | }
19 | ... on Person {
20 | name
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/tests/codegen/queries/variables.graphql:
--------------------------------------------------------------------------------
1 | query OperationName($id: ID, $input: ExampleInput!, $ids: [ID!]!, $ids2: [ID], $ids3: [[ID]]) {
2 | withInputs(id: $id, input: $input)
3 | }
4 |
--------------------------------------------------------------------------------
/tests/codegen/queries/with_directives.graphql:
--------------------------------------------------------------------------------
1 | query OperationName @owner(name: "Patrick", age: 30, items: [1, 2, 3], enum: NAME, bool: true) {
2 | person {
3 | name @root
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/python/alias.py:
--------------------------------------------------------------------------------
1 | class OperationNameResultLazy:
2 | # alias for something
3 | lazy: bool
4 |
5 | class OperationNameResult:
6 | id: str
7 | # alias for id
8 | second_id: str
9 | # alias for float
10 | a_float: float
11 | lazy: OperationNameResultLazy
12 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/python/basic.py:
--------------------------------------------------------------------------------
1 | from uuid import UUID
2 | from datetime import date, datetime, time
3 | from decimal import Decimal
4 |
5 | class OperationNameResultLazy:
6 | something: bool
7 |
8 | class OperationNameResult:
9 | id: str
10 | integer: int
11 | float: float
12 | boolean: bool
13 | uuid: UUID
14 | date: date
15 | datetime: datetime
16 | time: time
17 | decimal: Decimal
18 | lazy: OperationNameResultLazy
19 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/python/custom_scalar.py:
--------------------------------------------------------------------------------
1 | from typing import NewType
2 |
3 | JSON = NewType("JSON", str)
4 |
5 | class OperationNameResult:
6 | json: JSON
7 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/python/enum.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 |
3 | class Color(Enum):
4 | RED = "RED"
5 | GREEN = "GREEN"
6 | BLUE = "BLUE"
7 |
8 | class OperationNameResult:
9 | enum: Color
10 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/python/fragment.py:
--------------------------------------------------------------------------------
1 | class PersonName:
2 | # typename: Person
3 | name: str
4 |
5 | class OperationNameResult:
6 | person: PersonName
7 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/python/generic_types.py:
--------------------------------------------------------------------------------
1 | from typing import List
2 |
3 | class ListLifeGenericResultListLifeItems1:
4 | name: str
5 | age: int
6 |
7 | class ListLifeGenericResultListLifeItems2:
8 | name: str
9 | age: int
10 |
11 | class ListLifeGenericResultListLife:
12 | items1: list[ListLifeGenericResultListLifeItems1]
13 | items2: list[ListLifeGenericResultListLifeItems2]
14 |
15 | class ListLifeGenericResult:
16 | list_life: ListLifeGenericResultListLife
17 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/python/interface.py:
--------------------------------------------------------------------------------
1 | class OperationNameResultInterface:
2 | id: str
3 |
4 | class OperationNameResult:
5 | interface: OperationNameResultInterface
6 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/python/interface_fragments.py:
--------------------------------------------------------------------------------
1 | from typing import Union
2 |
3 | class OperationNameResultInterfaceBlogPost:
4 | # typename: BlogPost
5 | id: str
6 | title: str
7 |
8 | class OperationNameResultInterfaceImage:
9 | # typename: Image
10 | id: str
11 | url: str
12 |
13 | OperationNameResultInterface = Union[OperationNameResultInterfaceBlogPost, OperationNameResultInterfaceImage]
14 |
15 | class OperationNameResult:
16 | interface: OperationNameResultInterface
17 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/python/interface_fragments_with_spread.py:
--------------------------------------------------------------------------------
1 | from typing import Union
2 |
3 | class PartialBlogPost:
4 | # typename: BlogPost
5 | title: str
6 |
7 | class OperationNameResultInterfaceBlogPost:
8 | # typename: BlogPost
9 | id: str
10 | title: str
11 |
12 | class OperationNameResultInterfaceImage:
13 | # typename: Image
14 | id: str
15 | url: str
16 |
17 | OperationNameResultInterface = Union[OperationNameResultInterfaceBlogPost, OperationNameResultInterfaceImage]
18 |
19 | class OperationNameResult:
20 | interface: OperationNameResultInterface
21 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/python/interface_single_fragment.py:
--------------------------------------------------------------------------------
1 | class OperationNameResultInterfaceBlogPost:
2 | # typename: BlogPost
3 | id: str
4 | title: str
5 |
6 | class OperationNameResult:
7 | interface: OperationNameResultInterfaceBlogPost
8 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/python/multiple_types.py:
--------------------------------------------------------------------------------
1 | from typing import List
2 |
3 | class OperationNameResultPerson:
4 | name: str
5 |
6 | class OperationNameResultListOfPeople:
7 | name: str
8 |
9 | class OperationNameResult:
10 | person: OperationNameResultPerson
11 | list_of_people: list[OperationNameResultListOfPeople]
12 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/python/multiple_types_optional.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | class OperationNameResultOptionalPerson:
4 | name: str
5 |
6 | class OperationNameResult:
7 | optional_person: Optional[OperationNameResultOptionalPerson]
8 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/python/mutation-fragment.py:
--------------------------------------------------------------------------------
1 | class IdFragment:
2 | # typename: BlogPost
3 | id: str
4 |
5 | class addBookResult:
6 | add_book: IdFragment
7 |
8 | class addBookVariables:
9 | input: str
10 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/python/mutation.py:
--------------------------------------------------------------------------------
1 | class addBookResultAddBook:
2 | id: str
3 |
4 | class addBookResult:
5 | add_book: addBookResultAddBook
6 |
7 | class addBookVariables:
8 | input: str
9 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/python/mutation_with_object.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional
2 | from enum import Enum
3 |
4 | class AddBlogPostsResultAddBlogPostsPosts:
5 | title: str
6 |
7 | class AddBlogPostsResultAddBlogPosts:
8 | posts: list[AddBlogPostsResultAddBlogPostsPosts]
9 |
10 | class AddBlogPostsResult:
11 | add_blog_posts: AddBlogPostsResultAddBlogPosts
12 |
13 | class Color(Enum):
14 | RED = "RED"
15 | GREEN = "GREEN"
16 | BLUE = "BLUE"
17 |
18 | class BlogPostInput:
19 | title: str = "I replaced my doorbell. You wouldn't believe what happened next!"
20 | color: Color = Color.RED
21 | pi: float = 3.14159
22 | a_bool: bool = True
23 | an_int: int = 42
24 | an_optional_int: Optional[int] = None
25 |
26 | class AddBlogPostsVariables:
27 | input: list[BlogPostInput]
28 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/python/nullable_list_of_non_scalars.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional
2 |
3 | class OperationNameResultOptionalListOfPeople:
4 | name: str
5 | age: int
6 |
7 | class OperationNameResult:
8 | optional_list_of_people: Optional[list[OperationNameResultOptionalListOfPeople]]
9 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/python/optional_and_lists.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional
2 |
3 | class OperationNameResult:
4 | optional_int: Optional[int]
5 | list_of_int: list[int]
6 | list_of_optional_int: list[Optional[int]]
7 | optional_list_of_optional_int: Optional[list[Optional[int]]]
8 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/python/union.py:
--------------------------------------------------------------------------------
1 | from typing import Optional, Union
2 |
3 | class OperationNameResultUnionAnimal:
4 | # typename: Animal
5 | age: int
6 |
7 | class OperationNameResultUnionPerson:
8 | # typename: Person
9 | name: str
10 |
11 | OperationNameResultUnion = Union[OperationNameResultUnionAnimal, OperationNameResultUnionPerson]
12 |
13 | class OperationNameResultOptionalUnionAnimal:
14 | # typename: Animal
15 | age: int
16 |
17 | class OperationNameResultOptionalUnionPerson:
18 | # typename: Person
19 | name: str
20 |
21 | OperationNameResultOptionalUnion = Union[OperationNameResultOptionalUnionAnimal, OperationNameResultOptionalUnionPerson]
22 |
23 | class OperationNameResult:
24 | union: OperationNameResultUnion
25 | optional_union: Optional[OperationNameResultOptionalUnion]
26 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/python/union_return.py:
--------------------------------------------------------------------------------
1 | class OperationNameResultGetPersonOrAnimalPerson:
2 | # typename: Person
3 | name: str
4 | age: int
5 |
6 | class OperationNameResult:
7 | get_person_or_animal: OperationNameResultGetPersonOrAnimalPerson
8 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/python/union_with_one_type.py:
--------------------------------------------------------------------------------
1 | class OperationNameResultUnionAnimal:
2 | # typename: Animal
3 | age: int
4 | name: str
5 |
6 | class OperationNameResult:
7 | union: OperationNameResultUnionAnimal
8 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/python/union_with_typename.py:
--------------------------------------------------------------------------------
1 | from typing import Optional, Union
2 |
3 | class OperationNameResultUnionAnimal:
4 | # typename: Animal
5 | age: int
6 |
7 | class OperationNameResultUnionPerson:
8 | # typename: Person
9 | name: str
10 |
11 | OperationNameResultUnion = Union[OperationNameResultUnionAnimal, OperationNameResultUnionPerson]
12 |
13 | class OperationNameResultOptionalUnionAnimal:
14 | # typename: Animal
15 | age: int
16 |
17 | class OperationNameResultOptionalUnionPerson:
18 | # typename: Person
19 | name: str
20 |
21 | OperationNameResultOptionalUnion = Union[OperationNameResultOptionalUnionAnimal, OperationNameResultOptionalUnionPerson]
22 |
23 | class OperationNameResult:
24 | union: OperationNameResultUnion
25 | optional_union: Optional[OperationNameResultOptionalUnion]
26 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/python/union_with_typename_and_fragment.py:
--------------------------------------------------------------------------------
1 | from typing import Optional, Union
2 |
3 | class AnimalProjection:
4 | # typename: Animal
5 | age: int
6 |
7 | class OperationNameResultUnionPerson:
8 | # typename: Person
9 | name: str
10 |
11 | OperationNameResultUnion = Union[AnimalProjection, OperationNameResultUnionPerson]
12 |
13 | class OperationNameResultOptionalUnionPerson:
14 | # typename: Person
15 | name: str
16 |
17 | OperationNameResultOptionalUnion = Union[AnimalProjection, OperationNameResultOptionalUnionPerson]
18 |
19 | class OperationNameResult:
20 | union: OperationNameResultUnion
21 | optional_union: Optional[OperationNameResultOptionalUnion]
22 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/python/variables.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional
2 |
3 | class OperationNameResult:
4 | with_inputs: bool
5 |
6 | class PersonInput:
7 | name: str
8 | age: Optional[int] = None
9 |
10 | class ExampleInput:
11 | id: str
12 | name: str
13 | age: int
14 | person: Optional[PersonInput]
15 | people: list[PersonInput]
16 | optional_people: Optional[list[PersonInput]]
17 |
18 | class OperationNameVariables:
19 | id: Optional[str]
20 | input: ExampleInput
21 | ids: list[str]
22 | ids2: Optional[list[Optional[str]]]
23 | ids3: Optional[list[Optional[list[Optional[str]]]]]
24 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/python/with_directives.py:
--------------------------------------------------------------------------------
1 | class OperationNameResultPerson:
2 | name: str
3 |
4 | class OperationNameResult:
5 | person: OperationNameResultPerson
6 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/typescript/alias.ts:
--------------------------------------------------------------------------------
1 | type OperationNameResultLazy = {
2 | // alias for something
3 | lazy: boolean
4 | }
5 |
6 | type OperationNameResult = {
7 | id: string
8 | // alias for id
9 | second_id: string
10 | // alias for float
11 | a_float: number
12 | lazy: OperationNameResultLazy
13 | }
14 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/typescript/basic.ts:
--------------------------------------------------------------------------------
1 | type OperationNameResultLazy = {
2 | something: boolean
3 | }
4 |
5 | type OperationNameResult = {
6 | id: string
7 | integer: number
8 | float: number
9 | boolean: boolean
10 | uuid: string
11 | date: string
12 | datetime: string
13 | time: string
14 | decimal: string
15 | lazy: OperationNameResultLazy
16 | }
17 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/typescript/custom_scalar.ts:
--------------------------------------------------------------------------------
1 | type JSON = string
2 |
3 | type OperationNameResult = {
4 | json: JSON
5 | }
6 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/typescript/enum.ts:
--------------------------------------------------------------------------------
1 | enum Color {
2 | RED = "RED",
3 | GREEN = "GREEN",
4 | BLUE = "BLUE",
5 | }
6 |
7 | type OperationNameResult = {
8 | enum: Color
9 | }
10 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/typescript/fragment.ts:
--------------------------------------------------------------------------------
1 | type PersonName = {
2 | name: string
3 | }
4 |
5 | type OperationNameResult = {
6 | person: PersonName
7 | }
8 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/typescript/generic_types.ts:
--------------------------------------------------------------------------------
1 | type ListLifeGenericResultListLifeItems1 = {
2 | name: string
3 | age: number
4 | }
5 |
6 | type ListLifeGenericResultListLifeItems2 = {
7 | name: string
8 | age: number
9 | }
10 |
11 | type ListLifeGenericResultListLife = {
12 | items1: ListLifeGenericResultListLifeItems1[]
13 | items2: ListLifeGenericResultListLifeItems2[]
14 | }
15 |
16 | type ListLifeGenericResult = {
17 | list_life: ListLifeGenericResultListLife
18 | }
19 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/typescript/interface.ts:
--------------------------------------------------------------------------------
1 | type OperationNameResultInterface = {
2 | id: string
3 | }
4 |
5 | type OperationNameResult = {
6 | interface: OperationNameResultInterface
7 | }
8 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/typescript/interface_fragments.ts:
--------------------------------------------------------------------------------
1 | type OperationNameResultInterfaceBlogPost = {
2 | id: string
3 | title: string
4 | }
5 |
6 | type OperationNameResultInterfaceImage = {
7 | id: string
8 | url: string
9 | }
10 |
11 | type OperationNameResultInterface = OperationNameResultInterfaceBlogPost | OperationNameResultInterfaceImage
12 |
13 | type OperationNameResult = {
14 | interface: OperationNameResultInterface
15 | }
16 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/typescript/interface_fragments_with_spread.ts:
--------------------------------------------------------------------------------
1 | type PartialBlogPost = {
2 | title: string
3 | }
4 |
5 | type OperationNameResultInterfaceBlogPost = {
6 | id: string
7 | title: string
8 | }
9 |
10 | type OperationNameResultInterfaceImage = {
11 | id: string
12 | url: string
13 | }
14 |
15 | type OperationNameResultInterface = OperationNameResultInterfaceBlogPost | OperationNameResultInterfaceImage
16 |
17 | type OperationNameResult = {
18 | interface: OperationNameResultInterface
19 | }
20 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/typescript/interface_single_fragment.ts:
--------------------------------------------------------------------------------
1 | type OperationNameResultInterfaceBlogPost = {
2 | id: string
3 | title: string
4 | }
5 |
6 | type OperationNameResult = {
7 | interface: OperationNameResultInterfaceBlogPost
8 | }
9 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/typescript/multiple_types.ts:
--------------------------------------------------------------------------------
1 | type OperationNameResultPerson = {
2 | name: string
3 | }
4 |
5 | type OperationNameResultListOfPeople = {
6 | name: string
7 | }
8 |
9 | type OperationNameResult = {
10 | person: OperationNameResultPerson
11 | list_of_people: OperationNameResultListOfPeople[]
12 | }
13 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/typescript/multiple_types_optional.ts:
--------------------------------------------------------------------------------
1 | type OperationNameResultOptionalPerson = {
2 | name: string
3 | }
4 |
5 | type OperationNameResult = {
6 | optional_person: OperationNameResultOptionalPerson | undefined
7 | }
8 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/typescript/mutation-fragment.ts:
--------------------------------------------------------------------------------
1 | type IdFragment = {
2 | id: string
3 | }
4 |
5 | type addBookResult = {
6 | add_book: IdFragment
7 | }
8 |
9 | type addBookVariables = {
10 | input: string
11 | }
12 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/typescript/mutation.ts:
--------------------------------------------------------------------------------
1 | type addBookResultAddBook = {
2 | id: string
3 | }
4 |
5 | type addBookResult = {
6 | add_book: addBookResultAddBook
7 | }
8 |
9 | type addBookVariables = {
10 | input: string
11 | }
12 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/typescript/mutation_with_object.ts:
--------------------------------------------------------------------------------
1 | type AddBlogPostsResultAddBlogPostsPosts = {
2 | title: string
3 | }
4 |
5 | type AddBlogPostsResultAddBlogPosts = {
6 | posts: AddBlogPostsResultAddBlogPostsPosts[]
7 | }
8 |
9 | type AddBlogPostsResult = {
10 | add_blog_posts: AddBlogPostsResultAddBlogPosts
11 | }
12 |
13 | enum Color {
14 | RED = "RED",
15 | GREEN = "GREEN",
16 | BLUE = "BLUE",
17 | }
18 |
19 | type BlogPostInput = {
20 | title: string
21 | color: Color
22 | pi: number
23 | a_bool: boolean
24 | an_int: number
25 | an_optional_int: number | undefined
26 | }
27 |
28 | type AddBlogPostsVariables = {
29 | input: BlogPostInput[]
30 | }
31 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/typescript/nullable_list_of_non_scalars.ts:
--------------------------------------------------------------------------------
1 | type OperationNameResultOptionalListOfPeople = {
2 | name: string
3 | age: number
4 | }
5 |
6 | type OperationNameResult = {
7 | optional_list_of_people: OperationNameResultOptionalListOfPeople[] | undefined
8 | }
9 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/typescript/optional_and_lists.ts:
--------------------------------------------------------------------------------
1 | type OperationNameResult = {
2 | optional_int: number | undefined
3 | list_of_int: number[]
4 | list_of_optional_int: (number | undefined)[]
5 | optional_list_of_optional_int: (number | undefined)[] | undefined
6 | }
7 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/typescript/union.ts:
--------------------------------------------------------------------------------
1 | type OperationNameResultUnionAnimal = {
2 | age: number
3 | }
4 |
5 | type OperationNameResultUnionPerson = {
6 | name: string
7 | }
8 |
9 | type OperationNameResultUnion = OperationNameResultUnionAnimal | OperationNameResultUnionPerson
10 |
11 | type OperationNameResultOptionalUnionAnimal = {
12 | age: number
13 | }
14 |
15 | type OperationNameResultOptionalUnionPerson = {
16 | name: string
17 | }
18 |
19 | type OperationNameResultOptionalUnion = OperationNameResultOptionalUnionAnimal | OperationNameResultOptionalUnionPerson
20 |
21 | type OperationNameResult = {
22 | union: OperationNameResultUnion
23 | optional_union: OperationNameResultOptionalUnion | undefined
24 | }
25 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/typescript/union_return.ts:
--------------------------------------------------------------------------------
1 | type OperationNameResultGetPersonOrAnimalPerson = {
2 | name: string
3 | age: number
4 | }
5 |
6 | type OperationNameResult = {
7 | get_person_or_animal: OperationNameResultGetPersonOrAnimalPerson
8 | }
9 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/typescript/union_with_one_type.ts:
--------------------------------------------------------------------------------
1 | type OperationNameResultUnionAnimal = {
2 | age: number
3 | name: string
4 | }
5 |
6 | type OperationNameResult = {
7 | union: OperationNameResultUnionAnimal
8 | }
9 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/typescript/union_with_typename.ts:
--------------------------------------------------------------------------------
1 | type OperationNameResultUnionAnimal = {
2 | age: number
3 | }
4 |
5 | type OperationNameResultUnionPerson = {
6 | name: string
7 | }
8 |
9 | type OperationNameResultUnion = OperationNameResultUnionAnimal | OperationNameResultUnionPerson
10 |
11 | type OperationNameResultOptionalUnionAnimal = {
12 | age: number
13 | }
14 |
15 | type OperationNameResultOptionalUnionPerson = {
16 | name: string
17 | }
18 |
19 | type OperationNameResultOptionalUnion = OperationNameResultOptionalUnionAnimal | OperationNameResultOptionalUnionPerson
20 |
21 | type OperationNameResult = {
22 | __typename: string
23 | union: OperationNameResultUnion
24 | optional_union: OperationNameResultOptionalUnion | undefined
25 | }
26 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/typescript/union_with_typename_and_fragment.ts:
--------------------------------------------------------------------------------
1 | type AnimalProjection = {
2 | age: number
3 | }
4 |
5 | type OperationNameResultUnionPerson = {
6 | name: string
7 | }
8 |
9 | type OperationNameResultUnion = AnimalProjection | OperationNameResultUnionPerson
10 |
11 | type OperationNameResultOptionalUnionPerson = {
12 | name: string
13 | }
14 |
15 | type OperationNameResultOptionalUnion = AnimalProjection | OperationNameResultOptionalUnionPerson
16 |
17 | type OperationNameResult = {
18 | __typename: string
19 | union: OperationNameResultUnion
20 | optional_union: OperationNameResultOptionalUnion | undefined
21 | }
22 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/typescript/variables.ts:
--------------------------------------------------------------------------------
1 | type OperationNameResult = {
2 | with_inputs: boolean
3 | }
4 |
5 | type PersonInput = {
6 | name: string
7 | age: number | undefined
8 | }
9 |
10 | type ExampleInput = {
11 | id: string
12 | name: string
13 | age: number
14 | person: PersonInput | undefined
15 | people: PersonInput[]
16 | optional_people: PersonInput[] | undefined
17 | }
18 |
19 | type OperationNameVariables = {
20 | id: string | undefined
21 | input: ExampleInput
22 | ids: string[]
23 | ids2: (string | undefined)[] | undefined
24 | ids3: ((string | undefined)[] | undefined)[] | undefined
25 | }
26 |
--------------------------------------------------------------------------------
/tests/codegen/snapshots/typescript/with_directives.ts:
--------------------------------------------------------------------------------
1 | type OperationNameResultPerson = {
2 | name: string
3 | }
4 |
5 | type OperationNameResult = {
6 | person: OperationNameResultPerson
7 | }
8 |
--------------------------------------------------------------------------------
/tests/codegen/test_print_operation.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 |
3 | import pytest
4 |
5 | from strawberry.codegen import QueryCodegen
6 | from strawberry.codegen.plugins.print_operation import PrintOperationPlugin
7 |
8 | HERE = Path(__file__).parent
9 | QUERIES = list(HERE.glob("queries/*.graphql"))
10 |
11 |
12 | @pytest.mark.parametrize("query", QUERIES, ids=[x.name for x in QUERIES])
13 | def test_codegen(
14 | query: Path,
15 | schema,
16 | ):
17 | generator = QueryCodegen(schema, plugins=[PrintOperationPlugin(query)])
18 | query_content = query.read_text()
19 |
20 | result = generator.run(query_content)
21 |
22 | assert result.to_string() == query_content
23 |
--------------------------------------------------------------------------------
/tests/codemods/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/codemods/__init__.py
--------------------------------------------------------------------------------
/tests/d.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from typing import TYPE_CHECKING, Annotated
4 |
5 | import strawberry
6 |
7 | if TYPE_CHECKING:
8 | from tests.c import C
9 |
10 |
11 | @strawberry.type
12 | class D:
13 | id: strawberry.ID
14 |
15 | @strawberry.field
16 | async def c_list(
17 | self,
18 | ) -> list[Annotated[C, strawberry.lazy("tests.c")]]: # pragma: no cover
19 | from tests.c import C
20 |
21 | return [C(id=self.id)]
22 |
--------------------------------------------------------------------------------
/tests/django/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/django/__init__.py
--------------------------------------------------------------------------------
/tests/django/app/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/django/app/__init__.py
--------------------------------------------------------------------------------
/tests/django/app/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 |
4 | class Example(models.Model): # noqa: DJ008
5 | name = models.CharField(max_length=100)
6 |
--------------------------------------------------------------------------------
/tests/django/app/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from strawberry.django.views import GraphQLView as BaseGraphQLView
4 | from tests.views.schema import Query, schema
5 |
6 |
7 | class GraphQLView(BaseGraphQLView):
8 | def get_root_value(self, request) -> Query:
9 | return Query()
10 |
11 |
12 | urlpatterns = [
13 | path("graphql/", GraphQLView.as_view(schema=schema)),
14 | ]
15 |
--------------------------------------------------------------------------------
/tests/django/conftest.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from typing import TYPE_CHECKING
4 |
5 | import pytest
6 |
7 | if TYPE_CHECKING:
8 | from strawberry.django.test import GraphQLTestClient
9 |
10 |
11 | @pytest.fixture
12 | def graphql_client() -> GraphQLTestClient:
13 | from django.test.client import Client
14 |
15 | from strawberry.django.test import GraphQLTestClient
16 |
17 | return GraphQLTestClient(Client())
18 |
--------------------------------------------------------------------------------
/tests/django/django_settings.py:
--------------------------------------------------------------------------------
1 | SECRET_KEY = 1
2 |
3 | INSTALLED_APPS = ["tests.django.app"]
4 | ROOT_URLCONF = "tests.django.app.urls"
5 |
6 | TEMPLATES = [
7 | {
8 | "BACKEND": "django.template.backends.django.DjangoTemplates",
9 | "DIRS": [],
10 | "APP_DIRS": True,
11 | }
12 | ]
13 |
14 | DATABASES = {"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}}
15 |
16 | # This is for channels integration, but only one django settings can be used
17 | # per pytest_django settings
18 | CHANNEL_LAYERS = {"default": {"BACKEND": "channels.layers.InMemoryChannelLayer"}}
19 |
--------------------------------------------------------------------------------
/tests/django/test_extensions.py:
--------------------------------------------------------------------------------
1 | def test_extensions(graphql_client):
2 | query = "{ hello }"
3 |
4 | response = graphql_client.query(query)
5 |
6 | assert response.extensions["example"] == "example"
7 |
--------------------------------------------------------------------------------
/tests/enums/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/enums/__init__.py
--------------------------------------------------------------------------------
/tests/exceptions/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/exceptions/__init__.py
--------------------------------------------------------------------------------
/tests/exceptions/classes/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/exceptions/classes/__init__.py
--------------------------------------------------------------------------------
/tests/exceptions/test_exception_source.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from pathlib import Path
3 |
4 | import pytest
5 |
6 | from strawberry.exceptions.exception_source import ExceptionSource
7 |
8 | pytestmark = pytest.mark.skipif(
9 | sys.platform == "win32", reason="Test is meant to run on Unix systems"
10 | )
11 |
12 |
13 | def test_returns_relative_path(mocker):
14 | mocker.patch.object(Path, "cwd", return_value="/home/user/project/")
15 |
16 | source = ExceptionSource(
17 | path=Path("/home/user/project/src/main.py"),
18 | code="",
19 | start_line=1,
20 | end_line=1,
21 | error_line=1,
22 | error_column=1,
23 | error_column_end=1,
24 | )
25 |
26 | assert source.path_relative_to_cwd == Path("src/main.py")
27 |
28 |
29 | def test_returns_relative_path_when_is_already_relative():
30 | source = ExceptionSource(
31 | path=Path("src/main.py"),
32 | code="",
33 | start_line=1,
34 | end_line=1,
35 | error_line=1,
36 | error_column=1,
37 | error_column_end=1,
38 | )
39 |
40 | assert source.path_relative_to_cwd == Path("src/main.py")
41 |
--------------------------------------------------------------------------------
/tests/experimental/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/experimental/__init__.py
--------------------------------------------------------------------------------
/tests/experimental/pydantic/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/experimental/pydantic/__init__.py
--------------------------------------------------------------------------------
/tests/experimental/pydantic/schema/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/experimental/pydantic/schema/__init__.py
--------------------------------------------------------------------------------
/tests/experimental/pydantic/schema/test_forward_reference.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import textwrap
4 | from typing import Optional
5 |
6 | import pydantic
7 |
8 | import strawberry
9 |
10 |
11 | def test_auto_fields():
12 | global User
13 |
14 | class UserModel(pydantic.BaseModel):
15 | age: int
16 | password: Optional[str]
17 | other: float
18 |
19 | @strawberry.experimental.pydantic.type(UserModel)
20 | class User:
21 | age: strawberry.auto
22 | password: strawberry.auto
23 |
24 | @strawberry.type
25 | class Query:
26 | @strawberry.field
27 | def user(self) -> User:
28 | return User(age=1, password="ABC")
29 |
30 | schema = strawberry.Schema(query=Query)
31 |
32 | expected_schema = """
33 | type Query {
34 | user: User!
35 | }
36 |
37 | type User {
38 | age: Int!
39 | password: String
40 | }
41 | """
42 |
43 | assert str(schema) == textwrap.dedent(expected_schema).strip()
44 |
45 | query = "{ user { age } }"
46 |
47 | result = schema.execute_sync(query)
48 |
49 | assert not result.errors
50 | assert result.data["user"]["age"] == 1
51 |
--------------------------------------------------------------------------------
/tests/experimental/pydantic/utils.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from strawberry.experimental.pydantic._compat import IS_PYDANTIC_V2
4 |
5 | needs_pydantic_v2 = pytest.mark.skipif(
6 | not IS_PYDANTIC_V2, reason="requires Pydantic v2"
7 | )
8 | needs_pydantic_v1 = pytest.mark.skipif(IS_PYDANTIC_V2, reason="requires Pydantic v1")
9 |
--------------------------------------------------------------------------------
/tests/extensions/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/extensions/__init__.py
--------------------------------------------------------------------------------
/tests/extensions/tracing/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/extensions/tracing/__init__.py
--------------------------------------------------------------------------------
/tests/fastapi/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/fastapi/__init__.py
--------------------------------------------------------------------------------
/tests/fastapi/app.py:
--------------------------------------------------------------------------------
1 | from typing import Any, Union
2 |
3 | from fastapi import BackgroundTasks, Depends, FastAPI, Request, WebSocket
4 | from strawberry.fastapi import GraphQLRouter
5 | from tests.views.schema import schema
6 |
7 |
8 | def custom_context_dependency() -> str:
9 | return "Hi!"
10 |
11 |
12 | async def get_context(
13 | background_tasks: BackgroundTasks,
14 | request: Request = None,
15 | ws: WebSocket = None,
16 | custom_value=Depends(custom_context_dependency),
17 | ) -> dict[str, Any]:
18 | return {
19 | "custom_value": custom_value,
20 | "request": request or ws,
21 | "background_tasks": background_tasks,
22 | }
23 |
24 |
25 | async def get_root_value(
26 | request: Request = None, ws: WebSocket = None
27 | ) -> Union[Request, WebSocket]:
28 | return request or ws
29 |
30 |
31 | def create_app(schema=schema, **kwargs: Any) -> FastAPI:
32 | app = FastAPI()
33 |
34 | graphql_app = GraphQLRouter(
35 | schema, context_getter=get_context, root_value_getter=get_root_value, **kwargs
36 | )
37 | app.include_router(graphql_app, prefix="/graphql")
38 |
39 | return app
40 |
--------------------------------------------------------------------------------
/tests/fastapi/test_async.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from typing import TYPE_CHECKING, Optional
4 |
5 | import pytest
6 |
7 | import strawberry
8 |
9 | if TYPE_CHECKING:
10 | from starlette.testclient import TestClient
11 |
12 |
13 | @pytest.fixture
14 | def test_client() -> TestClient:
15 | from starlette.testclient import TestClient
16 |
17 | from tests.fastapi.app import create_app
18 |
19 | @strawberry.type
20 | class Query:
21 | @strawberry.field
22 | async def hello(self, name: Optional[str] = None) -> str:
23 | return f"Hello {name or 'world'}"
24 |
25 | async_schema = strawberry.Schema(Query)
26 | app = create_app(schema=async_schema)
27 | return TestClient(app)
28 |
29 |
30 | def test_simple_query(test_client: TestClient):
31 | response = test_client.post("/graphql", json={"query": "{ hello }"})
32 |
33 | assert response.json() == {"data": {"hello": "Hello world"}}
34 |
--------------------------------------------------------------------------------
/tests/federation/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/federation/__init__.py
--------------------------------------------------------------------------------
/tests/federation/printer/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/federation/printer/__init__.py
--------------------------------------------------------------------------------
/tests/federation/printer/test_interface_object.py:
--------------------------------------------------------------------------------
1 | import textwrap
2 |
3 | import strawberry
4 |
5 |
6 | def test_interface_object():
7 | @strawberry.federation.interface_object(keys=["id"])
8 | class SomeInterface:
9 | id: strawberry.ID
10 |
11 | schema = strawberry.federation.Schema(
12 | types=[SomeInterface], enable_federation_2=True
13 | )
14 |
15 | expected = """
16 | schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@interfaceObject", "@key"]) {
17 | query: Query
18 | }
19 |
20 | type Query {
21 | _entities(representations: [_Any!]!): [_Entity]!
22 | _service: _Service!
23 | }
24 |
25 | type SomeInterface @key(fields: "id") @interfaceObject {
26 | id: ID!
27 | }
28 |
29 | scalar _Any
30 |
31 | union _Entity = SomeInterface
32 |
33 | type _Service {
34 | sdl: String!
35 | }
36 | """
37 |
38 | assert schema.as_str() == textwrap.dedent(expected).strip()
39 |
--------------------------------------------------------------------------------
/tests/federation/printer/test_one_of.py:
--------------------------------------------------------------------------------
1 | import textwrap
2 | from typing import Optional
3 |
4 | import strawberry
5 |
6 |
7 | def test_prints_one_of_directive():
8 | @strawberry.federation.input(one_of=True, tags=["myTag", "anotherTag"])
9 | class Input:
10 | a: Optional[str] = strawberry.UNSET
11 | b: Optional[int] = strawberry.UNSET
12 |
13 | @strawberry.federation.type
14 | class Query:
15 | hello: str
16 |
17 | schema = strawberry.federation.Schema(
18 | query=Query, types=[Input], enable_federation_2=True
19 | )
20 |
21 | expected = """
22 | directive @oneOf on INPUT_OBJECT
23 |
24 | schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@tag"]) {
25 | query: Query
26 | }
27 |
28 | input Input @tag(name: "myTag") @tag(name: "anotherTag") @oneOf {
29 | a: String
30 | b: Int
31 | }
32 |
33 | type Query {
34 | _service: _Service!
35 | hello: String!
36 | }
37 |
38 | scalar _Any
39 |
40 | type _Service {
41 | sdl: String!
42 | }
43 | """
44 |
45 | assert schema.as_str() == textwrap.dedent(expected).strip()
46 |
--------------------------------------------------------------------------------
/tests/federation/test_types.py:
--------------------------------------------------------------------------------
1 | import strawberry
2 |
3 |
4 | def test_type():
5 | @strawberry.federation.type(keys=["id"])
6 | class Location:
7 | id: strawberry.ID
8 |
9 | assert Location(id=strawberry.ID("1")).id == "1"
10 |
11 |
12 | def test_type_and_override():
13 | @strawberry.federation.type(keys=["id"])
14 | class Location:
15 | id: strawberry.ID
16 | address: str = strawberry.federation.field(override="start")
17 |
18 | location = Location(id=strawberry.ID("1"), address="ABC")
19 |
20 | assert location.id == "1"
21 | assert location.address == "ABC"
22 |
23 |
24 | def test_type_and_override_with_resolver():
25 | @strawberry.federation.type(keys=["id"])
26 | class Location:
27 | id: strawberry.ID
28 | address: str = strawberry.federation.field(
29 | override="start", resolver=lambda: "ABC"
30 | )
31 |
32 | location = Location(id=strawberry.ID("1"))
33 |
34 | assert location.id == "1"
35 |
--------------------------------------------------------------------------------
/tests/fields/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/fields/__init__.py
--------------------------------------------------------------------------------
/tests/fields/test_field_basics.py:
--------------------------------------------------------------------------------
1 | import strawberry
2 |
3 |
4 | def test_type_add_type_definition_with_fields():
5 | @strawberry.type
6 | class Query:
7 | name: str
8 | age: int
9 |
10 | definition = Query.__strawberry_definition__
11 |
12 | assert definition.name == "Query"
13 | assert len(definition.fields) == 2
14 |
15 | assert definition.fields[0].python_name == "name"
16 | assert definition.fields[0].type is str
17 |
18 | assert definition.fields[1].python_name == "age"
19 | assert definition.fields[1].type is int
20 |
21 |
22 | def test_passing_nothing_to_fields():
23 | @strawberry.type
24 | class Query:
25 | name: str = strawberry.field()
26 | age: int = strawberry.field()
27 |
28 | definition = Query.__strawberry_definition__
29 |
30 | assert definition.name == "Query"
31 | assert len(definition.fields) == 2
32 |
33 | assert definition.fields[0].python_name == "name"
34 | assert definition.fields[0].type is str
35 |
36 | assert definition.fields[1].python_name == "age"
37 | assert definition.fields[1].type is int
38 |
--------------------------------------------------------------------------------
/tests/fields/test_field_descriptions.py:
--------------------------------------------------------------------------------
1 | import strawberry
2 |
3 |
4 | def test_field_descriptions():
5 | description = "this description is super cool"
6 |
7 | field = strawberry.field(description=description)
8 |
9 | assert field.description == description
10 |
--------------------------------------------------------------------------------
/tests/fields/test_field_names.py:
--------------------------------------------------------------------------------
1 | import strawberry
2 |
3 |
4 | def test_field_name_standard():
5 | standard_field = strawberry.field()
6 |
7 | assert standard_field.python_name is None
8 | assert standard_field.graphql_name is None
9 |
10 |
11 | def test_field_name_standard_on_schema():
12 | @strawberry.type()
13 | class Query:
14 | normal_field: int
15 |
16 | [field] = Query.__strawberry_definition__.fields
17 |
18 | assert field.python_name == "normal_field"
19 | assert field.graphql_name is None
20 |
21 |
22 | def test_field_name_override():
23 | field_name = "override"
24 |
25 | standard_field = strawberry.field(name=field_name)
26 |
27 | assert standard_field.python_name is None # Set once field is added to a Schema
28 | assert standard_field.graphql_name == field_name
29 |
30 |
31 | def test_field_name_override_with_schema():
32 | field_name = "override_name"
33 |
34 | @strawberry.type()
35 | class Query:
36 | override_field: bool = strawberry.field(name=field_name)
37 |
38 | [field] = Query.__strawberry_definition__.fields
39 |
40 | assert field.python_name == "override_field"
41 | assert field.graphql_name == field_name
42 |
--------------------------------------------------------------------------------
/tests/file_uploads/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/file_uploads/__init__.py
--------------------------------------------------------------------------------
/tests/fixtures/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/fixtures/__init__.py
--------------------------------------------------------------------------------
/tests/fixtures/sample_package/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/fixtures/sample_package/__init__.py
--------------------------------------------------------------------------------
/tests/fixtures/sample_package/sample_module.py:
--------------------------------------------------------------------------------
1 | import strawberry
2 |
3 |
4 | class SampleClass:
5 | def __init__(self, schema):
6 | self.schema = schema
7 |
8 |
9 | @strawberry.type
10 | class User:
11 | name: str
12 | age: int
13 |
14 |
15 | @strawberry.type
16 | class Query:
17 | @strawberry.field
18 | def user(self) -> User:
19 | return User(name="Patrick", age=100)
20 |
21 |
22 | def create_schema():
23 | return strawberry.Schema(query=Query)
24 |
25 |
26 | schema = create_schema()
27 | sample_instance = SampleClass(schema)
28 | not_a_schema = 42
29 |
--------------------------------------------------------------------------------
/tests/http/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/http/__init__.py
--------------------------------------------------------------------------------
/tests/http/clients/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/http/clients/__init__.py
--------------------------------------------------------------------------------
/tests/http/context.py:
--------------------------------------------------------------------------------
1 | def get_context(context: object) -> dict[str, object]:
2 | assert isinstance(context, dict)
3 |
4 | return {**context, "custom_value": "a value from context"}
5 |
--------------------------------------------------------------------------------
/tests/http/test_http.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from strawberry.http.base import BaseView
4 |
5 | from .clients.base import HttpClient
6 |
7 |
8 | @pytest.mark.parametrize("method", ["delete", "head", "put", "patch"])
9 | async def test_does_only_allow_get_and_post(
10 | method: str,
11 | http_client: HttpClient,
12 | ):
13 | response = await http_client.request(url="/graphql", method=method) # type: ignore
14 |
15 | assert response.status_code == 405
16 |
17 |
18 | async def test_the_http_handler_uses_the_views_decode_json_method(
19 | http_client: HttpClient, mocker
20 | ):
21 | spy = mocker.spy(BaseView, "decode_json")
22 |
23 | response = await http_client.query(query="{ hello }")
24 | assert response.status_code == 200
25 |
26 | data = response.json["data"]
27 | assert isinstance(data, dict)
28 | assert data["hello"] == "Hello world"
29 |
30 | assert spy.call_count == 1
31 |
--------------------------------------------------------------------------------
/tests/http/test_mutation.py:
--------------------------------------------------------------------------------
1 | from .clients.base import HttpClient
2 |
3 |
4 | async def test_mutation(http_client: HttpClient):
5 | response = await http_client.query(
6 | query="mutation { hello }",
7 | headers={
8 | "Content-Type": "application/json",
9 | },
10 | )
11 | data = response.json["data"]
12 |
13 | assert response.status_code == 200
14 | assert data["hello"] == "strawberry"
15 |
--------------------------------------------------------------------------------
/tests/http/test_process_result.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from typing_extensions import Literal
4 |
5 | import pytest
6 |
7 | from strawberry.http import GraphQLHTTPResponse
8 | from strawberry.types import ExecutionResult
9 |
10 | from .clients.base import HttpClient
11 |
12 |
13 | def process_result(result: ExecutionResult) -> GraphQLHTTPResponse:
14 | if result.data:
15 | return {
16 | "data": {key.upper(): result for key, result in result.data.items()},
17 | }
18 |
19 | return {}
20 |
21 |
22 | @pytest.fixture
23 | def http_client(http_client_class) -> HttpClient:
24 | return http_client_class(result_override=process_result)
25 |
26 |
27 | @pytest.mark.parametrize("method", ["get", "post"])
28 | async def test_custom_process_result(
29 | method: Literal["get", "post"], http_client: HttpClient
30 | ):
31 | response = await http_client.query(
32 | method=method,
33 | query="{ hello }",
34 | )
35 | assert response.json["data"] == {"HELLO": "Hello world"}
36 |
--------------------------------------------------------------------------------
/tests/litestar/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/litestar/__init__.py
--------------------------------------------------------------------------------
/tests/litestar/app.py:
--------------------------------------------------------------------------------
1 | from typing import Any
2 |
3 | from litestar import Litestar, Request
4 | from litestar.di import Provide
5 | from strawberry.litestar import make_graphql_controller
6 | from tests.views.schema import schema
7 |
8 |
9 | def custom_context_dependency() -> str:
10 | return "Hi!"
11 |
12 |
13 | async def get_root_value(request: Request = None):
14 | return request
15 |
16 |
17 | async def get_context(app_dependency: str, request: Request = None):
18 | return {"custom_value": app_dependency, "request": request}
19 |
20 |
21 | def create_app(schema=schema, **kwargs: Any):
22 | GraphQLController = make_graphql_controller(
23 | schema,
24 | path="/graphql",
25 | context_getter=get_context,
26 | root_value_getter=get_root_value,
27 | **kwargs,
28 | )
29 |
30 | return Litestar(
31 | route_handlers=[GraphQLController],
32 | dependencies={
33 | "app_dependency": Provide(custom_context_dependency, sync_to_thread=True)
34 | },
35 | )
36 |
--------------------------------------------------------------------------------
/tests/litestar/conftest.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 |
4 | @pytest.fixture
5 | def test_client():
6 | from litestar.testing import TestClient
7 | from tests.litestar.app import create_app
8 |
9 | app = create_app()
10 | return TestClient(app)
11 |
12 |
13 | @pytest.fixture
14 | def test_client_keep_alive():
15 | from litestar.testing import TestClient
16 | from tests.litestar.app import create_app
17 |
18 | app = create_app(keep_alive=True, keep_alive_interval=0.1)
19 | return TestClient(app)
20 |
--------------------------------------------------------------------------------
/tests/objects/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/objects/__init__.py
--------------------------------------------------------------------------------
/tests/objects/generics/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/objects/generics/__init__.py
--------------------------------------------------------------------------------
/tests/objects/test_inheritance.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | import strawberry
4 |
5 |
6 | def test_inherited_fields():
7 | @strawberry.type
8 | class A:
9 | a: str = strawberry.field(default="")
10 |
11 | @strawberry.type
12 | class B(A):
13 | b: Optional[str] = strawberry.field(default=None)
14 |
15 | assert strawberry.Schema(query=B)
16 |
--------------------------------------------------------------------------------
/tests/objects/test_object_instantiation.py:
--------------------------------------------------------------------------------
1 | import strawberry
2 |
3 |
4 | def test_can_instantiate_types_directly():
5 | @strawberry.type
6 | class User:
7 | username: str
8 |
9 | @strawberry.field
10 | def email(self) -> str:
11 | return self.username + "@somesite.com"
12 |
13 | user = User(username="abc")
14 | assert user.username == "abc"
15 | assert user.email() == "abc@somesite.com"
16 |
--------------------------------------------------------------------------------
/tests/plugins/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/plugins/__init__.py
--------------------------------------------------------------------------------
/tests/python_312/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/python_312/__init__.py
--------------------------------------------------------------------------------
/tests/relay/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/relay/__init__.py
--------------------------------------------------------------------------------
/tests/sanic/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/sanic/__init__.py
--------------------------------------------------------------------------------
/tests/schema/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/schema/__init__.py
--------------------------------------------------------------------------------
/tests/schema/extensions/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/schema/extensions/__init__.py
--------------------------------------------------------------------------------
/tests/schema/extensions/schema_extensions/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/schema/extensions/schema_extensions/__init__.py
--------------------------------------------------------------------------------
/tests/schema/extensions/test_imports.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 |
4 | def test_can_import(mocker):
5 | # mocking sys.modules.ddtrace so we don't get an ImportError
6 | mocker.patch.dict("sys.modules", ddtrace=mocker.MagicMock())
7 |
8 |
9 | def test_fails_if_import_is_not_found():
10 | with pytest.raises(ImportError):
11 | from strawberry.extensions.tracing import Blueberry # noqa: F401
12 |
--------------------------------------------------------------------------------
/tests/schema/extensions/test_input_mutation_future.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import textwrap
4 | from uuid import UUID
5 |
6 | import strawberry
7 | from strawberry.field_extensions import InputMutationExtension
8 |
9 |
10 | @strawberry.type
11 | class Query:
12 | @strawberry.field
13 | async def hello(self) -> str:
14 | return "hi"
15 |
16 |
17 | @strawberry.type
18 | class Mutation:
19 | @strawberry.mutation(extensions=[InputMutationExtension()])
20 | async def buggy(self, some_id: UUID) -> None:
21 | del some_id
22 |
23 |
24 | def test_schema():
25 | schema = strawberry.Schema(query=Query, mutation=Mutation)
26 |
27 | expected_schema = '''
28 | input BuggyInput {
29 | someId: UUID!
30 | }
31 |
32 | type Mutation {
33 | buggy(
34 | """Input data for `buggy` mutation"""
35 | input: BuggyInput!
36 | ): Void
37 | }
38 |
39 | type Query {
40 | hello: String!
41 | }
42 |
43 | scalar UUID
44 |
45 | """Represents NULL values"""
46 | scalar Void
47 | '''
48 |
49 | assert textwrap.dedent(expected_schema).strip() == str(schema).strip()
50 |
--------------------------------------------------------------------------------
/tests/schema/test_annotated/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/schema/test_annotated/__init__.py
--------------------------------------------------------------------------------
/tests/schema/test_annotated/type_a.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from typing import Annotated, Optional
4 | from uuid import UUID
5 |
6 | import strawberry
7 |
8 |
9 | @strawberry.type
10 | class Query:
11 | @strawberry.field
12 | def get_testing(
13 | self,
14 | info: strawberry.Info,
15 | id_: Annotated[UUID, strawberry.argument(name="id")],
16 | ) -> Optional[str]: ...
17 |
--------------------------------------------------------------------------------
/tests/schema/test_annotated/type_b.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from typing import Annotated, Optional
4 | from uuid import UUID
5 |
6 | import strawberry
7 |
8 |
9 | @strawberry.type
10 | class Query:
11 | @strawberry.field
12 | def get_testing(
13 | self,
14 | id_: Annotated[UUID, strawberry.argument(name="id")],
15 | info: strawberry.Info,
16 | ) -> Optional[str]: ...
17 |
--------------------------------------------------------------------------------
/tests/schema/test_config.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from strawberry.schema.config import StrawberryConfig
4 | from strawberry.types.info import Info
5 |
6 |
7 | def test_config_post_init_auto_camel_case():
8 | config = StrawberryConfig(auto_camel_case=True)
9 |
10 | assert config.name_converter.auto_camel_case is True
11 |
12 |
13 | def test_config_post_init_no_auto_camel_case():
14 | config = StrawberryConfig(auto_camel_case=False)
15 |
16 | assert config.name_converter.auto_camel_case is False
17 |
18 |
19 | def test_config_post_init_info_class():
20 | class CustomInfo(Info):
21 | test: str = "foo"
22 |
23 | config = StrawberryConfig(info_class=CustomInfo)
24 |
25 | assert config.info_class is CustomInfo
26 | assert config.info_class.test == "foo"
27 |
28 |
29 | def test_config_post_init_info_class_is_default():
30 | config = StrawberryConfig()
31 |
32 | assert config.info_class is Info
33 |
34 |
35 | def test_config_post_init_info_class_is_not_subclass():
36 | with pytest.raises(TypeError) as exc_info:
37 | StrawberryConfig(info_class=object)
38 |
39 | assert str(exc_info.value) == "`info_class` must be a subclass of strawberry.Info"
40 |
--------------------------------------------------------------------------------
/tests/schema/test_dataloaders.py:
--------------------------------------------------------------------------------
1 | from dataclasses import dataclass
2 |
3 | import pytest
4 |
5 | import strawberry
6 | from strawberry.dataloader import DataLoader
7 |
8 |
9 | @pytest.mark.asyncio
10 | async def test_can_use_dataloaders(mocker):
11 | @dataclass
12 | class User:
13 | id: str
14 |
15 | async def idx(keys) -> list[User]:
16 | return [User(key) for key in keys]
17 |
18 | mock_loader = mocker.Mock(side_effect=idx)
19 |
20 | loader = DataLoader(load_fn=mock_loader)
21 |
22 | @strawberry.type
23 | class Query:
24 | @strawberry.field
25 | async def get_user(self, id: strawberry.ID) -> str:
26 | user = await loader.load(id)
27 |
28 | return user.id
29 |
30 | schema = strawberry.Schema(query=Query)
31 |
32 | query = """{
33 | a: getUser(id: "1")
34 | b: getUser(id: "2")
35 | }"""
36 |
37 | result = await schema.execute(query)
38 |
39 | assert not result.errors
40 | assert result.data == {
41 | "a": "1",
42 | "b": "2",
43 | }
44 |
45 | mock_loader.assert_called_once_with(["1", "2"])
46 |
--------------------------------------------------------------------------------
/tests/schema/test_lazy/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/schema/test_lazy/__init__.py
--------------------------------------------------------------------------------
/tests/schema/test_lazy/schema.py:
--------------------------------------------------------------------------------
1 | import strawberry
2 | from tests.schema.test_lazy.type_c import Query
3 |
4 | schema = strawberry.Schema(query=Query)
5 |
--------------------------------------------------------------------------------
/tests/schema/test_lazy/test_lazy.py:
--------------------------------------------------------------------------------
1 | import textwrap
2 |
3 | import strawberry
4 | from strawberry.printer import print_schema
5 |
6 |
7 | def test_cyclic_import():
8 | from .type_a import TypeA
9 | from .type_b import TypeB
10 |
11 | @strawberry.type
12 | class Query:
13 | a: TypeA
14 | b: TypeB
15 |
16 | expected = """
17 | type Query {
18 | a: TypeA!
19 | b: TypeB!
20 | }
21 |
22 | type TypeA {
23 | listOfB: [TypeB!]
24 | typeB: TypeB!
25 | }
26 |
27 | type TypeB {
28 | typeA: TypeA!
29 | typeAList: [TypeA!]!
30 | typeCList: [TypeC!]!
31 | }
32 |
33 | type TypeC {
34 | name: String!
35 | }
36 | """
37 |
38 | schema = strawberry.Schema(Query)
39 |
40 | assert print_schema(schema) == textwrap.dedent(expected).strip()
41 |
--------------------------------------------------------------------------------
/tests/schema/test_lazy/type_a.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING, Annotated, Optional
2 |
3 | import strawberry
4 |
5 | if TYPE_CHECKING:
6 | from .type_b import TypeB
7 |
8 |
9 | @strawberry.type
10 | class TypeA:
11 | list_of_b: Optional[
12 | list[Annotated["TypeB", strawberry.lazy("tests.schema.test_lazy.type_b")]]
13 | ] = None
14 |
15 | @strawberry.field
16 | def type_b(self) -> Annotated["TypeB", strawberry.lazy(".type_b")]:
17 | from .type_b import TypeB
18 |
19 | return TypeB()
20 |
--------------------------------------------------------------------------------
/tests/schema/test_lazy/type_b.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING, Annotated
2 |
3 | import strawberry
4 |
5 | if TYPE_CHECKING:
6 | from .type_a import TypeA
7 | from .type_c import TypeC
8 |
9 | ListTypeA = list[TypeA]
10 | ListTypeC = list[TypeC]
11 | else:
12 | TypeA = Annotated["TypeA", strawberry.lazy("tests.schema.test_lazy.type_a")]
13 | ListTypeA = list[
14 | Annotated["TypeA", strawberry.lazy("tests.schema.test_lazy.type_a")]
15 | ]
16 | ListTypeC = list[
17 | Annotated["TypeC", strawberry.lazy("tests.schema.test_lazy.type_c")]
18 | ]
19 |
20 |
21 | @strawberry.type
22 | class TypeB:
23 | @strawberry.field()
24 | def type_a(
25 | self,
26 | ) -> TypeA:
27 | from .type_a import TypeA
28 |
29 | return TypeA()
30 |
31 | @strawberry.field()
32 | def type_a_list(
33 | self,
34 | ) -> ListTypeA: # pragma: no cover
35 | from .type_a import TypeA
36 |
37 | return [TypeA()]
38 |
39 | @strawberry.field()
40 | def type_c_list(
41 | self,
42 | ) -> ListTypeC: # pragma: no cover
43 | from .type_c import TypeC
44 |
45 | return [TypeC()]
46 |
--------------------------------------------------------------------------------
/tests/schema/test_lazy/type_c.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from typing import Annotated, Generic, TypeVar
3 |
4 | import strawberry
5 |
6 | T = TypeVar("T")
7 |
8 |
9 | @strawberry.type
10 | class TypeC:
11 | name: str
12 |
13 |
14 | @strawberry.type
15 | class Edge(Generic[T]):
16 | @strawberry.field
17 | def node(self) -> T: # type: ignore
18 | ...
19 |
20 |
21 | @strawberry.type
22 | class Query:
23 | type_a: Edge[TypeC]
24 | type_b: Edge[Annotated["TypeC", strawberry.lazy("tests.schema.test_lazy.type_c")]]
25 |
26 |
27 | if __name__ == "__main__":
28 | schema = strawberry.Schema(query=Query)
29 | sys.stdout.write(f"{schema.as_str()}\n")
30 |
--------------------------------------------------------------------------------
/tests/schema/test_lazy/type_d.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from typing import Annotated, Generic, TypeVar
3 |
4 | import strawberry
5 |
6 | T = TypeVar("T")
7 |
8 |
9 | class Mixin(Generic[T]):
10 | node: T
11 |
12 |
13 | @strawberry.type
14 | class TypeD(Mixin[int]):
15 | name: str
16 |
17 |
18 | @strawberry.type
19 | class Query:
20 | type_d_1: TypeD
21 | type_d: Annotated["TypeD", strawberry.lazy("tests.schema.test_lazy.type_d")]
22 |
23 |
24 | if __name__ == "__main__":
25 | schema = strawberry.Schema(query=Query)
26 | sys.stdout.write(f"{schema.as_str()}\n")
27 |
--------------------------------------------------------------------------------
/tests/schema/test_lazy_types/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/schema/test_lazy_types/__init__.py
--------------------------------------------------------------------------------
/tests/schema/test_lazy_types/test_cyclic.py:
--------------------------------------------------------------------------------
1 | import textwrap
2 |
3 | import strawberry
4 | from strawberry.printer import print_schema
5 |
6 |
7 | def test_cyclic_import():
8 | from .type_a import TypeA
9 | from .type_b import TypeB
10 |
11 | @strawberry.type
12 | class Query:
13 | a: TypeA
14 | b: TypeB
15 |
16 | expected = """
17 | type Query {
18 | a: TypeA!
19 | b: TypeB!
20 | }
21 |
22 | type TypeA {
23 | listOfB: [TypeB!]
24 | typeB: TypeB!
25 | }
26 |
27 | type TypeB {
28 | typeA: TypeA!
29 | }
30 | """
31 |
32 | schema = strawberry.Schema(Query)
33 |
34 | assert print_schema(schema) == textwrap.dedent(expected).strip()
35 |
--------------------------------------------------------------------------------
/tests/schema/test_lazy_types/test_lazy_enums.py:
--------------------------------------------------------------------------------
1 | import enum
2 | import textwrap
3 | from typing import TYPE_CHECKING
4 |
5 | import pytest
6 |
7 | import strawberry
8 | from strawberry.printer import print_schema
9 |
10 | if TYPE_CHECKING:
11 | import tests
12 |
13 |
14 | @strawberry.enum
15 | class LazyEnum(enum.Enum):
16 | BREAD = "BREAD"
17 |
18 |
19 | def test_lazy_enum():
20 | with pytest.deprecated_call():
21 |
22 | @strawberry.type
23 | class Query:
24 | a: strawberry.LazyType[
25 | "LazyEnum", "tests.schema.test_lazy_types.test_lazy_enums"
26 | ]
27 |
28 | expected = """
29 | enum LazyEnum {
30 | BREAD
31 | }
32 |
33 | type Query {
34 | a: LazyEnum!
35 | }
36 | """
37 |
38 | schema = strawberry.Schema(Query)
39 |
40 | assert print_schema(schema) == textwrap.dedent(expected).strip()
41 |
--------------------------------------------------------------------------------
/tests/schema/test_lazy_types/type_a.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING, Optional
2 |
3 | import strawberry
4 |
5 | if TYPE_CHECKING:
6 | import tests.schema.test_lazy_types
7 |
8 | from .type_b import TypeB
9 |
10 |
11 | @strawberry.type
12 | class TypeA:
13 | list_of_b: Optional[
14 | list[strawberry.LazyType["TypeB", "tests.schema.test_lazy_types.type_b"]]
15 | ] = None
16 |
17 | @strawberry.field
18 | def type_b(self) -> strawberry.LazyType["TypeB", ".type_b"]: # noqa: F722
19 | from .type_b import TypeB
20 |
21 | return TypeB()
22 |
--------------------------------------------------------------------------------
/tests/schema/test_lazy_types/type_b.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING
2 |
3 | import strawberry
4 |
5 | if TYPE_CHECKING:
6 | import tests
7 |
8 | from .type_a import TypeA
9 |
10 |
11 | @strawberry.type
12 | class TypeB:
13 | @strawberry.field()
14 | def type_a(
15 | self,
16 | ) -> strawberry.LazyType["TypeA", "tests.schema.test_lazy_types.type_a"]:
17 | from .type_a import TypeA
18 |
19 | return TypeA()
20 |
--------------------------------------------------------------------------------
/tests/schema/test_unresolved_fields.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | import strawberry
4 | from strawberry.exceptions.unresolved_field_type import UnresolvedFieldTypeError
5 |
6 |
7 | @pytest.mark.raises_strawberry_exception(
8 | UnresolvedFieldTypeError,
9 | match=(
10 | "Could not resolve the type of 'user'. Check that "
11 | "the class is accessible from the global module scope."
12 | ),
13 | )
14 | def test_unresolved_field_fails():
15 | @strawberry.type
16 | class Query:
17 | user: "User" # type: ignore # noqa: F821
18 |
19 | strawberry.Schema(query=Query)
20 |
21 |
22 | @pytest.mark.raises_strawberry_exception(
23 | UnresolvedFieldTypeError,
24 | match=(
25 | "Could not resolve the type of 'user'. Check that "
26 | "the class is accessible from the global module scope."
27 | ),
28 | )
29 | def test_unresolved_field_with_resolver_fails():
30 | @strawberry.type
31 | class Query:
32 | @strawberry.field
33 | def user(self) -> "User": # type: ignore # noqa: F821
34 | ...
35 |
36 | strawberry.Schema(query=Query)
37 |
--------------------------------------------------------------------------------
/tests/schema/types/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/schema/types/__init__.py
--------------------------------------------------------------------------------
/tests/schema_codegen/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/schema_codegen/__init__.py
--------------------------------------------------------------------------------
/tests/schema_codegen/test_order.py:
--------------------------------------------------------------------------------
1 | import textwrap
2 |
3 | from strawberry.schema_codegen import codegen
4 |
5 |
6 | def test_generates_used_interface_before():
7 | schema = """
8 | type Human implements Being {
9 | id: ID!
10 | name: String!
11 | friends: [Human]
12 | }
13 |
14 | type Cat implements Being {
15 | id: ID!
16 | name: String!
17 | livesLeft: Int
18 | }
19 |
20 | interface Being {
21 | id: ID!
22 | name: String!
23 | }
24 | """
25 |
26 | expected = textwrap.dedent(
27 | """
28 | import strawberry
29 |
30 | @strawberry.interface
31 | class Being:
32 | id: strawberry.ID
33 | name: str
34 |
35 | @strawberry.type
36 | class Human(Being):
37 | id: strawberry.ID
38 | name: str
39 | friends: list[Human | None] | None
40 |
41 | @strawberry.type
42 | class Cat(Being):
43 | id: strawberry.ID
44 | name: str
45 | lives_left: int | None
46 | """
47 | ).strip()
48 |
49 | assert codegen(schema).strip() == expected
50 |
--------------------------------------------------------------------------------
/tests/schema_codegen/test_union.py:
--------------------------------------------------------------------------------
1 | import textwrap
2 |
3 | from strawberry.schema_codegen import codegen
4 |
5 |
6 | def test_union():
7 | schema = """
8 | union User = Admin | Client
9 |
10 | type Admin {
11 | name: String!
12 | }
13 |
14 | type Client {
15 | name: String!
16 | }
17 | """
18 |
19 | expected = textwrap.dedent(
20 | """
21 | import strawberry
22 | from typing import Annotated
23 |
24 | User = Annotated[Admin | Client, strawberry.union(name="User")]
25 |
26 | @strawberry.type
27 | class Admin:
28 | name: str
29 |
30 | @strawberry.type
31 | class Client:
32 | name: str
33 | """
34 | ).strip()
35 |
36 | assert codegen(schema).strip() == expected
37 |
--------------------------------------------------------------------------------
/tests/test/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/test/__init__.py
--------------------------------------------------------------------------------
/tests/test_deprecations.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | import strawberry
4 | from strawberry.utils.deprecations import DEPRECATION_MESSAGES
5 |
6 |
7 | @strawberry.type
8 | class A:
9 | a: int
10 |
11 |
12 | def test_type_definition_is_aliased():
13 | with pytest.warns(
14 | match="_type_definition is deprecated, use __strawberry_definition__ instead"
15 | ):
16 | assert A.__strawberry_definition__ is A._type_definition
17 |
18 |
19 | def test_get_warns():
20 | with pytest.warns(match=DEPRECATION_MESSAGES._TYPE_DEFINITION):
21 | assert A._type_definition.fields[0]
22 |
23 |
24 | def test_can_import_type_definition():
25 | from strawberry.types.base import TypeDefinition
26 |
27 | assert TypeDefinition
28 |
--------------------------------------------------------------------------------
/tests/test_info.py:
--------------------------------------------------------------------------------
1 | from typing import Any
2 |
3 | import pytest
4 |
5 | import strawberry
6 |
7 |
8 | def test_can_use_info_with_two_arguments():
9 | CustomInfo = strawberry.Info[int, str]
10 |
11 | assert CustomInfo.__args__ == (int, str)
12 |
13 |
14 | def test_can_use_info_with_one_argument():
15 | CustomInfo = strawberry.Info[int]
16 |
17 | assert CustomInfo.__args__ == (int, Any)
18 |
19 |
20 | def test_cannot_use_info_with_more_than_two_arguments():
21 | with pytest.raises(
22 | TypeError,
23 | match="Too many (arguments|parameters) for ; actual 3, expected 2",
24 | ):
25 | strawberry.Info[int, str, int] # type: ignore
26 |
--------------------------------------------------------------------------------
/tests/test_inspect.py:
--------------------------------------------------------------------------------
1 | from strawberry.utils.inspect import in_async_context
2 |
3 |
4 | def test_in_async_context_sync():
5 | assert not in_async_context()
6 |
7 |
8 | async def test_in_async_context_async():
9 | assert in_async_context()
10 |
11 |
12 | async def test_in_async_context_async_with_inner_sync_function():
13 | def inner_sync_function():
14 | assert in_async_context()
15 |
16 | inner_sync_function()
17 |
--------------------------------------------------------------------------------
/tests/test_printer/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/test_printer/__init__.py
--------------------------------------------------------------------------------
/tests/test_printer/test_one_of.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import textwrap
4 |
5 | import strawberry
6 | from strawberry.schema_directives import OneOf
7 |
8 |
9 | @strawberry.input(directives=[OneOf()])
10 | class ExampleInputTagged:
11 | a: str | None
12 | b: int | None
13 |
14 |
15 | @strawberry.type
16 | class ExampleResult:
17 | a: str | None
18 | b: int | None
19 |
20 |
21 | @strawberry.type
22 | class Query:
23 | @strawberry.field
24 | def test(self, input: ExampleInputTagged) -> ExampleResult: # pragma: no cover
25 | return input # type: ignore
26 |
27 |
28 | schema = strawberry.Schema(query=Query)
29 |
30 |
31 | def test_prints_one_of_directive():
32 | expected_type = """
33 | directive @oneOf on INPUT_OBJECT
34 |
35 | input ExampleInputTagged @oneOf {
36 | a: String
37 | b: Int
38 | }
39 |
40 | type ExampleResult {
41 | a: String
42 | b: Int
43 | }
44 |
45 | type Query {
46 | test(input: ExampleInputTagged!): ExampleResult!
47 | }
48 | """
49 |
50 | assert str(schema) == textwrap.dedent(expected_type).strip()
51 |
--------------------------------------------------------------------------------
/tests/test_repr.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 |
3 | import strawberry
4 |
5 |
6 | def test_repr_type():
7 | @strawberry.type
8 | class MyType:
9 | s: str
10 | i: int
11 | b: bool
12 | f: float
13 | id: strawberry.ID
14 |
15 | assert (
16 | repr(MyType(s="a", i=1, b=True, f=3.2, id="123"))
17 | == "test_repr_type..MyType(s='a', i=1, b=True, f=3.2, id='123')"
18 | )
19 |
20 |
21 | def test_repr_enum():
22 | @strawberry.enum()
23 | class Test(Enum):
24 | A = 1
25 | B = 2
26 | C = 3
27 |
28 | assert repr(Test(1)) == ""
29 |
--------------------------------------------------------------------------------
/tests/tools/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/tools/__init__.py
--------------------------------------------------------------------------------
/tests/typecheckers/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/typecheckers/__init__.py
--------------------------------------------------------------------------------
/tests/typecheckers/utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/typecheckers/utils/__init__.py
--------------------------------------------------------------------------------
/tests/typecheckers/utils/marks.py:
--------------------------------------------------------------------------------
1 | import shutil
2 | import sys
3 |
4 | import pytest
5 |
6 |
7 | def pyright_exist() -> bool:
8 | return shutil.which("pyright") is not None
9 |
10 |
11 | def mypy_exists() -> bool:
12 | return shutil.which("mypy") is not None
13 |
14 |
15 | skip_on_windows = pytest.mark.skipif(
16 | sys.platform == "win32",
17 | reason="Do not run pyright on windows due to path issues",
18 | )
19 |
20 | requires_pyright = pytest.mark.skipif(
21 | not pyright_exist(),
22 | reason="These tests require pyright",
23 | )
24 |
25 | requires_mypy = pytest.mark.skipif(
26 | not mypy_exists(),
27 | reason="These tests require mypy",
28 | )
29 |
--------------------------------------------------------------------------------
/tests/typecheckers/utils/result.py:
--------------------------------------------------------------------------------
1 | from dataclasses import dataclass
2 | from typing import Literal
3 |
4 | ResultType = Literal[
5 | "error",
6 | "information",
7 | "note",
8 | ]
9 |
10 |
11 | @dataclass
12 | class Result:
13 | type: ResultType
14 | message: str
15 | line: int
16 | column: int
17 |
--------------------------------------------------------------------------------
/tests/typecheckers/utils/typecheck.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import concurrent.futures
4 | from dataclasses import dataclass
5 |
6 | from .mypy import run_mypy
7 | from .pyright import run_pyright
8 | from .result import Result
9 |
10 |
11 | @dataclass
12 | class TypecheckResult:
13 | pyright: list[Result]
14 | mypy: list[Result]
15 |
16 |
17 | def typecheck(code: str, strict: bool = True) -> TypecheckResult:
18 | with concurrent.futures.ThreadPoolExecutor() as executor:
19 | pyright_future = executor.submit(run_pyright, code, strict=strict)
20 | mypy_future = executor.submit(run_mypy, code, strict=strict)
21 |
22 | pyright_results = pyright_future.result()
23 | mypy_results = mypy_future.result()
24 |
25 | return TypecheckResult(pyright=pyright_results, mypy=mypy_results)
26 |
--------------------------------------------------------------------------------
/tests/types/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/types/__init__.py
--------------------------------------------------------------------------------
/tests/types/cross_module_resolvers/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/types/cross_module_resolvers/__init__.py
--------------------------------------------------------------------------------
/tests/types/cross_module_resolvers/a_mod.py:
--------------------------------------------------------------------------------
1 | import strawberry
2 |
3 |
4 | def a_resolver() -> list["AObject"]:
5 | return []
6 |
7 |
8 | @strawberry.type
9 | class ABase:
10 | a_name: str
11 |
12 |
13 | @strawberry.type
14 | class AObject(ABase):
15 | a_age: int
16 |
17 | @strawberry.field
18 | def a_is_of_full_age(self) -> bool:
19 | return self.a_age >= 18
20 |
--------------------------------------------------------------------------------
/tests/types/cross_module_resolvers/b_mod.py:
--------------------------------------------------------------------------------
1 | import strawberry
2 |
3 |
4 | def b_resolver() -> list["BObject"]:
5 | return []
6 |
7 |
8 | @strawberry.type
9 | class BBase:
10 | b_name: str = strawberry.field()
11 |
12 |
13 | @strawberry.type
14 | class BObject(BBase):
15 | b_age: int = strawberry.field()
16 |
17 | @strawberry.field
18 | def b_is_of_full_age(self) -> bool:
19 | return self.b_age >= 18
20 |
--------------------------------------------------------------------------------
/tests/types/cross_module_resolvers/x_mod.py:
--------------------------------------------------------------------------------
1 | def typeless_resolver() -> list: # pragma: no cover
2 | return []
3 |
--------------------------------------------------------------------------------
/tests/types/resolving/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/types/resolving/__init__.py
--------------------------------------------------------------------------------
/tests/types/resolving/test_enums.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 |
3 | import strawberry
4 | from strawberry.annotation import StrawberryAnnotation
5 |
6 |
7 | def test_basic():
8 | @strawberry.enum
9 | class NumaNuma(Enum):
10 | MA = "ma"
11 | I = "i" # noqa: E741
12 | A = "a"
13 | HI = "hi"
14 |
15 | annotation = StrawberryAnnotation(NumaNuma)
16 | resolved = annotation.resolve()
17 |
18 | # TODO: Remove reference to .enum_definition with StrawberryEnum
19 | assert resolved is NumaNuma._enum_definition
20 |
--------------------------------------------------------------------------------
/tests/types/resolving/test_forward_references.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | import strawberry
4 | from strawberry.annotation import StrawberryAnnotation
5 |
6 |
7 | def test_forward_reference():
8 | global ForwardClass
9 |
10 | annotation = StrawberryAnnotation("ForwardClass", namespace=globals())
11 |
12 | @strawberry.type
13 | class ForwardClass:
14 | backward: bool
15 |
16 | resolved = annotation.resolve()
17 |
18 | assert resolved is ForwardClass
19 |
20 | del ForwardClass
21 |
22 |
23 | @pytest.mark.xfail(reason="Combining locals() and globals() strangely makes this fail")
24 | def test_forward_reference_locals_and_globals():
25 | global BackwardClass
26 |
27 | namespace = {**locals(), **globals()}
28 |
29 | annotation = StrawberryAnnotation("BackwardClass", namespace=namespace)
30 |
31 | @strawberry.type
32 | class BackwardClass:
33 | backward: bool
34 |
35 | resolved = annotation.resolve()
36 |
37 | assert resolved is BackwardClass
38 |
39 | del BackwardClass
40 |
--------------------------------------------------------------------------------
/tests/types/resolving/test_literals.py:
--------------------------------------------------------------------------------
1 | from typing import Optional, Union
2 |
3 | from strawberry.annotation import StrawberryAnnotation
4 |
5 |
6 | def test_bool():
7 | annotation = StrawberryAnnotation(bool)
8 | resolved = annotation.resolve()
9 |
10 | assert resolved is bool
11 |
12 |
13 | def test_float():
14 | annotation = StrawberryAnnotation(float)
15 | resolved = annotation.resolve()
16 |
17 | assert resolved is float
18 |
19 |
20 | def test_int():
21 | annotation = StrawberryAnnotation(int)
22 | resolved = annotation.resolve()
23 |
24 | assert resolved is int
25 |
26 |
27 | def test_str():
28 | annotation = StrawberryAnnotation(str)
29 | resolved = annotation.resolve()
30 |
31 | assert resolved is str
32 |
33 |
34 | def test_none():
35 | annotation = StrawberryAnnotation(None)
36 | annotation.resolve()
37 |
38 | annotation = StrawberryAnnotation(type(None))
39 | annotation.resolve()
40 |
41 | annotation = StrawberryAnnotation(Optional[int])
42 | annotation.resolve()
43 |
44 | annotation = StrawberryAnnotation(Union[None, int])
45 | annotation.resolve()
46 |
--------------------------------------------------------------------------------
/tests/types/test_cast.py:
--------------------------------------------------------------------------------
1 | import strawberry
2 | from strawberry.types.cast import get_strawberry_type_cast
3 |
4 |
5 | def test_cast():
6 | @strawberry.type
7 | class SomeType: ...
8 |
9 | class OtherType: ...
10 |
11 | obj = OtherType
12 | assert get_strawberry_type_cast(obj) is None
13 |
14 | cast_obj = strawberry.cast(SomeType, obj)
15 | assert cast_obj is obj
16 | assert get_strawberry_type_cast(cast_obj) is SomeType
17 |
18 |
19 | def test_cast_none_obj():
20 | @strawberry.type
21 | class SomeType: ...
22 |
23 | obj = None
24 | assert get_strawberry_type_cast(obj) is None
25 |
26 | cast_obj = strawberry.cast(SomeType, obj)
27 | assert cast_obj is None
28 | assert get_strawberry_type_cast(obj) is None
29 |
--------------------------------------------------------------------------------
/tests/types/test_deferred_annotations.py:
--------------------------------------------------------------------------------
1 | from sys import modules
2 | from types import ModuleType
3 |
4 | import strawberry
5 |
6 | deferred_module_source = """
7 | from __future__ import annotations
8 |
9 | import strawberry
10 |
11 | @strawberry.type
12 | class User:
13 | username: str
14 | email: str
15 |
16 | @strawberry.interface
17 | class UserContent:
18 | created_by: User
19 | """
20 |
21 |
22 | def test_deferred_other_module():
23 | mod = ModuleType("tests.deferred_module")
24 | modules[mod.__name__] = mod
25 |
26 | try:
27 | exec(deferred_module_source, mod.__dict__) # noqa: S102
28 |
29 | @strawberry.type
30 | class Post(mod.UserContent):
31 | title: str
32 | body: str
33 |
34 | definition = Post.__strawberry_definition__
35 | assert definition.fields[0].type == mod.User
36 | finally:
37 | del modules[mod.__name__]
38 |
--------------------------------------------------------------------------------
/tests/utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/utils/__init__.py
--------------------------------------------------------------------------------
/tests/utils/test_get_first_operation.py:
--------------------------------------------------------------------------------
1 | from graphql import OperationType, parse
2 |
3 | from strawberry.utils.operation import get_first_operation
4 |
5 |
6 | def test_document_without_operation_definition_notes():
7 | document = parse(
8 | """
9 | fragment Test on Query {
10 | hello
11 | }
12 | """
13 | )
14 | assert get_first_operation(document) is None
15 |
16 |
17 | def test_single_operation_definition_note():
18 | document = parse(
19 | """
20 | query Operation1 {
21 | hello
22 | }
23 | """
24 | )
25 | assert get_first_operation(document) is not None
26 | assert get_first_operation(document).operation == OperationType.QUERY
27 |
28 |
29 | def test_multiple_operation_definition_notes():
30 | document = parse(
31 | """
32 | mutation Operation1 {
33 | hello
34 | }
35 | query Operation2 {
36 | hello
37 | }
38 | """
39 | )
40 | assert get_first_operation(document) is not None
41 | assert get_first_operation(document).operation == OperationType.MUTATION
42 |
--------------------------------------------------------------------------------
/tests/utils/test_logging.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | from graphql.error import GraphQLError
4 |
5 | from strawberry.utils.logging import StrawberryLogger
6 |
7 |
8 | def test_strawberry_logger_error(caplog):
9 | caplog.set_level(logging.ERROR, logger="strawberry.execution")
10 |
11 | exc = GraphQLError("test exception")
12 | StrawberryLogger.error(exc)
13 |
14 | assert caplog.record_tuples == [
15 | ("strawberry.execution", logging.ERROR, "test exception")
16 | ]
17 |
--------------------------------------------------------------------------------
/tests/utils/test_pretty_print.py:
--------------------------------------------------------------------------------
1 | from decimal import Decimal
2 |
3 | from strawberry.utils.debug import pretty_print_graphql_operation
4 |
5 |
6 | def test_pretty_print(mocker):
7 | mock = mocker.patch("builtins.print")
8 |
9 | pretty_print_graphql_operation("Example", "{ query }", variables={})
10 |
11 | mock.assert_called_with("{ \x1b[38;5;125mquery\x1b[39m }\n")
12 |
13 |
14 | def test_pretty_print_variables(mocker):
15 | mock = mocker.patch("builtins.print")
16 |
17 | pretty_print_graphql_operation("Example", "{ query }", variables={"example": 1})
18 |
19 | mock.assert_called_with(
20 | "{\n\x1b[38;5;250m "
21 | '\x1b[39m\x1b[38;5;28;01m"example"\x1b[39;00m:\x1b[38;5;250m '
22 | "\x1b[39m\x1b[38;5;241m1\x1b[39m\n}\n"
23 | )
24 |
25 |
26 | def test_pretty_print_variables_object(mocker):
27 | mock = mocker.patch("builtins.print")
28 |
29 | pretty_print_graphql_operation(
30 | "Example", "{ query }", variables={"example": Decimal(1)}
31 | )
32 |
33 | mock.assert_called_with(
34 | "{\n\x1b[38;5;250m "
35 | '\x1b[39m\x1b[38;5;28;01m"example"\x1b[39;00m:\x1b[38;5;250m '
36 | "\x1b[39m\x1b[38;5;124m\"Decimal('1')\"\x1b[39m\n}\n"
37 | )
38 |
--------------------------------------------------------------------------------
/tests/views/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/views/__init__.py
--------------------------------------------------------------------------------
/tests/websockets/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/websockets/__init__.py
--------------------------------------------------------------------------------