Beispiel #1
0
def ast_from_value(value: Any, type_: GraphQLInputType) -> Optional[ValueNode]:
    """
    This is a partial copy paste of the ast_from_value function in
    graphql-core utilities/ast_from_value.py

    Overwrite the if blocks that use recursion and add a new case to return a
    VariableNode when value is a DSLVariable

    Produce a GraphQL Value AST given a Python object.
    """
    if isinstance(value, DSLVariable):
        return value.set_type(type_).ast_variable

    if is_non_null_type(type_):
        type_ = cast(GraphQLNonNull, type_)
        ast_value = ast_from_value(value, type_.of_type)
        if isinstance(ast_value, NullValueNode):
            return None
        return ast_value

    # only explicit None, not Undefined or NaN
    if value is None:
        return NullValueNode()

    # undefined
    if value is Undefined:
        return None

    # Convert Python list to GraphQL list. If the GraphQLType is a list, but the value
    # is not a list, convert the value using the list's item type.
    if is_list_type(type_):
        type_ = cast(GraphQLList, type_)
        item_type = type_.of_type
        if isinstance(value, Iterable) and not isinstance(value, str):
            maybe_value_nodes = (ast_from_value(item, item_type) for item in value)
            value_nodes = filter(None, maybe_value_nodes)
            return ListValueNode(values=FrozenList(value_nodes))
        return ast_from_value(value, item_type)

    # Populate the fields of the input object by creating ASTs from each value in the
    # Python dict according to the fields in the input type.
    if is_input_object_type(type_):
        if value is None or not isinstance(value, Mapping):
            return None
        type_ = cast(GraphQLInputObjectType, type_)
        field_items = (
            (field_name, ast_from_value(value[field_name], field.type))
            for field_name, field in type_.fields.items()
            if field_name in value
        )
        field_nodes = (
            ObjectFieldNode(name=NameNode(value=field_name), value=field_value)
            for field_name, field_value in field_items
            if field_value
        )
        return ObjectValueNode(fields=FrozenList(field_nodes))

    return default_ast_from_value(value, type_)
 def can_hash():
     fl1 = FrozenList([1, 2])
     fl2 = FrozenList([1, 2])
     assert fl2 == fl1
     assert fl2 is not fl1
     assert hash(fl2) == hash(fl1)
     fl3 = FrozenList([1, 3])
     assert fl3 != fl1
     assert hash(fl3) != hash(fl1)
Beispiel #3
0
 def select(self, *fields):
     selection_set = self.ast_field.selection_set
     added_selections = selections(*fields)
     if selection_set:
         selection_set.selections = FrozenList(selection_set.selections +
                                               list(added_selections))
     else:
         self.ast_field.selection_set = SelectionSetNode(
             selections=FrozenList(added_selections))
     return self
Beispiel #4
0
    def select(self, *fields):
        selection_nodes = list(selections(*fields))
        if not self.ast_field.selection_set:
            self.ast_field.selection_set = ast.SelectionSetNode(
                selections=FrozenList(selection_nodes)
            )
            return self

        selection_nodes.extend(self.ast_field.selection_set.selections)
        self.ast_field.selection_set = ast.SelectionSetNode(
            selections=FrozenList(selection_nodes)
        )
        return self
 def can_deep_copy():
     fl11 = FrozenList([1, 2])
     fl12 = FrozenList([2, 1])
     fl1 = FrozenList([fl11, fl12])
     fl2 = deepcopy(fl1)
     assert isinstance(fl2, FrozenList)
     assert fl2 == fl1
     assert hash(fl2) == hash(fl1)
     assert isinstance(fl2[0], FrozenList)
     assert isinstance(fl2[1], FrozenList)
     assert fl2[0] == fl1[0]
     assert fl2[0] is not fl1[0]
     assert fl2[1] == fl1[1]
     assert fl2[1] is not fl1[1]
Beispiel #6
0
Datei: dsl.py Projekt: tony/gql
def get_arg_serializer(arg_type, known_serializers):
    if isinstance(arg_type, GraphQLNonNull):
        return get_arg_serializer(arg_type.of_type, known_serializers)
    if isinstance(arg_type, GraphQLInputField):
        return get_arg_serializer(arg_type.type, known_serializers)
    if isinstance(arg_type, GraphQLInputObjectType):
        if arg_type in known_serializers:
            return known_serializers[arg_type]
        known_serializers[arg_type] = None
        serializers = {
            k: get_arg_serializer(v, known_serializers)
            for k, v in arg_type.fields.items()
        }
        known_serializers[arg_type] = lambda value: ObjectValueNode(
            fields=FrozenList(
                ObjectFieldNode(name=NameNode(value=k),
                                value=serializers[k](v))
                for k, v in value.items()))
        return known_serializers[arg_type]
    if isinstance(arg_type, GraphQLList):
        inner_serializer = get_arg_serializer(arg_type.of_type,
                                              known_serializers)
        return partial(serialize_list, inner_serializer)
    if isinstance(arg_type, GraphQLEnumType):
        return lambda value: EnumValueNode(value=arg_type.serialize(value))
    return lambda value: ast_from_value(arg_type.serialize(value), arg_type)
def _remove_empty(node: graphql.ValueNode) -> graphql.ValueNode:
    """Recursive part for removing empty child nodes."""
    if isinstance(node, graphql.ListValueNode) and node.values is not None:
        node.values = FrozenList([
            _remove_empty(node) for node in node.values if not is_empty(node)
        ])
    return node
Beispiel #8
0
    def args(self, **kwargs) -> "DSLField":
        r"""Set the arguments of a field

        The arguments are parsed to be stored in the AST of this field.

        .. note::
            You can also call the field directly with your arguments.
            :code:`ds.Query.human(id=1000)` is equivalent to:
            :code:`ds.Query.human.args(id=1000)`

        :param \**kwargs: the arguments (keyword=value)
        :return: itself

        :raises KeyError: if any of the provided arguments does not exist
                          for this field.
        """

        assert self.ast_field.arguments is not None

        self.ast_field.arguments = FrozenList(
            self.ast_field.arguments
            + [
                ArgumentNode(
                    name=NameNode(value=name),
                    value=ast_from_value(value, self._get_argument(name).type),
                )
                for name, value in kwargs.items()
            ]
        )

        log.debug(f"Added arguments {kwargs} in field {self!r})")

        return self
        def schema_extension_ast_are_available_from_schema_object():
            schema = extend_test_schema("""
                extend schema {
                  mutation: Mutation
                }
                type Mutation

                extend schema {
                  subscription: Subscription
                }
                type Subscription
                """)

            ast = parse("""
                extend schema @foo
                """)
            schema = extend_schema(schema, ast)

            nodes = schema.extension_ast_nodes or FrozenList()
            assert "".join(print_ast(node) + "\n"
                           for node in nodes) == dedent("""
                extend schema {
                  mutation: Mutation
                }
                extend schema {
                  subscription: Subscription
                }
                extend schema @foo
                """)
 def can_copy():
     fl1 = FrozenList([1, 2])
     fl2 = copy(fl1)
     assert isinstance(fl2, FrozenList)
     assert fl2 == fl1
     assert hash(fl2) == hash(fl1)
     assert fl2 is not fl1
Beispiel #11
0
def dsl_gql(
    *operations: "DSLOperation", **operations_with_name: "DSLOperation"
) -> DocumentNode:
    r"""Given arguments instances of :class:`DSLOperation`
    containing GraphQL operations,
    generate a Document which can be executed later in a
    gql client or a gql session.

    Similar to the :func:`gql.gql` function but instead of parsing a python
    string to describe the request, we are using operations which have been generated
    dynamically using instances of :class:`DSLField`, generated
    by instances of :class:`DSLType` which themselves originated from
    a :class:`DSLSchema` class.

    :param \*operations: the GraphQL operations
    :type \*operations: DSLOperation (DSLQuery, DSLMutation, DSLSubscription)
    :param \**operations_with_name: the GraphQL operations with an operation name
    :type \**operations_with_name: DSLOperation (DSLQuery, DSLMutation, DSLSubscription)

    :return: a Document which can be later executed or subscribed by a
        :class:`Client <gql.client.Client>`, by an
        :class:`async session <gql.client.AsyncClientSession>` or by a
        :class:`sync session <gql.client.SyncClientSession>`

    :raises TypeError: if an argument is not an instance of :class:`DSLOperation`
    """

    # Concatenate operations without and with name
    all_operations: Tuple["DSLOperation", ...] = (
        *operations,
        *(operation for operation in operations_with_name.values()),
    )

    # Set the operation name
    for name, operation in operations_with_name.items():
        operation.name = name

    # Check the type
    for operation in all_operations:
        if not isinstance(operation, DSLOperation):
            raise TypeError(
                "Operations should be instances of DSLOperation "
                "(DSLQuery, DSLMutation or DSLSubscription).\n"
                f"Received: {type(operation)}."
            )

    return DocumentNode(
        definitions=[
            OperationDefinitionNode(
                operation=OperationType(operation.operation_type),
                selection_set=operation.selection_set,
                variable_definitions=FrozenList(
                    operation.variable_definitions.get_ast_definitions()
                ),
                **({"name": NameNode(value=operation.name)} if operation.name else {}),
            )
            for operation in all_operations
        ]
    )
Beispiel #12
0
def print_test_schema_changes(extended_schema):
    ast = parse(print_schema(extended_schema))
    ast.definitions = FrozenList(
        node
        for node in ast.definitions
        if print_ast(node) not in test_schema_definitions
    )
    return print_ast(ast)
 def can_read():
     fl = FrozenList([1, 2, 3])
     assert fl == [1, 2, 3]
     assert list(i for i in fl) == fl
     assert fl.copy() == fl
     assert 2 in fl
     assert 4 not in fl
     assert fl + [4, 5] == [1, 2, 3, 4, 5]
     assert [4, 5] + fl == [4, 5, 1, 2, 3]
     assert fl * 2 == [1, 2, 3, 1, 2, 3]
     assert 2 * fl == [1, 2, 3, 1, 2, 3]
     assert fl[1] == 2
     with raises(IndexError):
         fl[3]
     assert fl[1:4] == [2, 3]
     assert fl[::2] == [1, 3]
     assert len(fl) == 3
     assert min(fl) == 1
     assert max(fl) == 3
     assert sum(fl) == 6
     assert fl.index(2) == 1
     with raises(ValueError):
         fl.index(4)
     assert fl.count(2) == 1
     assert fl.count(4) == 0
     assert list(reversed(fl)) == [3, 2, 1]
     assert sorted(fl) == [1, 2, 3]
Beispiel #14
0
def query(*fields, **kwargs):
    if "operation" not in kwargs:
        kwargs["operation"] = "query"
    return DocumentNode(definitions=[
        OperationDefinitionNode(
            operation=OperationType(kwargs["operation"]),
            selection_set=SelectionSetNode(
                selections=FrozenList(selections(*fields))),
        )
    ])
Beispiel #15
0
def print_schema_changes(schema: GraphQLSchema,
                         extended_schema: GraphQLSchema) -> str:
    schema_definitions = [
        print_ast(definition)
        for definition in parse(print_schema(schema)).definitions
    ]
    ast = parse(print_schema(extended_schema))
    return print_ast(
        DocumentNode(definitions=FrozenList(
            node for node in ast.definitions
            if print_ast(node) not in schema_definitions)))
Beispiel #16
0
 def args(self, **kwargs):
     added_args = []
     for name, value in kwargs.items():
         arg = self.field.args.get(name)
         arg_type_serializer = get_arg_serializer(arg.type)
         serialized_value = arg_type_serializer(value)
         added_args.append(
             ArgumentNode(name=NameNode(value=name),
                          value=serialized_value))
     ast_field = self.ast_field
     ast_field.arguments = FrozenList(ast_field.arguments + added_args)
     return self
Beispiel #17
0
    def select(
        self, *fields: "DSLField", **fields_with_alias: "DSLField"
    ) -> "DSLField":
        r"""Select the new children fields
        that we want to receive in the request.

        If used multiple times, we will add the new children fields
        to the existing children fields.

        :param \*fields: new children fields
        :type \*fields: DSLField
        :param \**fields_with_alias: new children fields with alias as key
        :type \**fields_with_alias: DSLField
        :return: itself

        :raises TypeError: if any of the provided fields are not instances
                           of the :class:`DSLField` class.
        """

        # Concatenate fields without and with alias
        added_fields: Tuple["DSLField", ...] = self.get_aliased_fields(
            fields, fields_with_alias
        )

        added_selections: List[FieldNode] = self.get_ast_fields(added_fields)

        current_selection_set: Optional[SelectionSetNode] = self.ast_field.selection_set

        if current_selection_set is None:
            self.ast_field.selection_set = SelectionSetNode(
                selections=FrozenList(added_selections)
            )
        else:
            current_selection_set.selections = FrozenList(
                current_selection_set.selections + added_selections
            )

        log.debug(f"Added fields: {fields} in {self!r}")

        return self
Beispiel #18
0
 def args(self, **kwargs):
     added_args = []
     for name, value in kwargs.items():
         arg = self.field.args.get(name)
         if not arg:
             raise KeyError(
                 f"Argument {name} does not exist in {self.field}.")
         arg_type_serializer = get_arg_serializer(arg.type)
         serialized_value = arg_type_serializer(value)
         added_args.append(
             ArgumentNode(name=NameNode(value=name),
                          value=serialized_value))
     ast_field = self.ast_field
     ast_field.arguments = FrozenList(ast_field.arguments + added_args)
     return self
def remove_directives_from_ast(ast, directive_names_to_omit):
    """Return an equivalent AST to the input, but with instances of the named directives omitted.

    Args:
        ast: GraphQL library AST object, such as a Field, InlineFragment, or OperationDefinition
        directive_names_to_omit: set of strings describing the names of the directives to omit

    Returns:
        GraphQL library AST object, equivalent to the input one, with all instances of
        the named directives omitted. If the specified directives do not appear in the input AST,
        the returned object is the exact same object as the input.
    """
    if not isinstance(
            ast, (FieldNode, InlineFragmentNode, OperationDefinitionNode)):
        return ast

    made_changes = False

    new_selection_set = None
    if ast.selection_set is not None:
        new_selections = []
        for selection_ast in ast.selection_set.selections:
            new_selection_ast = remove_directives_from_ast(
                selection_ast, directive_names_to_omit)

            if selection_ast is not new_selection_ast:
                # Since we did not get the exact same object as the input, changes were made.
                # That means this call will also need to make changes and return a new object.
                made_changes = True

            new_selections.append(new_selection_ast)
        new_selection_set = SelectionSetNode(selections=new_selections)

    directives_to_keep = FrozenList([
        directive for directive in ast.directives
        if directive.name.value not in directive_names_to_omit
    ])
    if len(directives_to_keep) != len(ast.directives):
        made_changes = True

    if not made_changes:
        # We didn't change anything, return the original input object.
        return ast

    new_ast = copy(ast)
    new_ast.selection_set = new_selection_set
    new_ast.directives = directives_to_keep
    return new_ast
Beispiel #20
0
    def __init__(
        self, *fields: "DSLField", **fields_with_alias: "DSLField",
    ):
        r"""Given arguments of type :class:`DSLField` containing GraphQL requests,
        generate an operation which can be converted to a Document
        using the :func:`dsl_gql <gql.dsl.dsl_gql>`.

        The fields arguments should be fields of root GraphQL types
        (Query, Mutation or Subscription) and correspond to the
        operation_type of this operation.

        :param \*fields: root instances of the dynamically generated requests
        :type \*fields: DSLField
        :param \**fields_with_alias: root instances fields with alias as key
        :type \**fields_with_alias: DSLField

        :raises TypeError: if an argument is not an instance of :class:`DSLField`
        :raises AssertionError: if an argument is not a field which correspond
                                to the operation type
        """

        self.name: Optional[str] = None
        self.variable_definitions: DSLVariableDefinitions = DSLVariableDefinitions()

        # Concatenate fields without and with alias
        all_fields: Tuple["DSLField", ...] = DSLField.get_aliased_fields(
            fields, fields_with_alias
        )

        # Check that we receive only arguments of type DSLField
        # And that the root type correspond to the operation
        for field in all_fields:
            if not isinstance(field, DSLField):
                raise TypeError(
                    (
                        "fields must be instances of DSLField. "
                        f"Received type: {type(field)}"
                    )
                )
            assert field.type_name.upper() == self.operation_type.name, (
                f"Invalid root field for operation {self.operation_type.name}.\n"
                f"Received: {field.type_name}"
            )

        self.selection_set: SelectionSetNode = SelectionSetNode(
            selections=FrozenList(DSLField.get_ast_fields(all_fields))
        )
Beispiel #21
0
    def args(self, **kwargs):
        if self.camelcase:
            self.args_to_camelcase(kwargs)
        argument_nodes = list()
        for name, value in kwargs.items():
            arg = self.field.args.get(name)
            if not arg:
                raise ValueError(f"Invalid argument {name} for field {self.name}")
            arg_type_serializer = get_arg_serializer(arg.type)
            value = arg_type_serializer(value)

            argument_nodes.append(
                ast.ArgumentNode(
                    name=ast.NameNode(value=name), value=get_ast_value(value)
                )
            )
        self.ast_field.arguments = FrozenList(argument_nodes)
        return self
Beispiel #22
0
    def __init__(
        self,
        name: str,
        graphql_type: Union[GraphQLObjectType, GraphQLInterfaceType],
        graphql_field: GraphQLField,
    ):
        """Initialize the DSLField.

        .. warning::
            Don't instantiate this class yourself.
            Use attributes of the :class:`DSLType` instead.

        :param name: the name of the field
        :param graphql_type: the GraphQL type definition from the schema
        :param graphql_field: the GraphQL field definition from the schema
        """
        self._type: Union[GraphQLObjectType, GraphQLInterfaceType] = graphql_type
        self.field: GraphQLField = graphql_field
        self.ast_field: FieldNode = FieldNode(
            name=NameNode(value=name), arguments=FrozenList()
        )
        log.debug(f"Creating {self!r}")
Beispiel #23
0
 def should_return_true_for_frozen_lists():
     assert is_collection(FrozenList()) is True
     assert is_collection(FrozenList([0, 1, 2])) is True
     assert is_collection(FrozenList(["A", "B", "C"])) is True
Beispiel #24
0
 def __init__(self, name, field):
     self.field = field
     self.ast_field = FieldNode(name=NameNode(value=name),
                                arguments=FrozenList())
     self.selection_set = None
 def cannot_write():
     fl = FrozenList([1, 2, 3])
     with raises(FrozenError):
         fl[1] = 4
     with raises(FrozenError):
         fl[1:4] = [4]
     with raises(FrozenError):
         del fl[1]
     with raises(FrozenError):
         del fl[1:4]
     with raises(FrozenError):
         fl[1::2] = [4]
     with raises(FrozenError):
         del fl[::2]
     with raises(FrozenError):
         fl.append(4)
     with raises(FrozenError):
         fl.clear()
     with raises(FrozenError):
         fl.extend([4])
     with raises(FrozenError):
         fl += [4]
     with raises(FrozenError):
         fl *= 2
     with raises(FrozenError):
         fl.insert(1, 4)
     with raises(FrozenError):
         fl.pop()
     with raises(FrozenError):
         fl.remove(2)
     with raises(FrozenError):
         fl.sort()
     with raises(FrozenError):
         fl.reverse()
     assert fl == [1, 2, 3]
Beispiel #26
0
def serialize_list(serializer, list_values):
    assert isinstance(
        list_values,
        Iterable), f'Expected iterable, received "{list_values!r}".'
    return ListValueNode(values=FrozenList(serializer(v) for v in list_values))
 def can_add_rol():
     fl1 = FrozenList([1, 2])
     rol2 = FrozenList([3, 4])
     assert fl1 + rol2 == [1, 2, 3, 4]
 def can_add_tuple():
     fl = FrozenList([1, 2])
     assert fl + (3, 4) == [1, 2, 3, 4]
Beispiel #29
0
def get_schema_with_macros(macro_registry):
    """Get a new GraphQLSchema with fields where macro edges can be used.

    Preconditions:
    1. No macro in the registry has the same name as a field on the vertex where it applies.
    2. Members of a union type do not have outgoing macros with the same name.

    An easy way to satisfy the preconditions is to create the macro_registry using
    create_macro_registry, and only update it with register_macro_edge, which does all
    the necessary validation.

    Postconditions:
    1. Every GraphQLQuery that uses macros from this registry appropriately should
       successfully type-check against the schema generated from this function.
    2. A GraphQLQuery that uses macros not present in the registry, or uses valid
       macros but on types they are not defined at should fail schema validation with
       the schema generated from this function.
    3. This function is total -- A valid macro registry should not fail to create a
       GraphQL schema with macros.

    Args:
        macro_registry: MacroRegistry object containing a schema and macro descriptors
                        we want to add to the schema.

    Returns:
        GraphQLSchema with additional fields where macro edges can be used.
    """
    # The easiest way to manipulate the schema is through its AST. The easiest
    # way to get an AST is to print it and parse it.
    schema_ast = parse(print_schema(macro_registry.schema_without_macros))

    fields_by_definition_name = {}
    for definition in schema_ast.definitions:
        if isinstance(definition, (ObjectTypeDefinitionNode, InterfaceTypeDefinitionNode)):
            # Cast to list (from FrozenList) to allow for updates.
            fields_by_definition_name[definition.name.value] = list(definition.fields)

    for class_name, macros_for_class in six.iteritems(macro_registry.macro_edges_at_class):
        for macro_edge_name, macro_edge_descriptor in six.iteritems(macros_for_class):
            list_type_at_target = ListTypeNode(
                type=NamedTypeNode(name=NameNode(value=macro_edge_descriptor.target_class_name))
            )
            arguments = []
            directives = [DirectiveNode(name=NameNode(value=MacroEdgeDirective.name))]
            fields_by_definition_name[class_name].append(
                FieldDefinitionNode(
                    name=NameNode(value=macro_edge_name),
                    arguments=arguments,
                    type=list_type_at_target,
                    directives=directives,
                )
            )

    new_definitions = []
    for definition in schema_ast.definitions:
        # Create new (Object)/(Interface)TypeDefinitionNode based on the updated fields.
        if isinstance(definition, ObjectTypeDefinitionNode):
            new_definitions.append(
                ObjectTypeDefinitionNode(
                    interfaces=definition.interfaces,
                    description=definition.description,
                    name=definition.name,
                    directives=definition.directives,
                    loc=definition.loc,
                    fields=FrozenList(fields_by_definition_name[definition.name.value]),
                )
            )
        elif isinstance(definition, InterfaceTypeDefinitionNode):
            new_definitions.append(
                InterfaceTypeDefinitionNode(
                    description=definition.description,
                    name=definition.name,
                    directives=definition.directives,
                    loc=definition.loc,
                    fields=FrozenList(fields_by_definition_name[definition.name.value]),
                )
            )
        else:
            new_definitions.append(definition)

    new_schema_ast = DocumentNode(definitions=new_definitions)
    return build_ast_schema(new_schema_ast)
Beispiel #30
0
 def should_return_true_for_frozen_lists():
     assert is_iterable(FrozenList()) is True
     assert is_iterable(FrozenList([0, 1, 2])) is True
     assert is_iterable(FrozenList(["A", "B", "C"])) is True