From 51669bd51260611c97329b035efc7fc37c3004b2 Mon Sep 17 00:00:00 2001 From: Ethan Henderson Date: Fri, 24 May 2024 12:05:02 +0100 Subject: [PATCH 1/6] Implement MissingOptionalDependenciesError --- strawberry/exceptions/__init__.py | 2 ++ strawberry/exceptions/missing_dependencies.py | 22 +++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 strawberry/exceptions/missing_dependencies.py diff --git a/strawberry/exceptions/__init__.py b/strawberry/exceptions/__init__.py index f32401ac70..fa996a5886 100644 --- a/strawberry/exceptions/__init__.py +++ b/strawberry/exceptions/__init__.py @@ -12,6 +12,7 @@ from .invalid_argument_type import InvalidArgumentTypeError from .invalid_union_type import InvalidTypeForUnionMergeError, InvalidUnionTypeError from .missing_arguments_annotations import MissingArgumentsAnnotationsError +from .missing_dependencies import MissingOptionalDependenciesError from .missing_field_annotation import MissingFieldAnnotationError from .missing_return_annotation import MissingReturnAnnotationError from .not_a_strawberry_enum import NotAStrawberryEnumError @@ -186,4 +187,5 @@ class StrawberryGraphQLError(GraphQLError): "MissingFieldAnnotationError", "DuplicatedTypeName", "StrawberryGraphQLError", + "MissingOptionalDependenciesError", ] diff --git a/strawberry/exceptions/missing_dependencies.py b/strawberry/exceptions/missing_dependencies.py new file mode 100644 index 0000000000..cdec22ddc5 --- /dev/null +++ b/strawberry/exceptions/missing_dependencies.py @@ -0,0 +1,22 @@ +from __future__ import annotations + +from typing import Optional + +from .exception import StrawberryException + + +class MissingOptionalDependenciesError(StrawberryException): + """Some optional dependencies that are required for a particular + task are missing.""" + + def __init__( + self, + *, + packages: Optional[list[str]] = None, + extras: Optional[list[str]] = None, + ) -> None: + packages = packages or [] + if extras: + packages.append(f"'strawberry-graphql[{','.join(extras)}]'") + hint = f" (hint: pip install {' '.join(packages)})" if packages else "" + self.message = f"Some optional dependencies are missing{hint}" From bd70a4a404b767550c999768f63160e3286f5a4f Mon Sep 17 00:00:00 2001 From: Ethan Henderson Date: Fri, 24 May 2024 12:05:23 +0100 Subject: [PATCH 2/6] Add testing for MissingOptionalDependenciesError --- ...ass_missing_optional_dependencies_error.py | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 tests/exceptions/classes/test_exception_class_missing_optional_dependencies_error.py diff --git a/tests/exceptions/classes/test_exception_class_missing_optional_dependencies_error.py b/tests/exceptions/classes/test_exception_class_missing_optional_dependencies_error.py new file mode 100644 index 0000000000..d2d94043eb --- /dev/null +++ b/tests/exceptions/classes/test_exception_class_missing_optional_dependencies_error.py @@ -0,0 +1,43 @@ +import pytest + +from strawberry.exceptions import MissingOptionalDependenciesError + + +def test_missing_optional_dependencies_error(): + with pytest.raises(MissingOptionalDependenciesError) as exc_info: + raise MissingOptionalDependenciesError() + + assert str(exc_info.value) == "Some optional dependencies are missing" + + +def test_missing_optional_dependencies_error_packages(): + with pytest.raises(MissingOptionalDependenciesError) as exc_info: + raise MissingOptionalDependenciesError(packages=["a", "b"]) + + assert ( + str(exc_info.value) + == "Some optional dependencies are missing (hint: pip install a b)" + ) + + +def test_missing_optional_dependencies_error_extras(): + with pytest.raises(MissingOptionalDependenciesError) as exc_info: + raise MissingOptionalDependenciesError(extras=["dev", "test"]) + + assert ( + str(exc_info.value) + == "Some optional dependencies are missing (hint: pip install 'strawberry-graphql[dev,test]')" + ) + + +def test_missing_optional_dependencies_error_packages_and_extras(): + with pytest.raises(MissingOptionalDependenciesError) as exc_info: + raise MissingOptionalDependenciesError( + packages=["a", "b"], + extras=["dev", "test"], + ) + + assert ( + str(exc_info.value) + == "Some optional dependencies are missing (hint: pip install a b 'strawberry-graphql[dev,test]')" + ) From edd55120aef99b0c24aa9b84fd2d51ac2cca281e Mon Sep 17 00:00:00 2001 From: Ethan Henderson Date: Fri, 24 May 2024 12:06:48 +0100 Subject: [PATCH 3/6] Raise clearer error when the CLI extra dependencies need to be installed --- strawberry/cli/__init__.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/strawberry/cli/__init__.py b/strawberry/cli/__init__.py index f0d77cfadd..6dbaaca5f5 100644 --- a/strawberry/cli/__init__.py +++ b/strawberry/cli/__init__.py @@ -1,11 +1,15 @@ -from .commands.codegen import codegen as codegen # noqa -from .commands.export_schema import export_schema as export_schema # noqa -from .commands.server import server as server # noqa -from .commands.upgrade import upgrade as upgrade # noqa -from .commands.schema_codegen import schema_codegen as schema_codegen # noqa +try: + from .app import app + from .commands.codegen import codegen as codegen # noqa + from .commands.export_schema import export_schema as export_schema # noqa + from .commands.schema_codegen import schema_codegen as schema_codegen # noqa + from .commands.server import server as server # noqa + from .commands.upgrade import upgrade as upgrade # noqa -from .app import app + def run() -> None: + app() +except ModuleNotFoundError as exc: + from strawberry.exceptions import MissingOptionalDependenciesError -def run() -> None: - app() + raise MissingOptionalDependenciesError(extras=["cli"]) from exc From c22e8f1915f8fbe6611038b6437fc309a4ce88de Mon Sep 17 00:00:00 2001 From: Ethan Henderson Date: Fri, 24 May 2024 12:14:51 +0100 Subject: [PATCH 4/6] Add RELEASE.md for PR --- RELEASE.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 RELEASE.md diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 0000000000..0b0f808a4b --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,6 @@ +Release type: minor + +When calling the CLI without all the necessary dependencies installed, +a `MissingOptionalDependenciesError` will be raised instead of a +`ModuleNotFoundError`. This new exception will provide a more helpful +hint regarding how to fix the problem. From 274f759788758ed1e17888c410dde4608433da07 Mon Sep 17 00:00:00 2001 From: Ethan Henderson Date: Fri, 24 May 2024 12:33:04 +0100 Subject: [PATCH 5/6] Make suggested testing improvements --- ...ass_missing_optional_dependencies_error.py | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/tests/exceptions/classes/test_exception_class_missing_optional_dependencies_error.py b/tests/exceptions/classes/test_exception_class_missing_optional_dependencies_error.py index d2d94043eb..74e740a07c 100644 --- a/tests/exceptions/classes/test_exception_class_missing_optional_dependencies_error.py +++ b/tests/exceptions/classes/test_exception_class_missing_optional_dependencies_error.py @@ -7,7 +7,7 @@ def test_missing_optional_dependencies_error(): with pytest.raises(MissingOptionalDependenciesError) as exc_info: raise MissingOptionalDependenciesError() - assert str(exc_info.value) == "Some optional dependencies are missing" + assert exc_info.value.message == "Some optional dependencies are missing" def test_missing_optional_dependencies_error_packages(): @@ -15,21 +15,35 @@ def test_missing_optional_dependencies_error_packages(): raise MissingOptionalDependenciesError(packages=["a", "b"]) assert ( - str(exc_info.value) + exc_info.value.message == "Some optional dependencies are missing (hint: pip install a b)" ) +def test_missing_optional_dependencies_error_empty_packages(): + with pytest.raises(MissingOptionalDependenciesError) as exc_info: + raise MissingOptionalDependenciesError(packages=[]) + + assert exc_info.value.message == "Some optional dependencies are missing" + + def test_missing_optional_dependencies_error_extras(): with pytest.raises(MissingOptionalDependenciesError) as exc_info: raise MissingOptionalDependenciesError(extras=["dev", "test"]) assert ( - str(exc_info.value) + exc_info.value.message == "Some optional dependencies are missing (hint: pip install 'strawberry-graphql[dev,test]')" ) +def test_missing_optional_dependencies_error_empty_extras(): + with pytest.raises(MissingOptionalDependenciesError) as exc_info: + raise MissingOptionalDependenciesError(extras=[]) + + assert exc_info.value.message == "Some optional dependencies are missing" + + def test_missing_optional_dependencies_error_packages_and_extras(): with pytest.raises(MissingOptionalDependenciesError) as exc_info: raise MissingOptionalDependenciesError( @@ -38,6 +52,6 @@ def test_missing_optional_dependencies_error_packages_and_extras(): ) assert ( - str(exc_info.value) + exc_info.value.message == "Some optional dependencies are missing (hint: pip install a b 'strawberry-graphql[dev,test]')" ) From 3121c077fae203343f16e48b84630dd0bcf564d8 Mon Sep 17 00:00:00 2001 From: Patrick Arminio Date: Sat, 25 May 2024 11:02:10 +0200 Subject: [PATCH 6/6] Update base class --- strawberry/exceptions/missing_dependencies.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/strawberry/exceptions/missing_dependencies.py b/strawberry/exceptions/missing_dependencies.py index cdec22ddc5..1f0746a713 100644 --- a/strawberry/exceptions/missing_dependencies.py +++ b/strawberry/exceptions/missing_dependencies.py @@ -2,10 +2,8 @@ from typing import Optional -from .exception import StrawberryException - -class MissingOptionalDependenciesError(StrawberryException): +class MissingOptionalDependenciesError(Exception): """Some optional dependencies that are required for a particular task are missing."""