From 72bc96d1397cc5e802b54038870f7daf67d71e6d Mon Sep 17 00:00:00 2001 From: Christoph Zwerschke Date: Sat, 18 May 2019 19:03:30 +0200 Subject: [PATCH] Fix issues with Sphinx docs and add docs test --- .travis.yml | 2 +- docs/conf.py | 1 - docs/intro.rst | 2 + docs/modules/error.rst | 6 +- docs/modules/execution.rst | 2 + docs/modules/graphql.rst | 2 + docs/modules/language.rst | 24 ++++- docs/modules/pyutils.rst | 7 +- docs/modules/subscription.rst | 2 + docs/modules/type.rst | 21 ++-- docs/modules/utilities.rst | 2 + docs/modules/validation.rst | 15 ++- docs/usage/methods.rst | 2 +- docs/usage/other.rst | 4 +- docs/usage/parser.rst | 8 +- docs/usage/queries.rst | 8 +- docs/usage/resolvers.rst | 8 +- docs/usage/schema.rst | 2 + docs/usage/validator.rst | 9 +- graphql/__init__.py | 2 +- graphql/error/format_error.py | 2 +- graphql/error/invalid.py | 6 ++ graphql/language/visitor.py | 7 +- graphql/pyutils/read_only_dict.py | 1 - graphql/type/directives.py | 2 +- graphql/type/introspection.py | 23 ++-- graphql/type/scalars.py | 16 ++- graphql/type/schema.py | 7 +- graphql/utilities/strip_ignored_characters.py | 17 +-- graphql/validation/__init__.py | 12 ++- graphql/validation/rules/__init__.py | 3 + graphql/validation/specified_rules.py | 101 ++++++++++-------- graphql/validation/validation_context.py | 2 +- tests/pyutils/test_read_only_dict.py | 50 +++++++++ tests/pyutils/test_read_only_error.py | 6 ++ tox.ini | 10 +- 36 files changed, 280 insertions(+), 114 deletions(-) create mode 100644 tests/pyutils/test_read_only_dict.py create mode 100644 tests/pyutils/test_read_only_error.py diff --git a/.travis.yml b/.travis.yml index f82195a7..adc9500b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ language: python matrix: include: - - env: TOXENV=black,flake8,mypy + - env: TOXENV=black,flake8,mypy,docs python: 3.7 dist: xenial sudo: true diff --git a/docs/conf.py b/docs/conf.py index 0a884d78..8414d2ff 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -115,7 +115,6 @@ # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False - # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for diff --git a/docs/intro.rst b/docs/intro.rst index 5e0f3da7..25bd1d8d 100644 --- a/docs/intro.rst +++ b/docs/intro.rst @@ -41,6 +41,8 @@ Now you can start using GraphQL-core-next by importing from the top-level :mod:`graphql` package. Nearly everything defined in the sub-packages can also be imported directly from the top-level package. +.. currentmodule:: graphql + For instance, using the types defined in the :mod:`graphql.type` package, you can define a GraphQL schema, like this simple one:: diff --git a/docs/modules/error.rst b/docs/modules/error.rst index e36bb6d4..30ff35a9 100644 --- a/docs/modules/error.rst +++ b/docs/modules/error.rst @@ -1,6 +1,8 @@ Error ===== +.. currentmodule:: graphql.error + .. automodule:: graphql.error .. autoexception:: GraphQLError @@ -10,6 +12,4 @@ Error .. autofunction:: print_error .. autofunction:: format_error -The :mod:`graphql.error` module also contains the :const:`INVALID` singleton that is -used to describe invalid or undefined values and corresponds to the ``undefined`` -value in GraphQL.js. +.. autodata:: INVALID diff --git a/docs/modules/execution.rst b/docs/modules/execution.rst index 278296a9..aee737de 100644 --- a/docs/modules/execution.rst +++ b/docs/modules/execution.rst @@ -1,6 +1,8 @@ Execution ========= +.. currentmodule:: graphql.execution + .. automodule:: graphql.execution .. autofunction:: execute diff --git a/docs/modules/graphql.rst b/docs/modules/graphql.rst index 8a330de1..2109e44f 100644 --- a/docs/modules/graphql.rst +++ b/docs/modules/graphql.rst @@ -1,6 +1,8 @@ Reference ========= +.. currentmodule:: graphql + .. automodule:: graphql .. _top-level-functions: diff --git a/docs/modules/language.rst b/docs/modules/language.rst index 799fcf72..0cdc0fb8 100644 --- a/docs/modules/language.rst +++ b/docs/modules/language.rst @@ -99,5 +99,25 @@ Visitor .. autoclass:: ParallelVisitor .. autoclass:: TypeInfoVisitor -The module also exports the constants :const:`BREAK`, :const:`SKIP`, :const:`REMOVE` and -:const:`IDLE` that are used as special return values in the :class:`Visitor` methods. +The module also exports the following special symbols which can be used as +return values in the :class:`Visitor` methods to signal particular actions: + +.. data:: BREAK + :annotation: = True + + This return value signals that no further nodes shall be visited. + +.. data:: SKIP + :annotation: = False + + This return value signals that the current node shall be skipped. + +.. data:: REMOVE + :annotation: = Ellipsis + + This return value signals that the current node shall be deleted. + +.. data:: IDLE + :annotation: = None + + This return value signals that no additional action shall take place. diff --git a/docs/modules/pyutils.rst b/docs/modules/pyutils.rst index 431d3f3f..67951eb7 100644 --- a/docs/modules/pyutils.rst +++ b/docs/modules/pyutils.rst @@ -1,6 +1,8 @@ PyUtils ======= +.. currentmodule:: graphql.pyutils + .. automodule:: graphql.pyutils .. autofunction:: camel_to_snake @@ -18,5 +20,6 @@ PyUtils .. autofunction:: or_list .. autofunction:: quoted_or_list .. autofunction:: suggestion_list -.. autofunction:: ReadOnlyError -.. autofunction:: ReadOnlyList +.. autoclass:: ReadOnlyError +.. autoclass:: ReadOnlyList +.. autoclass:: ReadOnlyDict diff --git a/docs/modules/subscription.rst b/docs/modules/subscription.rst index 8a9cacac..2dfc4a1c 100644 --- a/docs/modules/subscription.rst +++ b/docs/modules/subscription.rst @@ -1,6 +1,8 @@ Subscription ============ +.. currentmodule:: graphql.subscription + .. automodule:: graphql.subscription .. autofunction:: subscribe diff --git a/docs/modules/type.rst b/docs/modules/type.rst index d8223350..b3d659f5 100644 --- a/docs/modules/type.rst +++ b/docs/modules/type.rst @@ -1,8 +1,11 @@ Type ==== +.. currentmodule:: graphql.type + .. automodule:: graphql.type + Definition ---------- @@ -99,6 +102,7 @@ Resolvers .. autoclass:: GraphQLTypeResolver .. autoclass:: ResponsePath + Directives ---------- @@ -116,11 +120,13 @@ Definitions .. autoclass:: GraphQLSkipDirective .. autoclass:: GraphQLDeprecatedDirective -The list of all specified directives is available as -:data:`specified_directives`. +.. autodata:: specified_directives + +.. data:: DEFAULT_DEPRECATION_REASON + :annotation: = 'No longer supported' + + String constant that can be used as the default value for ``deprecation_reason``. -The module also exports the constant :const:`DEFAULT_DEPRECATION_REASON` -that can be used as the default value for :obj:`deprecation_reason`. Introspection ------------- @@ -130,7 +136,6 @@ Predicates .. autofunction:: is_introspection_type - Definitions ^^^^^^^^^^^ @@ -139,8 +144,8 @@ Definitions .. autoclass:: TypeNameMetaFieldDef .. autoclass:: SchemaMetaFieldDef -The list of all introspection types is available as -:data:`introspection_types`. +.. autodata:: introspection_types + Scalars ------- @@ -162,6 +167,7 @@ Definitions The list of all specified directives is available as :data:`specified_directives`. + Schema ------ @@ -184,7 +190,6 @@ Functions: .. autofunction:: validate_schema - Assertions ^^^^^^^^^^ diff --git a/docs/modules/utilities.rst b/docs/modules/utilities.rst index 5d9c1fd3..dff2a907 100644 --- a/docs/modules/utilities.rst +++ b/docs/modules/utilities.rst @@ -1,6 +1,8 @@ Utilities ========= +.. currentmodule:: graphql.utilities + .. automodule:: graphql.utilities The GraphQL query recommended for a full schema introspection: diff --git a/docs/modules/validation.rst b/docs/modules/validation.rst index 68ad1df9..367a65ac 100644 --- a/docs/modules/validation.rst +++ b/docs/modules/validation.rst @@ -1,12 +1,25 @@ Validation ========== +.. currentmodule:: graphql.validation + .. automodule:: graphql.validation .. autofunction:: validate +.. autoclass:: ASTValidationContext + +.. autoclass:: ASTValidationRule + +.. autoclass:: SDLValidationContext + +.. autoclass:: SDLValidationRule + .. autoclass:: ValidationContext +.. autoclass:: ValidationRule + + Rules ----- @@ -15,6 +28,7 @@ rules in this list has been adjusted to lead to the most clear output when encou multiple validation errors: .. autodata:: specified_rules + :annotation: = ReadOnlyList([...]) Spec Section: "Executable Definitions" @@ -119,4 +133,3 @@ Spec Section: "Variables are Input Types" Spec Section: "All Variable Usages Are Allowed" .. autoclass:: VariablesInAllowedPositionRule - diff --git a/docs/usage/methods.rst b/docs/usage/methods.rst index bde53e59..77d0ab87 100644 --- a/docs/usage/methods.rst +++ b/docs/usage/methods.rst @@ -52,6 +52,6 @@ In a similar vein, you can also attach resolvers as methods to the resolved obje deeper levels than the root of the query. In that case, instead of resolving to dictionaries with keys for all the fields, as we did above, you would resolve to objects with attributes for all the fields. For instance, you would define a class ``Human`` -with a method :meth:`friends` for resolving the friends of a human. You can also make +with a method ``friends()`` for resolving the friends of a human. You can also make use of inheritance in this case. The ``Human`` class and a ``Droid`` class could inherit from a ``Character`` class and use its methods as resolvers for common fields. diff --git a/docs/usage/other.rst b/docs/usage/other.rst index 8c0dbb52..8bfb0223 100644 --- a/docs/usage/other.rst +++ b/docs/usage/other.rst @@ -1,10 +1,12 @@ Subscriptions ------------- +.. currentmodule:: graphql.subscription + Sometimes you need to not only query data from a server, but you also want to push data from the server to the client. GraphQL-core-next has you also covered here, because it implements the "Subscribe" algorithm described in the GraphQL spec. To execute a GraphQL -subscription, you must use the :func:`graphql.subscribe` method from the +subscription, you must use the :func:`subscribe` method from the :mod:`graphql.subscription` module. Instead of a single ``ExecutionResult``, this function returns an asynchronous iterator yielding a stream of those, unless there was an immediate error. Of course you will then also need to maintain a persistent channel diff --git a/docs/usage/parser.rst b/docs/usage/parser.rst index 229b4bd7..708a1c45 100644 --- a/docs/usage/parser.rst +++ b/docs/usage/parser.rst @@ -1,6 +1,8 @@ Parsing GraphQL Queries and Schema Notation ------------------------------------------- +.. currentmodule:: graphql.language + When executing GraphQL queries, the first step that happens under the hood is parsing the query. But GraphQL-core-next also exposes the parser for direct usage via the :func:`graphql.language.parse` function. When you pass this function a GraphQL source @@ -58,12 +60,12 @@ This will give the same result as manually creating the AST document:: ]) -When parsing with `no_location=False` (the default), the AST nodes will also have a -:attr:`loc` attribute carrying the information on the source code location corresponding +When parsing with ``no_location=False`` (the default), the AST nodes will also have a +``loc`` attribute carrying the information on the source code location corresponding to the AST nodes. When there is a syntax error in the GraphQL source code, then the :func:`parse` function -will raise a :exc:`GraphQLSyntaxError`. +will raise a :exc:`graphql.error.GraphQLSyntaxError`. The parser can not only be used to parse GraphQL queries, but also to parse the GraphQL schema definition language. This will result in another way of representing a GraphQL diff --git a/docs/usage/queries.rst b/docs/usage/queries.rst index 8f801048..3c0edd74 100644 --- a/docs/usage/queries.rst +++ b/docs/usage/queries.rst @@ -1,6 +1,8 @@ Executing Queries ----------------- +.. currentmodule:: graphql.execution + Now that we have defined the schema and breathed life into it with our resolver functions, we can execute arbitrary query against the schema. @@ -36,8 +38,8 @@ should get the expected result:: data={'droid': {'name': 'R2-D2', 'primaryFunction': 'Astromech'}}, errors=None) -The :class:`execution.ExecutionResult` has a :attr:`data` attribute with the actual -result, and an :attr:`errors` attribute with a list of errors if there were any. +The :class:`ExecutionResult` has a ``data`` attribute with the actual result, and an +``errors`` attribute with a list of errors if there were any. If all your resolvers work synchronously, as in our case, you can also use the :func:`graphql.graphql_sync` function to query the result in ordinary synchronous code:: @@ -118,7 +120,7 @@ Finally, let's see what happens when we try to access the secret backstory of ou While we get the name of the hero, the secret backstory fields remains empty, since its resolver function raises an error. However, we get the error that has been raised by the -resolver in the :attr:`errors` attribute of the result:: +resolver in the ``errors`` attribute of the result:: ExecutionResult( data={'hero': {'name': 'Luke Skywalker', 'secretBackstory': None}}, diff --git a/docs/usage/resolvers.rst b/docs/usage/resolvers.rst index cebf1db9..2b3c7b30 100644 --- a/docs/usage/resolvers.rst +++ b/docs/usage/resolvers.rst @@ -81,10 +81,10 @@ Note that the resolver functions get the current object as first argument. For a on the root Query type this is often not used, but a root object can also be defined when executing the query. As the second argument, they get an object containing execution information, as defined in the :class:`graphql.type.GraphQLResolveInfo` class. -This object also has a :attr:`context` attribute that can be used to provide every -resolver with contextual information like the currently logged in user, or a database -session. In our simple example we don't authenticate users and use static data instead -of a database, so we don't make use of it here. In addition to these two arguments, +This object also has a ``context`` attribute that can be used to provide every resolver +with contextual information like the currently logged in user, or a database session. +In our simple example we don't authenticate users and use static data instead of a +database, so we don't make use of it here. In addition to these two arguments, resolver functions optionally get the defined for the field in the schema, using the same names (the names are not translated from GraphQL naming conventions to Python naming conventions). diff --git a/docs/usage/schema.rst b/docs/usage/schema.rst index ed453a1b..93c09c0a 100644 --- a/docs/usage/schema.rst +++ b/docs/usage/schema.rst @@ -1,6 +1,8 @@ Building a Type Schema ---------------------- +.. currentmodule:: graphql.type + Using the classes in the :mod:`graphql.type` sub-package as building blocks, you can build a complete GraphQL type schema. diff --git a/docs/usage/validator.rst b/docs/usage/validator.rst index 605e688a..8ba21aea 100644 --- a/docs/usage/validator.rst +++ b/docs/usage/validator.rst @@ -1,6 +1,8 @@ Validating GraphQL Queries -------------------------- +.. currentmodule:: graphql.validation + When executing GraphQL queries, the second step that happens under the hood after parsing the source code is a validation against the given schema using the rules of the GraphQL specification. You can also run the validation step manually by calling the @@ -33,8 +35,9 @@ In this case, we will get:: " sub selection of subfields. Did you mean 'friends { ... }'?", locations=[SourceLocation(line=6, column=9)])] -These rules are implemented in the :mod:`graphql.validation.rules` module. Instead of -the default rules, you can also use a subset or create custom rules. The rules are based -on the :class:`graphql.validation.ValidationRule` class which is based on the +These rules are available in the :data:`specified_rules` list and implemented in the +``graphql.validation.rules`` subpackage. Instead of the default rules, +you can also use a subset or create custom rules. The rules are +based on the :class:`graphql.validation.ValidationRule` class which is based on the :class:`graphql.language.Visitor` class which provides a way of walking through an AST document using the visitor pattern. diff --git a/graphql/__init__.py b/graphql/__init__.py index 4b565e4e..af85f35c 100644 --- a/graphql/__init__.py +++ b/graphql/__init__.py @@ -34,7 +34,7 @@ - :mod:`graphql.error`: Creating and formatting GraphQL errors. - :mod:`graphql.utilities`: Common useful computations upon the GraphQL language and type objects. - - :mod:`graphql/subscription`: Subscribe to data updates. + - :mod:`graphql.subscription`: Subscribe to data updates. """ __version__ = "1.0.4" diff --git a/graphql/error/format_error.py b/graphql/error/format_error.py index 02ab1166..3c2289a2 100644 --- a/graphql/error/format_error.py +++ b/graphql/error/format_error.py @@ -8,7 +8,7 @@ def format_error(error: "GraphQLError") -> Dict[str, Any]: - """Format a GraphQL error + """Format a GraphQL error. Given a GraphQLError, format it according to the rules described by the "Response Format, Errors" section of the GraphQL Specification. diff --git a/graphql/error/invalid.py b/graphql/error/invalid.py index ca991508..9ec4dd84 100644 --- a/graphql/error/invalid.py +++ b/graphql/error/invalid.py @@ -22,3 +22,9 @@ def __ne__(self, other): # Used to indicate invalid values (like "undefined" in GraphQL.js): INVALID = InvalidType() + +INVALID.__doc__ = """Symbol for invalid or undefined values + +This singleton object is used to describe invalid or undefined values. +It corresponds to the ``undefined`` value in GraphQL.js. +""" diff --git a/graphql/language/visitor.py b/graphql/language/visitor.py index 73356d97..4cb33709 100644 --- a/graphql/language/visitor.py +++ b/graphql/language/visitor.py @@ -30,7 +30,7 @@ ] -# Special return values for the visitor function: +# Special return values for the visitor methods: # Note that in GraphQL.js these are defined differently: # BREAK = {}, SKIP = false, REMOVE = null, IDLE = undefined BREAK, SKIP, REMOVE, IDLE = True, False, Ellipsis, None @@ -139,8 +139,7 @@ def enter(self, node, key, parent, path, ancestors): :arg path: The key path to get to this node from the root node. :arg ancestors: All nodes and Arrays visited before reaching parent of this node. These correspond to array indices in `path`. - - Note: ancestors includes arrays which contain the parent of visited node. + Note: ancestors includes arrays which contain the parent of visited node. You can also define node kind specific methods by suffixing them with an underscore followed by the kind of the node to be visited. For instance, to visit `field` @@ -200,7 +199,7 @@ def visit(root: Node, visitor: Visitor, visitor_keys=None) -> Any: By returning different values from the enter and leave methods, the behavior of the visitor can be altered, including skipping over a sub-tree of the AST (by returning False), editing the AST by returning a value or None to remove the value, or to stop - the whole traversal by returning BREAK. + the whole traversal by returning `BREAK`. When using `visit()` to edit an AST, the original AST will not be modified, and a new version of the AST with the changes applied will be returned from the visit diff --git a/graphql/pyutils/read_only_dict.py b/graphql/pyutils/read_only_dict.py index 7ea36ff2..fd3d1d6b 100644 --- a/graphql/pyutils/read_only_dict.py +++ b/graphql/pyutils/read_only_dict.py @@ -32,4 +32,3 @@ def setdefault(self, key, default=None): def update(self, other=None): raise ReadOnlyError - diff --git a/graphql/type/directives.py b/graphql/type/directives.py index 0b3198b3..91ddcfaa 100644 --- a/graphql/type/directives.py +++ b/graphql/type/directives.py @@ -165,10 +165,10 @@ def assert_directive(directive: Any) -> GraphQLDirective: ) -# The full list of specified directives. specified_directives = ReadOnlyList( [GraphQLIncludeDirective, GraphQLSkipDirective, GraphQLDeprecatedDirective] ) +specified_directives.__doc__ = """The full list of specified directives.""" def is_specified_directive(directive: Any) -> bool: diff --git a/graphql/type/introspection.py b/graphql/type/introspection.py index ad4e88bf..bd262060 100644 --- a/graphql/type/introspection.py +++ b/graphql/type/introspection.py @@ -492,16 +492,19 @@ class TypeKind(Enum): # Since double underscore names are subject to name mangling in Python, # the introspection classes are best imported via this dictionary: -introspection_types = ReadOnlyDict({ - "__Schema": __Schema, - "__Directive": __Directive, - "__DirectiveLocation": __DirectiveLocation, - "__Type": __Type, - "__Field": __Field, - "__InputValue": __InputValue, - "__EnumValue": __EnumValue, - "__TypeKind": __TypeKind, -}) +introspection_types = ReadOnlyDict( + { + "__Schema": __Schema, + "__Directive": __Directive, + "__DirectiveLocation": __DirectiveLocation, + "__Type": __Type, + "__Field": __Field, + "__InputValue": __InputValue, + "__EnumValue": __EnumValue, + "__TypeKind": __TypeKind, + } +) +introspection_types.__doc__ = """A dictionary containing all introspection types.""" def is_introspection_type(type_: Any) -> bool: diff --git a/graphql/type/scalars.py b/graphql/type/scalars.py index 129c54c1..9bcb09d3 100644 --- a/graphql/type/scalars.py +++ b/graphql/type/scalars.py @@ -241,10 +241,18 @@ def parse_id_literal(ast, _variables=None): ) -specified_scalar_types = ReadOnlyDict({ - type_.name: type_ - for type_ in (GraphQLString, GraphQLInt, GraphQLFloat, GraphQLBoolean, GraphQLID) -}) +specified_scalar_types = ReadOnlyDict( + { + type_.name: type_ + for type_ in ( + GraphQLString, + GraphQLInt, + GraphQLFloat, + GraphQLBoolean, + GraphQLID, + ) + } +) def is_specified_scalar_type(type_: Any) -> bool: diff --git a/graphql/type/schema.py b/graphql/type/schema.py index c56930f0..e153f326 100644 --- a/graphql/type/schema.py +++ b/graphql/type/schema.py @@ -133,7 +133,12 @@ def __init__( ) # Build type map now to detect any errors within this schema. - initial_types = [query, mutation, subscription, introspection_types["__Schema"]] + initial_types: List[Optional[GraphQLNamedType]] = [ + query, + mutation, + subscription, + introspection_types["__Schema"], + ] if types: initial_types.extend(types) diff --git a/graphql/utilities/strip_ignored_characters.py b/graphql/utilities/strip_ignored_characters.py index 482f6b0b..1ab6f59d 100644 --- a/graphql/utilities/strip_ignored_characters.py +++ b/graphql/utilities/strip_ignored_characters.py @@ -14,12 +14,13 @@ def strip_ignored_characters(source: Union[str, Source]) -> str: Strips characters that are not significant to the validity or execution of a GraphQL document: - - UnicodeBOM - - WhiteSpace - - LineTerminator - - Comment - - Comma - - BlockString indentation + + - UnicodeBOM + - WhiteSpace + - LineTerminator + - Comment + - Comma + - BlockString indentation Note: It is required to have a delimiter character between neighboring non-punctuator tokes and this function always uses single space as delimiter. @@ -48,7 +49,7 @@ def strip_ignored_characters(source: Union[str, Source]) -> str: query SomeQuery($foo:String!$bar:String){someField(foo:$foo bar:$bar){a b{c d}}} - SDL example: + SDL example:: """ Type description @@ -60,7 +61,7 @@ def strip_ignored_characters(source: Union[str, Source]) -> str: bar: String } - Becomes:: + Becomes:: """Type description""" type Foo{"""Field description""" bar:String} ''' diff --git a/graphql/validation/__init__.py b/graphql/validation/__init__.py index b9d3ab11..664b05ca 100644 --- a/graphql/validation/__init__.py +++ b/graphql/validation/__init__.py @@ -6,7 +6,11 @@ from .validate import validate -from .validation_context import ValidationContext +from .validation_context import ( + ASTValidationContext, + SDLValidationContext, + ValidationContext, +) from .rules import ValidationRule, ASTValidationRule, SDLValidationRule @@ -93,10 +97,12 @@ __all__ = [ "validate", - "ValidationContext", - "ValidationRule", + "ASTValidationContext", "ASTValidationRule", + "SDLValidationContext", "SDLValidationRule", + "ValidationContext", + "ValidationRule", "specified_rules", "ExecutableDefinitionsRule", "FieldsOnCorrectTypeRule", diff --git a/graphql/validation/rules/__init__.py b/graphql/validation/rules/__init__.py index 8b78c381..ade50427 100644 --- a/graphql/validation/rules/__init__.py +++ b/graphql/validation/rules/__init__.py @@ -14,6 +14,7 @@ class ASTValidationRule(Visitor): + """Visitor for validation of an AST.""" context: ASTValidationContext @@ -25,6 +26,7 @@ def report_error(self, error: GraphQLError): class SDLValidationRule(ASTValidationRule): + """Visitor for validation of an SDL AST.""" context: ValidationContext @@ -33,6 +35,7 @@ def __init__(self, context: SDLValidationContext) -> None: class ValidationRule(ASTValidationRule): + """Visitor for validation using a GraphQL schema.""" context: ValidationContext diff --git a/graphql/validation/specified_rules.py b/graphql/validation/specified_rules.py index 89902637..8ae181a8 100644 --- a/graphql/validation/specified_rules.py +++ b/graphql/validation/specified_rules.py @@ -101,48 +101,59 @@ # The order of the rules in this list has been adjusted to lead to the # most clear output when encountering multiple validation errors. -specified_rules: List[RuleType] = ReadOnlyList([ - ExecutableDefinitionsRule, - UniqueOperationNamesRule, - LoneAnonymousOperationRule, - SingleFieldSubscriptionsRule, - KnownTypeNamesRule, - FragmentsOnCompositeTypesRule, - VariablesAreInputTypesRule, - ScalarLeafsRule, - FieldsOnCorrectTypeRule, - UniqueFragmentNamesRule, - KnownFragmentNamesRule, - NoUnusedFragmentsRule, - PossibleFragmentSpreadsRule, - NoFragmentCyclesRule, - UniqueVariableNamesRule, - NoUndefinedVariablesRule, - NoUnusedVariablesRule, - KnownDirectivesRule, - UniqueDirectivesPerLocationRule, - KnownArgumentNamesRule, - UniqueArgumentNamesRule, - ValuesOfCorrectTypeRule, - ProvidedRequiredArgumentsRule, - VariablesInAllowedPositionRule, - OverlappingFieldsCanBeMergedRule, - UniqueInputFieldNamesRule, -]) - -specified_sdl_rules: List[RuleType] = ReadOnlyList([ - LoneSchemaDefinitionRule, - UniqueOperationTypesRule, - UniqueTypeNamesRule, - UniqueEnumValueNamesRule, - UniqueFieldDefinitionNamesRule, - UniqueDirectiveNamesRule, - KnownTypeNamesRule, - KnownDirectivesRule, - UniqueDirectivesPerLocationRule, - PossibleTypeExtensionsRule, - KnownArgumentNamesOnDirectivesRule, - UniqueArgumentNamesRule, - UniqueInputFieldNamesRule, - ProvidedRequiredArgumentsOnDirectivesRule, -]) +specified_rules: List[RuleType] = ReadOnlyList( + [ + ExecutableDefinitionsRule, + UniqueOperationNamesRule, + LoneAnonymousOperationRule, + SingleFieldSubscriptionsRule, + KnownTypeNamesRule, + FragmentsOnCompositeTypesRule, + VariablesAreInputTypesRule, + ScalarLeafsRule, + FieldsOnCorrectTypeRule, + UniqueFragmentNamesRule, + KnownFragmentNamesRule, + NoUnusedFragmentsRule, + PossibleFragmentSpreadsRule, + NoFragmentCyclesRule, + UniqueVariableNamesRule, + NoUndefinedVariablesRule, + NoUnusedVariablesRule, + KnownDirectivesRule, + UniqueDirectivesPerLocationRule, + KnownArgumentNamesRule, + UniqueArgumentNamesRule, + ValuesOfCorrectTypeRule, + ProvidedRequiredArgumentsRule, + VariablesInAllowedPositionRule, + OverlappingFieldsCanBeMergedRule, + UniqueInputFieldNamesRule, + ] +) +specified_rules.__doc__ = """\ + This list includes all validation rules defined by the GraphQL spec. + + The order of the rules in this list has been adjusted to lead to the + most clear output when encountering multiple validation errors. + """ + +specified_sdl_rules: List[RuleType] = ReadOnlyList( + [ + LoneSchemaDefinitionRule, + UniqueOperationTypesRule, + UniqueTypeNamesRule, + UniqueEnumValueNamesRule, + UniqueFieldDefinitionNamesRule, + UniqueDirectiveNamesRule, + KnownTypeNamesRule, + KnownDirectivesRule, + UniqueDirectivesPerLocationRule, + PossibleTypeExtensionsRule, + KnownArgumentNamesOnDirectivesRule, + UniqueArgumentNamesRule, + UniqueInputFieldNamesRule, + ProvidedRequiredArgumentsOnDirectivesRule, + ] +) +specified_sdl_rules.__doc__ = """This list includes all rules for validating SDL.""" diff --git a/graphql/validation/validation_context.py b/graphql/validation/validation_context.py index b62d2ec5..7ff06cdc 100644 --- a/graphql/validation/validation_context.py +++ b/graphql/validation/validation_context.py @@ -137,7 +137,7 @@ def get_recursively_referenced_fragments( class SDLValidationContext(ASTValidationContext): - """Utility class providing a context for validation of an SDL ast. + """Utility class providing a context for validation of an SDL AST. An instance of this class is passed as the context attribute to all Validators, allowing access to commonly useful contextual information from within a validation diff --git a/tests/pyutils/test_read_only_dict.py b/tests/pyutils/test_read_only_dict.py new file mode 100644 index 00000000..cd3384db --- /dev/null +++ b/tests/pyutils/test_read_only_dict.py @@ -0,0 +1,50 @@ +from pytest import raises + +from graphql.pyutils import ReadOnlyError, ReadOnlyDict + + +def describe_read_only_list(): + def can_read(): + rod = ReadOnlyDict({1: 2, 3: 4}) + assert rod == {1: 2, 3: 4} + assert list(i for i in rod) == [1, 3] + assert rod.copy() == rod + assert 3 in rod + assert 2 not in rod + assert rod[1] == 2 + with raises(KeyError): + rod[2] + assert len(rod) == 2 + assert rod.get(1) == 2 + assert rod.get(2, 5) == 5 + assert list(rod.items()) == [(1, 2), (3, 4)] + assert list(rod.keys()) == [1, 3] + assert list(rod.values()) == [2, 4] + + def cannot_write(): + rod = ReadOnlyDict({1: 2, 3: 4}) + with raises(ReadOnlyError): + rod[1] = 2 + with raises(ReadOnlyError): + rod[4] = 5 + with raises(ReadOnlyError): + del rod[1] + with raises(ReadOnlyError): + del rod[3] + with raises(ReadOnlyError): + rod.clear() + with raises(ReadOnlyError): + rod.pop(1) + with raises(ReadOnlyError): + rod.pop(4, 5) + with raises(ReadOnlyError): + rod.popitem() + with raises(ReadOnlyError): + rod.setdefault(1, 2) + with raises(ReadOnlyError): + rod.setdefault(4, 5) + with raises(ReadOnlyError): + rod.update({1: 2}) + with raises(ReadOnlyError): + rod.update({4: 5}) + assert rod == {1: 2, 3: 4} diff --git a/tests/pyutils/test_read_only_error.py b/tests/pyutils/test_read_only_error.py new file mode 100644 index 00000000..f8ec7fc2 --- /dev/null +++ b/tests/pyutils/test_read_only_error.py @@ -0,0 +1,6 @@ +from graphql.pyutils import ReadOnlyError + + +def describe_read_only_error(): + def read_only_error_is_type_error(): + assert issubclass(ReadOnlyError, TypeError) diff --git a/tox.ini b/tox.ini index 6b2990a7..8ff44d2f 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py{36,37}, black, flake8, mypy, manifest +envlist = py{36,37}, black, flake8, mypy, docs, manifest [testenv:black] basepython = python3.7 @@ -19,6 +19,14 @@ deps = mypy commands = mypy graphql +[testenv:docs] +basepython = python3.7 +deps = + sphinx + sphinx_rtd_theme +commands = + sphinx-build -b html -nEW docs docs/_build/html + [testenv:manifest] basepython = python3.7 deps = check-manifest