strawberry-graphql 0.217.0__tar.gz → 0.218.0.dev1705418681__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (245) hide show
  1. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/PKG-INFO +1 -1
  2. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/pyproject.toml +4 -4
  3. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/aiohttp/views.py +37 -3
  4. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/asgi/__init__.py +19 -1
  5. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/chalice/views.py +3 -3
  6. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/channels/handlers/http_handler.py +43 -9
  7. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/django/views.py +27 -8
  8. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/fastapi/router.py +13 -0
  9. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/flask/views.py +9 -1
  10. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/http/async_base_view.py +117 -6
  11. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/http/base.py +13 -0
  12. strawberry_graphql-0.218.0.dev1705418681/strawberry/http/parse_content_type.py +17 -0
  13. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/http/sync_base_view.py +13 -4
  14. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/permission.py +5 -1
  15. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/quart/views.py +13 -1
  16. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/sanic/views.py +26 -1
  17. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/schema/base.py +6 -2
  18. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/schema/execute.py +26 -14
  19. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/schema/schema.py +2 -2
  20. strawberry_graphql-0.218.0.dev1705418681/strawberry/types/__init__.py +9 -0
  21. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/types/execution.py +11 -1
  22. strawberry_graphql-0.217.0/strawberry/types/__init__.py +0 -4
  23. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/LICENSE +0 -0
  24. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/README.md +0 -0
  25. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/__init__.py +0 -0
  26. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/__main__.py +0 -0
  27. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/aiohttp/__init__.py +0 -0
  28. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/aiohttp/handlers/__init__.py +0 -0
  29. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/aiohttp/handlers/graphql_transport_ws_handler.py +0 -0
  30. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/aiohttp/handlers/graphql_ws_handler.py +0 -0
  31. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/aiohttp/test/__init__.py +0 -0
  32. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/aiohttp/test/client.py +0 -0
  33. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/annotation.py +0 -0
  34. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/arguments.py +0 -0
  35. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/asgi/handlers/__init__.py +0 -0
  36. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/asgi/handlers/graphql_transport_ws_handler.py +0 -0
  37. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/asgi/handlers/graphql_ws_handler.py +0 -0
  38. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/asgi/test/__init__.py +0 -0
  39. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/asgi/test/client.py +0 -0
  40. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/auto.py +0 -0
  41. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/chalice/__init__.py +0 -0
  42. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/channels/__init__.py +0 -0
  43. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/channels/handlers/base.py +0 -0
  44. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/channels/handlers/graphql_transport_ws_handler.py +0 -0
  45. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/channels/handlers/graphql_ws_handler.py +0 -0
  46. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/channels/handlers/ws_handler.py +0 -0
  47. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/channels/router.py +0 -0
  48. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/channels/testing.py +0 -0
  49. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/cli/__init__.py +0 -0
  50. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/cli/app.py +0 -0
  51. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/cli/commands/__init__.py +0 -0
  52. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/cli/commands/codegen.py +0 -0
  53. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/cli/commands/export_schema.py +0 -0
  54. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/cli/commands/schema_codegen.py +0 -0
  55. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/cli/commands/server.py +0 -0
  56. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/cli/commands/upgrade/__init__.py +0 -0
  57. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/cli/commands/upgrade/_fake_progress.py +0 -0
  58. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/cli/commands/upgrade/_run_codemod.py +0 -0
  59. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/cli/constants.py +0 -0
  60. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/cli/debug_server.py +0 -0
  61. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/cli/utils/__init__.py +0 -0
  62. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/cli/utils/load_schema.py +0 -0
  63. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/codegen/__init__.py +0 -0
  64. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/codegen/exceptions.py +0 -0
  65. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/codegen/plugins/__init__.py +0 -0
  66. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/codegen/plugins/print_operation.py +0 -0
  67. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/codegen/plugins/python.py +0 -0
  68. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/codegen/plugins/typescript.py +0 -0
  69. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/codegen/query_codegen.py +0 -0
  70. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/codegen/types.py +0 -0
  71. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/codemods/__init__.py +0 -0
  72. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/codemods/annotated_unions.py +0 -0
  73. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/custom_scalar.py +0 -0
  74. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/dataloader.py +0 -0
  75. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/directive.py +0 -0
  76. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/django/__init__.py +0 -0
  77. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/django/apps.py +0 -0
  78. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/django/context.py +0 -0
  79. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/django/test/__init__.py +0 -0
  80. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/django/test/client.py +0 -0
  81. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/enum.py +0 -0
  82. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/exceptions/__init__.py +0 -0
  83. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/exceptions/conflicting_arguments.py +0 -0
  84. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/exceptions/duplicated_type_name.py +0 -0
  85. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/exceptions/exception.py +0 -0
  86. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/exceptions/exception_source.py +0 -0
  87. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/exceptions/handler.py +0 -0
  88. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/exceptions/invalid_argument_type.py +0 -0
  89. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/exceptions/invalid_union_type.py +0 -0
  90. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/exceptions/missing_arguments_annotations.py +0 -0
  91. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/exceptions/missing_field_annotation.py +0 -0
  92. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/exceptions/missing_return_annotation.py +0 -0
  93. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/exceptions/not_a_strawberry_enum.py +0 -0
  94. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/exceptions/object_is_not_a_class.py +0 -0
  95. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/exceptions/object_is_not_an_enum.py +0 -0
  96. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/exceptions/permission_fail_silently_requires_optional.py +0 -0
  97. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/exceptions/private_strawberry_field.py +0 -0
  98. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/exceptions/scalar_already_registered.py +0 -0
  99. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/exceptions/syntax.py +0 -0
  100. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/exceptions/unresolved_field_type.py +0 -0
  101. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/exceptions/utils/__init__.py +0 -0
  102. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/exceptions/utils/source_finder.py +0 -0
  103. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/experimental/__init__.py +0 -0
  104. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/experimental/pydantic/__init__.py +0 -0
  105. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/experimental/pydantic/_compat.py +0 -0
  106. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/experimental/pydantic/conversion.py +0 -0
  107. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/experimental/pydantic/conversion_types.py +0 -0
  108. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/experimental/pydantic/error_type.py +0 -0
  109. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/experimental/pydantic/exceptions.py +0 -0
  110. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/experimental/pydantic/fields.py +0 -0
  111. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/experimental/pydantic/object_type.py +0 -0
  112. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/experimental/pydantic/utils.py +0 -0
  113. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/ext/LICENSE +0 -0
  114. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/ext/__init__.py +0 -0
  115. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/ext/dataclasses/LICENSE +0 -0
  116. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/ext/dataclasses/__init__.py +0 -0
  117. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/ext/dataclasses/dataclasses.py +0 -0
  118. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/ext/mypy_plugin.py +0 -0
  119. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/extensions/__init__.py +0 -0
  120. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/extensions/add_validation_rules.py +0 -0
  121. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/extensions/base_extension.py +0 -0
  122. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/extensions/context.py +0 -0
  123. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/extensions/directives.py +0 -0
  124. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/extensions/disable_validation.py +0 -0
  125. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/extensions/field_extension.py +0 -0
  126. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/extensions/mask_errors.py +0 -0
  127. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/extensions/max_aliases.py +0 -0
  128. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/extensions/max_tokens.py +0 -0
  129. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/extensions/parser_cache.py +0 -0
  130. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/extensions/pyinstrument.py +0 -0
  131. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/extensions/query_depth_limiter.py +0 -0
  132. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/extensions/runner.py +0 -0
  133. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/extensions/tracing/__init__.py +0 -0
  134. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/extensions/tracing/apollo.py +0 -0
  135. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/extensions/tracing/datadog.py +0 -0
  136. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/extensions/tracing/opentelemetry.py +0 -0
  137. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/extensions/tracing/sentry.py +0 -0
  138. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/extensions/tracing/utils.py +0 -0
  139. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/extensions/utils.py +0 -0
  140. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/extensions/validation_cache.py +0 -0
  141. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/fastapi/__init__.py +0 -0
  142. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/fastapi/context.py +0 -0
  143. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/fastapi/handlers/__init__.py +0 -0
  144. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/fastapi/handlers/graphql_transport_ws_handler.py +0 -0
  145. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/fastapi/handlers/graphql_ws_handler.py +0 -0
  146. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/federation/__init__.py +0 -0
  147. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/federation/argument.py +0 -0
  148. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/federation/enum.py +0 -0
  149. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/federation/field.py +0 -0
  150. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/federation/mutation.py +0 -0
  151. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/federation/object_type.py +0 -0
  152. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/federation/scalar.py +0 -0
  153. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/federation/schema.py +0 -0
  154. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/federation/schema_directive.py +0 -0
  155. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/federation/schema_directives.py +0 -0
  156. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/federation/types.py +0 -0
  157. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/federation/union.py +0 -0
  158. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/field.py +0 -0
  159. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/field_extensions/__init__.py +0 -0
  160. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/field_extensions/input_mutation.py +0 -0
  161. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/file_uploads/__init__.py +0 -0
  162. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/file_uploads/scalars.py +0 -0
  163. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/file_uploads/utils.py +0 -0
  164. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/flask/__init__.py +0 -0
  165. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/http/__init__.py +0 -0
  166. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/http/exceptions.py +0 -0
  167. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/http/ides.py +0 -0
  168. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/http/temporal_response.py +0 -0
  169. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/http/types.py +0 -0
  170. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/http/typevars.py +0 -0
  171. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/lazy_type.py +0 -0
  172. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/mutation.py +0 -0
  173. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/object_type.py +0 -0
  174. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/parent.py +0 -0
  175. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/printer/__init__.py +0 -0
  176. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/printer/ast_from_value.py +0 -0
  177. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/printer/printer.py +0 -0
  178. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/private.py +0 -0
  179. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/py.typed +0 -0
  180. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/quart/__init__.py +0 -0
  181. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/relay/__init__.py +0 -0
  182. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/relay/exceptions.py +0 -0
  183. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/relay/fields.py +0 -0
  184. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/relay/types.py +0 -0
  185. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/relay/utils.py +0 -0
  186. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/resolvers.py +0 -0
  187. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/sanic/__init__.py +0 -0
  188. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/sanic/context.py +0 -0
  189. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/sanic/utils.py +0 -0
  190. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/scalars.py +0 -0
  191. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/schema/__init__.py +0 -0
  192. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/schema/compat.py +0 -0
  193. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/schema/config.py +0 -0
  194. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/schema/exceptions.py +0 -0
  195. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/schema/name_converter.py +0 -0
  196. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/schema/schema_converter.py +0 -0
  197. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/schema/types/__init__.py +0 -0
  198. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/schema/types/base_scalars.py +0 -0
  199. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/schema/types/concrete_type.py +0 -0
  200. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/schema/types/scalar.py +0 -0
  201. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/schema_codegen/__init__.py +0 -0
  202. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/schema_directive.py +0 -0
  203. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/starlite/__init__.py +0 -0
  204. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/starlite/controller.py +0 -0
  205. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/starlite/handlers/graphql_transport_ws_handler.py +0 -0
  206. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/starlite/handlers/graphql_ws_handler.py +0 -0
  207. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/static/apollo-sandbox.html +0 -0
  208. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/static/graphiql.html +0 -0
  209. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/static/pathfinder.html +0 -0
  210. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/subscriptions/__init__.py +0 -0
  211. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/subscriptions/protocols/__init__.py +0 -0
  212. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/subscriptions/protocols/graphql_transport_ws/__init__.py +0 -0
  213. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/subscriptions/protocols/graphql_transport_ws/handlers.py +0 -0
  214. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/subscriptions/protocols/graphql_transport_ws/types.py +0 -0
  215. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/subscriptions/protocols/graphql_ws/__init__.py +0 -0
  216. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/subscriptions/protocols/graphql_ws/handlers.py +0 -0
  217. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/subscriptions/protocols/graphql_ws/types.py +0 -0
  218. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/test/__init__.py +0 -0
  219. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/test/client.py +0 -0
  220. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/tools/__init__.py +0 -0
  221. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/tools/create_type.py +0 -0
  222. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/tools/merge_types.py +0 -0
  223. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/type.py +0 -0
  224. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/types/fields/__init__.py +0 -0
  225. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/types/fields/resolver.py +0 -0
  226. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/types/graphql.py +0 -0
  227. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/types/info.py +0 -0
  228. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/types/nodes.py +0 -0
  229. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/types/type_resolver.py +0 -0
  230. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/types/types.py +0 -0
  231. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/union.py +0 -0
  232. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/unset.py +0 -0
  233. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/utils/__init__.py +0 -0
  234. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/utils/aio.py +0 -0
  235. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/utils/await_maybe.py +0 -0
  236. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/utils/dataclasses.py +0 -0
  237. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/utils/debug.py +0 -0
  238. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/utils/deprecations.py +0 -0
  239. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/utils/graphql_lexer.py +0 -0
  240. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/utils/importer.py +0 -0
  241. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/utils/inspect.py +0 -0
  242. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/utils/logging.py +0 -0
  243. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/utils/operation.py +0 -0
  244. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/utils/str_converters.py +0 -0
  245. {strawberry_graphql-0.217.0 → strawberry_graphql-0.218.0.dev1705418681}/strawberry/utils/typing.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: strawberry-graphql
3
- Version: 0.217.0
3
+ Version: 0.218.0.dev1705418681
4
4
  Summary: A library for creating GraphQL APIs
5
5
  Home-page: https://strawberry.rocks/
6
6
  License: MIT
@@ -1,7 +1,7 @@
1
1
  [tool.poetry]
2
2
  name = "strawberry-graphql"
3
3
  packages = [ { include = "strawberry" } ]
4
- version = "0.217.0"
4
+ version = "0.218.0.dev.1705418681"
5
5
  description = "A library for creating GraphQL APIs"
6
6
  authors = ["Patrick Arminio <patrick.arminio@gmail.com>"]
7
7
  license = "MIT"
@@ -75,7 +75,7 @@ opentelemetry-sdk = "<2"
75
75
  pygments = "^2.3"
76
76
  pyinstrument = {version = ">=4.0.0", optional = false}
77
77
  pytest = "^7.2"
78
- pytest-asyncio = ">=0.20.3,<0.22.0"
78
+ pytest-asyncio = ">=0.20.3,<0.24.0"
79
79
  pytest-codspeed = "^2.0.1"
80
80
  pytest-cov = "^4.0.0"
81
81
  pytest-emoji = "^0.2.0"
@@ -96,8 +96,8 @@ types-toml = "^0.10.8"
96
96
  types-typed-ast = "^1.5.8"
97
97
  types-ujson = "^5.6.0"
98
98
  # added this here manually because poetry doesn't seem to be able to handle it =(
99
- botocore = "1.31.84"
100
- mypy = "1.7.0"
99
+ botocore = "1.34.13"
100
+ mypy = "1.8.0"
101
101
  pytest-mypy-plugins = ">=1.10,<4.0"
102
102
  types-protobuf = "^4.23.0.1"
103
103
 
@@ -7,10 +7,13 @@ from io import BytesIO
7
7
  from typing import (
8
8
  TYPE_CHECKING,
9
9
  Any,
10
+ AsyncGenerator,
11
+ Callable,
10
12
  Dict,
11
13
  Iterable,
12
14
  Mapping,
13
15
  Optional,
16
+ Union,
14
17
  cast,
15
18
  )
16
19
 
@@ -40,7 +43,7 @@ class AioHTTPRequestAdapter(AsyncHTTPRequestAdapter):
40
43
 
41
44
  @property
42
45
  def query_params(self) -> QueryParams:
43
- return self.request.query.copy()
46
+ return self.request.query.copy() # type: ignore[attr-defined]
44
47
 
45
48
  async def get_body(self) -> str:
46
49
  return (await self.request.content.read()).decode()
@@ -71,11 +74,17 @@ class AioHTTPRequestAdapter(AsyncHTTPRequestAdapter):
71
74
 
72
75
  @property
73
76
  def content_type(self) -> Optional[str]:
74
- return self.request.content_type
77
+ return self.headers.get("content-type")
75
78
 
76
79
 
77
80
  class GraphQLView(
78
- AsyncBaseHTTPView[web.Request, web.Response, web.Response, Context, RootValue]
81
+ AsyncBaseHTTPView[
82
+ web.Request,
83
+ Union[web.Response, web.StreamResponse],
84
+ web.Response,
85
+ Context,
86
+ RootValue,
87
+ ]
79
88
  ):
80
89
  # Mark the view as coroutine so that AIOHTTP does not confuse it with a deprecated
81
90
  # bare handler function.
@@ -131,6 +140,8 @@ class GraphQLView(
131
140
 
132
141
  if not ws_test.ok:
133
142
  try:
143
+ # TODO: pass this down from run to multipart thingy
144
+ self.request = request
134
145
  return await self.run(request=request)
135
146
  except HTTPException as e:
136
147
  return web.Response(
@@ -177,3 +188,26 @@ class GraphQLView(
177
188
  sub_response.content_type = "application/json"
178
189
 
179
190
  return sub_response
191
+
192
+ async def create_multipart_response(
193
+ self,
194
+ stream: Callable[[], AsyncGenerator[str, None]],
195
+ sub_response: web.Response,
196
+ ) -> web.StreamResponse:
197
+ # TODO: use sub response
198
+ response = web.StreamResponse(
199
+ status=200,
200
+ headers={
201
+ "Transfer-Encoding": "chunked",
202
+ "Content-type": "multipart/mixed;boundary=graphql;subscriptionSpec=1.0,application/json",
203
+ },
204
+ reason="OK",
205
+ )
206
+
207
+ await response.prepare(self.request)
208
+
209
+ async for data in stream():
210
+ await response.write(data.encode())
211
+
212
+ await response.write_eof()
213
+ return response
@@ -5,6 +5,8 @@ from datetime import timedelta
5
5
  from typing import (
6
6
  TYPE_CHECKING,
7
7
  Any,
8
+ AsyncIterator,
9
+ Callable,
8
10
  Mapping,
9
11
  Optional,
10
12
  Sequence,
@@ -14,7 +16,12 @@ from typing import (
14
16
 
15
17
  from starlette import status
16
18
  from starlette.requests import Request
17
- from starlette.responses import HTMLResponse, PlainTextResponse, Response
19
+ from starlette.responses import (
20
+ HTMLResponse,
21
+ PlainTextResponse,
22
+ Response,
23
+ StreamingResponse,
24
+ )
18
25
  from starlette.websockets import WebSocket
19
26
 
20
27
  from strawberry.asgi.handlers import (
@@ -213,3 +220,14 @@ class GraphQL(
213
220
  response.status_code = sub_response.status_code
214
221
 
215
222
  return response
223
+
224
+ async def create_multipart_response(
225
+ self, stream: Callable[[], AsyncIterator[str]], sub_response: Response
226
+ ) -> Response:
227
+ return StreamingResponse(
228
+ stream(),
229
+ headers={
230
+ "Transfer-Encoding": "chunked",
231
+ "Content-type": "multipart/mixed;boundary=graphql;subscriptionSpec=1.0,application/json",
232
+ },
233
+ )
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import warnings
4
- from typing import TYPE_CHECKING, Any, Dict, Mapping, Optional, Union, cast
4
+ from typing import TYPE_CHECKING, Any, Dict, List, Mapping, Optional, Union, cast
5
5
 
6
6
  from chalice.app import Request, Response
7
7
  from strawberry.http.exceptions import HTTPException
@@ -92,7 +92,7 @@ class GraphQLView(
92
92
  message: str,
93
93
  error_code: str,
94
94
  http_status_code: int,
95
- headers: Optional[Dict[str, str]] = None,
95
+ headers: Optional[Dict[str, str | List[str]]] = None,
96
96
  ) -> Response:
97
97
  """
98
98
  A wrapper for error responses
@@ -117,7 +117,7 @@ class GraphQLView(
117
117
  return Response(
118
118
  body=self.encode_json(response_data),
119
119
  status_code=status_code,
120
- headers=sub_response.headers,
120
+ headers=sub_response.headers, # type: ignore[arg-type]
121
121
  )
122
122
 
123
123
  def execute_request(self, request: Request) -> Response:
@@ -9,7 +9,16 @@ import json
9
9
  import warnings
10
10
  from functools import cached_property
11
11
  from io import BytesIO
12
- from typing import TYPE_CHECKING, Any, Dict, Mapping, Optional, Union
12
+ from typing import (
13
+ TYPE_CHECKING,
14
+ Any,
15
+ AsyncGenerator,
16
+ Callable,
17
+ Dict,
18
+ Mapping,
19
+ Optional,
20
+ Union,
21
+ )
13
22
  from urllib.parse import parse_qs
14
23
 
15
24
  from django.conf import settings
@@ -43,6 +52,14 @@ class ChannelsResponse:
43
52
  headers: Dict[bytes, bytes] = dataclasses.field(default_factory=dict)
44
53
 
45
54
 
55
+ @dataclasses.dataclass
56
+ class MultipartChannelsResponse:
57
+ stream: Callable[[], AsyncGenerator[str, None]]
58
+ status: int = 200
59
+ content_type: str = "multipart/mixed;boundary=graphql;subscriptionSpec=1.0"
60
+ headers: Dict[bytes, bytes] = dataclasses.field(default_factory=dict)
61
+
62
+
46
63
  @dataclasses.dataclass
47
64
  class ChannelsRequest:
48
65
  consumer: ChannelsConsumer
@@ -185,16 +202,25 @@ class BaseGraphQLHTTPConsumer(ChannelsConsumer, AsyncHttpConsumer):
185
202
  async def handle(self, body: bytes) -> None:
186
203
  request = ChannelsRequest(consumer=self, body=body)
187
204
  try:
188
- response: ChannelsResponse = await self.run(request)
205
+ response = await self.run(request)
189
206
 
190
207
  if b"Content-Type" not in response.headers:
191
208
  response.headers[b"Content-Type"] = response.content_type.encode()
192
209
 
193
- await self.send_response(
194
- response.status,
195
- response.content,
196
- headers=response.headers,
197
- )
210
+ if isinstance(response, MultipartChannelsResponse):
211
+ response.headers[b"Transfer-Encoding"] = b"chunked"
212
+ await self.send_headers(headers=response.headers)
213
+
214
+ async for chunk in response.stream():
215
+ # TODO: we should change more body
216
+ await self.send_body(chunk.encode("utf-8"), more_body=True)
217
+
218
+ else:
219
+ await self.send_response(
220
+ response.status,
221
+ response.content,
222
+ headers=response.headers,
223
+ )
198
224
  except HTTPException as e:
199
225
  await self.send_response(e.status_code, e.reason.encode())
200
226
 
@@ -203,7 +229,7 @@ class GraphQLHTTPConsumer(
203
229
  BaseGraphQLHTTPConsumer,
204
230
  AsyncBaseHTTPView[
205
231
  ChannelsRequest,
206
- ChannelsResponse,
232
+ Union[ChannelsResponse, MultipartChannelsResponse],
207
233
  TemporalResponse,
208
234
  Context,
209
235
  RootValue,
@@ -247,6 +273,14 @@ class GraphQLHTTPConsumer(
247
273
  async def get_sub_response(self, request: ChannelsRequest) -> TemporalResponse:
248
274
  return TemporalResponse()
249
275
 
276
+ async def create_multipart_response(
277
+ self,
278
+ stream: Callable[[], AsyncGenerator[str, None]],
279
+ sub_response: TemporalResponse,
280
+ ) -> MultipartChannelsResponse:
281
+ # TODO: sub response
282
+ return MultipartChannelsResponse(stream=stream)
283
+
250
284
  async def render_graphql_ide(self, request: ChannelsRequest) -> ChannelsResponse:
251
285
  return ChannelsResponse(
252
286
  content=self.graphql_ide_html.encode(), content_type="text/html"
@@ -301,5 +335,5 @@ class SyncGraphQLHTTPConsumer(
301
335
  request: ChannelsRequest,
302
336
  context: Optional[Context] = UNSET,
303
337
  root_value: Optional[RootValue] = UNSET,
304
- ) -> ChannelsResponse:
338
+ ) -> ChannelsResponse: # pyright: ignore
305
339
  return super().run(request, context, root_value)
@@ -5,6 +5,7 @@ import warnings
5
5
  from typing import (
6
6
  TYPE_CHECKING,
7
7
  Any,
8
+ AsyncIterator,
8
9
  Callable,
9
10
  Mapping,
10
11
  Optional,
@@ -14,8 +15,14 @@ from typing import (
14
15
 
15
16
  from asgiref.sync import markcoroutinefunction
16
17
  from django.core.serializers.json import DjangoJSONEncoder
17
- from django.http import HttpRequest, HttpResponseNotAllowed, JsonResponse
18
- from django.http.response import HttpResponse
18
+ from django.http import (
19
+ HttpRequest,
20
+ HttpResponse,
21
+ HttpResponseNotAllowed,
22
+ JsonResponse,
23
+ StreamingHttpResponse,
24
+ )
25
+ from django.http.response import HttpResponseBase
19
26
  from django.template import RequestContext, Template
20
27
  from django.template.exceptions import TemplateDoesNotExist
21
28
  from django.template.loader import render_to_string
@@ -116,7 +123,7 @@ class AsyncDjangoHTTPRequestAdapter(AsyncHTTPRequestAdapter):
116
123
 
117
124
  @property
118
125
  def content_type(self) -> Optional[str]:
119
- return self.request.content_type
126
+ return self.headers.get("Content-type")
120
127
 
121
128
  async def get_body(self) -> str:
122
129
  return self.request.body.decode()
@@ -159,8 +166,9 @@ class BaseView:
159
166
 
160
167
  def create_response(
161
168
  self, response_data: GraphQLHTTPResponse, sub_response: HttpResponse
162
- ) -> HttpResponse:
169
+ ) -> HttpResponseBase:
163
170
  data = self.encode_json(response_data)
171
+
164
172
  response = HttpResponse(
165
173
  data,
166
174
  content_type="application/json",
@@ -177,6 +185,17 @@ class BaseView:
177
185
 
178
186
  return response
179
187
 
188
+ async def create_multipart_response(
189
+ self, stream: Callable[[], AsyncIterator[Any]], sub_response: HttpResponse
190
+ ) -> HttpResponseBase:
191
+ return StreamingHttpResponse(
192
+ streaming_content=stream(),
193
+ headers={
194
+ "Transfer-Encoding": "chunked",
195
+ "Content-type": "multipart/mixed;boundary=graphql;subscriptionSpec=1.0,application/json",
196
+ },
197
+ )
198
+
180
199
  def encode_json(self, response_data: GraphQLHTTPResponse) -> str:
181
200
  return json.dumps(response_data, cls=DjangoJSONEncoder)
182
201
 
@@ -184,7 +203,7 @@ class BaseView:
184
203
  class GraphQLView(
185
204
  BaseView,
186
205
  SyncBaseHTTPView[
187
- HttpRequest, HttpResponse, TemporalHttpResponse, Context, RootValue
206
+ HttpRequest, HttpResponseBase, TemporalHttpResponse, Context, RootValue
188
207
  ],
189
208
  View,
190
209
  ):
@@ -207,7 +226,7 @@ class GraphQLView(
207
226
  @method_decorator(csrf_exempt)
208
227
  def dispatch(
209
228
  self, request: HttpRequest, *args: Any, **kwargs: Any
210
- ) -> Union[HttpResponseNotAllowed, TemplateResponse, HttpResponse]:
229
+ ) -> Union[HttpResponseNotAllowed, TemplateResponse, HttpResponseBase]:
211
230
  try:
212
231
  return self.run(request=request)
213
232
  except HTTPException as e:
@@ -233,7 +252,7 @@ class GraphQLView(
233
252
  class AsyncGraphQLView(
234
253
  BaseView,
235
254
  AsyncBaseHTTPView[
236
- HttpRequest, HttpResponse, TemporalHttpResponse, Context, RootValue
255
+ HttpRequest, HttpResponseBase, TemporalHttpResponse, Context, RootValue
237
256
  ],
238
257
  View,
239
258
  ):
@@ -266,7 +285,7 @@ class AsyncGraphQLView(
266
285
  @method_decorator(csrf_exempt)
267
286
  async def dispatch( # pyright: ignore
268
287
  self, request: HttpRequest, *args: Any, **kwargs: Any
269
- ) -> Union[HttpResponseNotAllowed, TemplateResponse, HttpResponse]:
288
+ ) -> Union[HttpResponseNotAllowed, TemplateResponse, HttpResponseBase]:
270
289
  try:
271
290
  return await self.run(request=request)
272
291
  except HTTPException as e:
@@ -6,6 +6,7 @@ from inspect import signature
6
6
  from typing import (
7
7
  TYPE_CHECKING,
8
8
  Any,
9
+ AsyncIterator,
9
10
  Awaitable,
10
11
  Callable,
11
12
  Mapping,
@@ -22,6 +23,7 @@ from starlette.responses import (
22
23
  HTMLResponse,
23
24
  PlainTextResponse,
24
25
  Response,
26
+ StreamingResponse,
25
27
  )
26
28
  from starlette.websockets import WebSocket
27
29
 
@@ -322,3 +324,14 @@ class GraphQLRouter(
322
324
  response.headers.raw.extend(sub_response.headers.raw)
323
325
 
324
326
  return response
327
+
328
+ async def create_multipart_response(
329
+ self, stream: Callable[[], AsyncIterator[str]], sub_response: Response
330
+ ) -> Response:
331
+ return StreamingResponse(
332
+ stream(),
333
+ headers={
334
+ "Transfer-Encoding": "chunked",
335
+ "Content-type": "multipart/mixed;boundary=graphql;subscriptionSpec=1.0,application/json",
336
+ },
337
+ )
@@ -1,7 +1,15 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import warnings
4
- from typing import TYPE_CHECKING, Any, List, Mapping, Optional, Union, cast
4
+ from typing import (
5
+ TYPE_CHECKING,
6
+ Any,
7
+ List,
8
+ Mapping,
9
+ Optional,
10
+ Union,
11
+ cast,
12
+ )
5
13
 
6
14
  from flask import Request, Response, render_template_string, request
7
15
  from flask.views import View
@@ -1,12 +1,17 @@
1
1
  import abc
2
+ import asyncio
3
+ import contextlib
2
4
  import json
3
5
  from typing import (
6
+ Any,
7
+ AsyncGenerator,
4
8
  Callable,
5
9
  Dict,
6
10
  Generic,
7
11
  List,
8
12
  Mapping,
9
13
  Optional,
14
+ Tuple,
10
15
  Union,
11
16
  )
12
17
 
@@ -15,15 +20,20 @@ from graphql import GraphQLError
15
20
  from strawberry import UNSET
16
21
  from strawberry.exceptions import MissingQueryError
17
22
  from strawberry.file_uploads.utils import replace_placeholders_with_files
18
- from strawberry.http import GraphQLHTTPResponse, GraphQLRequestData, process_result
23
+ from strawberry.http import (
24
+ GraphQLHTTPResponse,
25
+ GraphQLRequestData,
26
+ process_result,
27
+ )
19
28
  from strawberry.http.ides import GraphQL_IDE
20
29
  from strawberry.schema.base import BaseSchema
21
30
  from strawberry.schema.exceptions import InvalidOperationTypeError
22
- from strawberry.types import ExecutionResult
31
+ from strawberry.types import ExecutionResult, SubscriptionExecutionResult
23
32
  from strawberry.types.graphql import OperationType
24
33
 
25
34
  from .base import BaseView
26
35
  from .exceptions import HTTPException
36
+ from .parse_content_type import parse_content_type
27
37
  from .types import FormData, HTTPMethod, QueryParams
28
38
  from .typevars import Context, Request, Response, RootValue, SubResponse
29
39
 
@@ -94,9 +104,14 @@ class AsyncBaseHTTPView(
94
104
  async def render_graphql_ide(self, request: Request) -> Response:
95
105
  ...
96
106
 
107
+ async def create_multipart_response(
108
+ self, stream: Callable[[], AsyncGenerator[str, None]], sub_response: SubResponse
109
+ ) -> Response:
110
+ raise ValueError("Multipart responses are not supported")
111
+
97
112
  async def execute_operation(
98
113
  self, request: Request, context: Context, root_value: Optional[RootValue]
99
- ) -> ExecutionResult:
114
+ ) -> Union[ExecutionResult, SubscriptionExecutionResult]:
100
115
  request_adapter = self.request_adapter_class(request)
101
116
 
102
117
  try:
@@ -192,6 +207,11 @@ class AsyncBaseHTTPView(
192
207
  except MissingQueryError as e:
193
208
  raise HTTPException(400, "No GraphQL query found in the request") from e
194
209
 
210
+ if isinstance(result, SubscriptionExecutionResult):
211
+ stream = self._get_stream(request, result)
212
+
213
+ return await self.create_multipart_response(stream, sub_response)
214
+
195
215
  response_data = await self.process_result(request=request, result=result)
196
216
 
197
217
  if result.errors:
@@ -201,15 +221,106 @@ class AsyncBaseHTTPView(
201
221
  response_data=response_data, sub_response=sub_response
202
222
  )
203
223
 
224
+ def encode_multipart_data(self, data: Any, separator: str) -> str:
225
+ return "".join(
226
+ [
227
+ f"\r\n--{separator}\r\n",
228
+ "Content-Type: application/json\r\n\r\n",
229
+ self.encode_json(data),
230
+ "\n",
231
+ ]
232
+ )
233
+
234
+ def _stream_with_heartbeat(
235
+ self, stream: Callable[[], AsyncGenerator[str, None]]
236
+ ) -> Callable[[], AsyncGenerator[str, None]]:
237
+ """Adds a heartbeat to the stream, to prevent the connection from closing
238
+ when there are no messages being sent."""
239
+ # TODO: handle errors
240
+ # TODO: should we do this more efficiently? and only send the heartbeat when
241
+ # 5 seconds have passed after the last message? (apollo router doesn't seem to do this)
242
+ queue = asyncio.Queue[Tuple[bool, Any]](1)
243
+
244
+ cancelling = False
245
+
246
+ async def drain():
247
+ try:
248
+ async for item in stream():
249
+ await queue.put((False, item))
250
+ except Exception as e:
251
+ if not cancelling:
252
+ await queue.put((True, e))
253
+ else:
254
+ raise
255
+
256
+ async def heartbeat():
257
+ while True:
258
+ await queue.put((False, self.encode_multipart_data({}, "graphql")))
259
+
260
+ await asyncio.sleep(5)
261
+
262
+ async def merged() -> AsyncGenerator[str, None]:
263
+ heartbeat_task = asyncio.create_task(heartbeat())
264
+ task = asyncio.create_task(drain())
265
+
266
+ async def cancel_tasks():
267
+ nonlocal cancelling
268
+ cancelling = True
269
+ task.cancel()
270
+
271
+ with contextlib.suppress(asyncio.CancelledError):
272
+ await task
273
+
274
+ heartbeat_task.cancel()
275
+
276
+ with contextlib.suppress(asyncio.CancelledError):
277
+ await heartbeat_task
278
+
279
+ try:
280
+ while not task.done():
281
+ raised, data = await queue.get()
282
+
283
+ if raised:
284
+ await cancel_tasks()
285
+ raise data
286
+
287
+ yield data
288
+ finally:
289
+ await cancel_tasks()
290
+
291
+ return merged
292
+
293
+ def _get_stream(
294
+ self,
295
+ request: Request,
296
+ result: SubscriptionExecutionResult,
297
+ separator: str = "graphql",
298
+ ) -> Callable[[], AsyncGenerator[str, None]]:
299
+ async def stream():
300
+ async for value in result:
301
+ response = await self.process_result(request, value)
302
+ yield self.encode_multipart_data({"payload": response}, separator)
303
+
304
+ yield f"\r\n--{separator}--\r\n"
305
+
306
+ return self._stream_with_heartbeat(stream)
307
+
308
+ async def parse_multipart_subscriptions(
309
+ self, request: AsyncHTTPRequestAdapter
310
+ ) -> Dict[str, str]:
311
+ return self.parse_json(await request.get_body())
312
+
204
313
  async def parse_http_body(
205
314
  self, request: AsyncHTTPRequestAdapter
206
315
  ) -> GraphQLRequestData:
207
- content_type = request.content_type or ""
316
+ content_type, params = parse_content_type(request.content_type or "")
208
317
 
209
- if "application/json" in content_type:
318
+ if content_type == "application/json":
210
319
  data = self.parse_json(await request.get_body())
211
- elif content_type.startswith("multipart/form-data"):
320
+ elif content_type == "multipart/form-data":
212
321
  data = await self.parse_multipart(request)
322
+ elif self._is_multipart_subscriptions(content_type, params):
323
+ data = await self.parse_multipart_subscriptions(request)
213
324
  elif request.method == "GET":
214
325
  data = self.parse_query_params(request.query_params)
215
326
  else:
@@ -76,3 +76,16 @@ class BaseView(Generic[Request]):
76
76
  replace_variables=self._ide_replace_variables,
77
77
  graphql_ide=self.graphql_ide,
78
78
  )
79
+
80
+ def _is_multipart_subscriptions(
81
+ self, content_type: str, params: Dict[str, str]
82
+ ) -> bool:
83
+ if content_type != "multipart/mixed":
84
+ return False
85
+
86
+ if params.get("boundary") != "graphql":
87
+ return False
88
+
89
+ return tuple(
90
+ part.strip() for part in params.get("subscriptionspec", "").split(",")
91
+ ) == ("1.0", "application/json")
@@ -0,0 +1,17 @@
1
+ from email.message import Message
2
+ from typing import Dict, Tuple
3
+
4
+
5
+ def parse_content_type(content_type: str) -> Tuple[str, Dict[str, str]]:
6
+ """Parse a content type header into a mime-type and a dictionary of parameters."""
7
+
8
+ email = Message()
9
+ email["content-type"] = content_type
10
+
11
+ params = email.get_params()
12
+
13
+ assert params
14
+
15
+ mime_type, _ = params.pop(0)
16
+
17
+ return mime_type, dict(params)
@@ -16,7 +16,11 @@ from graphql import GraphQLError
16
16
  from strawberry import UNSET
17
17
  from strawberry.exceptions import MissingQueryError
18
18
  from strawberry.file_uploads.utils import replace_placeholders_with_files
19
- from strawberry.http import GraphQLHTTPResponse, GraphQLRequestData, process_result
19
+ from strawberry.http import (
20
+ GraphQLHTTPResponse,
21
+ GraphQLRequestData,
22
+ process_result,
23
+ )
20
24
  from strawberry.http.ides import GraphQL_IDE
21
25
  from strawberry.schema import BaseSchema
22
26
  from strawberry.schema.exceptions import InvalidOperationTypeError
@@ -25,6 +29,7 @@ from strawberry.types.graphql import OperationType
25
29
 
26
30
  from .base import BaseView
27
31
  from .exceptions import HTTPException
32
+ from .parse_content_type import parse_content_type
28
33
  from .types import HTTPMethod, QueryParams
29
34
  from .typevars import Context, Request, Response, RootValue, SubResponse
30
35
 
@@ -144,14 +149,18 @@ class SyncBaseHTTPView(
144
149
  raise HTTPException(400, "File(s) missing in form data") from e
145
150
 
146
151
  def parse_http_body(self, request: SyncHTTPRequestAdapter) -> GraphQLRequestData:
147
- content_type = request.content_type or ""
152
+ content_type, params = parse_content_type(request.content_type or "")
148
153
 
149
- if "application/json" in content_type:
154
+ if content_type == "application/json":
150
155
  data = self.parse_json(request.body)
151
- elif content_type.startswith("multipart/form-data"):
156
+ elif content_type == "multipart/form-data":
152
157
  data = self.parse_multipart(request)
153
158
  elif request.method == "GET":
154
159
  data = self.parse_query_params(request.query_params)
160
+ elif self._is_multipart_subscriptions(content_type, params):
161
+ raise HTTPException(
162
+ 400, "Multipart subcriptions are not supported in sync mode"
163
+ )
155
164
  else:
156
165
  raise HTTPException(400, "Unsupported content type")
157
166