Compare commits

...

6 Commits

Author SHA1 Message Date
Erik Wrede
8290326308
release: 3.4.3 2024-11-09 21:43:17 +01:00
Philipp Hagemeister
4a274b8424
fix: raise proper error when UUID parsing fails (#1582)
* Do not raise AttributeError when parsing non-string UUIDs

When a user sends a dictionary or other object as a UUID variable like `{[123]}`, previously graphene crashed with an `AttributeError`, like this:

```
(…)
  File "…/lib/python3.12/site-packages/graphql/utils/is_valid_value.py", line 78, in is_valid_value
    parse_result = type.parse_value(value)
                   ^^^^^^^^^^^^^^^^^^^^^^^
  File "…/lib/python3.12/site-packages/graphene/types/uuid.py", line 33, in parse_value
    return _UUID(value)
           ^^^^^^^^^^^^
  File "/usr/lib/python3.12/uuid.py", line 175, in __init__
    hex = hex.replace('urn:', '').replace('uuid:', '')
          ^^^^^^^^^^^
AttributeError: 'dict' object has no attribute 'replace'
```

But an `AttributeError` makes it seem like this is the server's fault, when it's obviously the client's.

Report a proper GraphQLError.

* fix: adjust exception message structure

---------

Co-authored-by: Erik Wrede <erikwrede@users.noreply.github.com>
2024-11-09 21:42:51 +01:00
Erik Wrede
b3db1c0cb2
release: 3.4.2 2024-11-09 18:18:36 +01:00
Muhammed Al-Dulaimi
3ed7bf6362
chore: Make Union meta overridable (#1583)
This PR makes the Union Options configurable, similar to how it works with ObjectTypes
---------

Co-authored-by: Erik Wrede <erikwrede@users.noreply.github.com>
2024-11-09 18:17:42 +01:00
Erik Wrede
ccae7364e5
release: 3.4.1 2024-10-27 21:16:40 +01:00
Erik Wrede
cf97cbb1de
fix: use dateutil-parse for < 3.11 support (#1581)
* fix: use dateutil-parse for < 3.11 support

* chore: lint

* chore: lint

* fix mypy deps

* fix mypy deps

* chore: lint

* chore: fix test
2024-10-27 21:14:55 +01:00
8 changed files with 49 additions and 7 deletions

View File

@ -46,7 +46,7 @@ from .types import (
from .utils.module_loading import lazy_import from .utils.module_loading import lazy_import
from .utils.resolve_only_args import resolve_only_args from .utils.resolve_only_args import resolve_only_args
VERSION = (3, 4, 0, "final", 0) VERSION = (3, 4, 3, "final", 0)
__version__ = get_version(VERSION) __version__ = get_version(VERSION)

View File

@ -1,5 +1,7 @@
import datetime import datetime
from dateutil.parser import isoparse
from graphql.error import GraphQLError from graphql.error import GraphQLError
from graphql.language import StringValueNode, print_ast from graphql.language import StringValueNode, print_ast
@ -71,7 +73,7 @@ class DateTime(Scalar):
f"DateTime cannot represent non-string value: {repr(value)}" f"DateTime cannot represent non-string value: {repr(value)}"
) )
try: try:
return datetime.datetime.fromisoformat(value) return isoparse(value)
except ValueError: except ValueError:
raise GraphQLError(f"DateTime cannot represent value: {repr(value)}") raise GraphQLError(f"DateTime cannot represent value: {repr(value)}")

View File

@ -227,6 +227,18 @@ def test_time_query_variable(sample_time):
assert result.data == {"time": isoformat} assert result.data == {"time": isoformat}
def test_support_isoformat():
isoformat = "2011-11-04T00:05:23Z"
# test time variable provided as Python time
result = schema.execute(
"""query DateTime($time: DateTime){ datetime(in: $time) }""",
variables={"time": isoformat},
)
assert not result.errors
assert result.data == {"datetime": "2011-11-04T00:05:23+00:00"}
def test_bad_variables(sample_date, sample_datetime, sample_time): def test_bad_variables(sample_date, sample_datetime, sample_time):
def _test_bad_variables(type_, input_): def _test_bad_variables(type_, input_):
result = schema.execute( result = schema.execute(

View File

@ -36,6 +36,21 @@ def test_uuidstring_query_variable():
assert result.data == {"uuid": uuid_value} assert result.data == {"uuid": uuid_value}
def test_uuidstring_invalid_argument():
uuid_value = {"not": "a string"}
result = schema.execute(
"""query Test($uuid: UUID){ uuid(input: $uuid) }""",
variables={"uuid": uuid_value},
)
assert result.errors
assert len(result.errors) == 1
assert (
result.errors[0].message
== "Variable '$uuid' got invalid value {'not': 'a string'}; UUID cannot represent value: {'not': 'a string'}"
)
def test_uuidstring_optional_uuid_input(): def test_uuidstring_optional_uuid_input():
""" """
Test that we can provide a null value to an optional input Test that we can provide a null value to an optional input

View File

@ -51,12 +51,14 @@ class Union(UnmountedType, BaseType):
""" """
@classmethod @classmethod
def __init_subclass_with_meta__(cls, types=None, **options): def __init_subclass_with_meta__(cls, types=None, _meta=None, **options):
assert ( assert (
isinstance(types, (list, tuple)) and len(types) > 0 isinstance(types, (list, tuple)) and len(types) > 0
), f"Must provide types for Union {cls.__name__}." ), f"Must provide types for Union {cls.__name__}."
_meta = UnionOptions(cls) if not _meta:
_meta = UnionOptions(cls)
_meta.types = types _meta.types = types
super(Union, cls).__init_subclass_with_meta__(_meta=_meta, **options) super(Union, cls).__init_subclass_with_meta__(_meta=_meta, **options)

View File

@ -1,5 +1,6 @@
from uuid import UUID as _UUID from uuid import UUID as _UUID
from graphql.error import GraphQLError
from graphql.language.ast import StringValueNode from graphql.language.ast import StringValueNode
from graphql import Undefined from graphql import Undefined
@ -28,4 +29,9 @@ class UUID(Scalar):
@staticmethod @staticmethod
def parse_value(value): def parse_value(value):
return _UUID(value) if isinstance(value, _UUID):
return value
try:
return _UUID(value)
except (ValueError, AttributeError):
raise GraphQLError(f"UUID cannot represent value: {repr(value)}")

View File

@ -53,7 +53,11 @@ tests_require = [
"coveralls>=3.3,<5", "coveralls>=3.3,<5",
] ]
dev_requires = ["ruff==0.5.0"] + tests_require dev_requires = [
"ruff==0.5.0",
"types-python-dateutil>=2.8.1,<3",
"mypy>=1.10,<2",
] + tests_require
setup( setup(
name="graphene", name="graphene",
@ -83,6 +87,7 @@ setup(
install_requires=[ install_requires=[
"graphql-core>=3.1,<3.3", "graphql-core>=3.1,<3.3",
"graphql-relay>=3.1,<3.3", "graphql-relay>=3.1,<3.3",
"python-dateutil>=2.7.0,<3",
"typing-extensions>=4.7.1,<5", "typing-extensions>=4.7.1,<5",
], ],
tests_require=tests_require, tests_require=tests_require,

View File

@ -20,7 +20,7 @@ commands =
[testenv:mypy] [testenv:mypy]
basepython = python3.10 basepython = python3.10
deps = deps =
mypy>=1.10,<2 .[dev]
commands = commands =
mypy graphene mypy graphene