Return a GraphQL schema object corresponding to the schema of the given schema graph.

    Args:
        schema_graph: SchemaGraph
        class_to_field_type_overrides: dict, class name -> {field name -> field type},
                                       (string -> {string -> GraphQLType}). Used to override the
                                       type of a field in the class where it's first defined and all
                                       the class's subclasses.
        hidden_classes: set of strings, classes to not include in the GraphQL schema.

    Returns:
        tuple of (GraphQL schema object, GraphQL type equivalence hints dict).
        The tuple is of type (GraphQLSchema, {GraphQLObjectType -> GraphQLUnionType}).

def get_graphql_schema_from_schema_graph(schema_graph, class_to_field_type_overrides,
                                         hidden_classes):
    """Return a GraphQL schema object corresponding to the schema of the given schema graph.

    Args:
        schema_graph: SchemaGraph
        class_to_field_type_overrides: dict, class name -> {field name -> field type},
                                       (string -> {string -> GraphQLType}). Used to override the
                                       type of a field in the class where it's first defined and all
                                       the class's subclasses.
        hidden_classes: set of strings, classes to not include in the GraphQL schema.

    Returns:
        tuple of (GraphQL schema object, GraphQL type equivalence hints dict).
        The tuple is of type (GraphQLSchema, {GraphQLObjectType -> GraphQLUnionType}).
    """
    _validate_overriden_fields_are_not_defined_in_superclasses(class_to_field_type_overrides,
                                                               schema_graph)

    # The field types of subclasses must also be overridden.
    # Remember that the result returned by get_subclass_set(class_name) includes class_name itself.
    inherited_field_type_overrides = _get_inherited_field_types(class_to_field_type_overrides,
                                                                schema_graph)

    # We remove the base vertex class from the schema if it has no properties.
    # If it has no properties, it's meaningless and makes the schema less syntactically sweet.
    if not schema_graph.get_element_by_class_name(ORIENTDB_BASE_VERTEX_CLASS_NAME).properties:
        hidden_classes.add(ORIENTDB_BASE_VERTEX_CLASS_NAME)

    graphql_types = OrderedDict()
    type_equivalence_hints = OrderedDict()

    # For each vertex class, construct its analogous GraphQL type representation.
    for vertex_cls_name in sorted(schema_graph.vertex_class_names):
        vertex_cls = schema_graph.get_element_by_class_name(vertex_cls_name)
        if vertex_cls_name in hidden_classes:
            continue

        inherited_field_type_overrides.setdefault(vertex_cls_name, dict())
        field_type_overrides = inherited_field_type_overrides[vertex_cls_name]

        # We have to use delayed type binding here, because some of the type references
        # are circular: if an edge connects vertices of types A and B, then
        # GraphQL type A has a List[B] field, and type B has a List[A] field.
        # To avoid the circular dependency, GraphQL allows us to initialize the types
        # initially without their field information, and fill in their field information
        # later using a lambda function as the second argument to GraphQLObjectType.
        # This lambda function will be called on each type after all types are created
        # in their initial blank state.
        #
        # However, 'cls_name' is a variable that would not be correctly bound
        # if we naively tried to construct a lambda in-place, because Python lambdas
        # are not closures. Instead, call a function with 'cls_name' as an argument,
        # and have that function construct and return the required lambda.
        field_specification_lambda = _create_field_specification(
            schema_graph, graphql_types, field_type_overrides, hidden_classes, vertex_cls_name)

        # Abstract classes are interfaces, concrete classes are object types.
        current_graphql_type = None
        if vertex_cls.abstract:
            # "fields" is a kwarg in the interface constructor, even though
            # it's a positional arg in the object type constructor.
            current_graphql_type = GraphQLInterfaceType(vertex_cls_name,
                                                        fields=field_specification_lambda)
        else:
            # For similar reasons as the field_specification_lambda,
            # we need to create an interface specification lambda function that
            # specifies the interfaces implemented by this type.
            interface_specification_lambda = _create_interface_specification(
                schema_graph, graphql_types, hidden_classes, vertex_cls_name)

            # N.B.: Ignore the "is_type_of" argument below, it is simply a circumvention of
            #       a sanity check inside the GraphQL library. The library assumes that we'll use
            #       its execution system, so it complains that we don't provide a means to
            #       differentiate between different implementations of the same interface.
            #       We don't care, because we compile the GraphQL query to a database query.
            current_graphql_type = GraphQLObjectType(vertex_cls_name,
                                                     field_specification_lambda,
                                                     interfaces=interface_specification_lambda,
                                                     is_type_of=lambda: None)

        graphql_types[vertex_cls_name] = current_graphql_type

    # For each vertex class, construct all union types representations.
    for vertex_cls_name in sorted(schema_graph.vertex_class_names):
        vertex_cls = schema_graph.get_element_by_class_name(vertex_cls_name)
        if vertex_cls_name in hidden_classes:
            continue

        vertex_cls_subclasses = schema_graph.get_subclass_set(vertex_cls_name)
        if not vertex_cls.abstract and len(vertex_cls_subclasses) > 1:
            # In addition to creating this class' corresponding GraphQL type, we'll need a
            # union type to represent it when it appears as the endpoint of an edge.
            union_type_name = _get_union_type_name(vertex_cls_subclasses)

            # For similar reasons as the field_specification_lambda,
            # we need to create a union type specification lambda function that specifies
            # the types that this union type is composed of.
            type_specification_lambda = _create_union_types_specification(
                schema_graph, graphql_types, hidden_classes, vertex_cls_name)

            union_type = GraphQLUnionType(union_type_name, types=type_specification_lambda)
            graphql_types[union_type_name] = union_type
            type_equivalence_hints[graphql_types[vertex_cls_name]] = union_type

    # Include all abstract non-vertex classes whose only non-abstract subclasses are vertices.
    for non_graph_cls_name in sorted(schema_graph.non_graph_class_names):
        if non_graph_cls_name in hidden_classes:
            continue
        if not schema_graph.get_element_by_class_name(non_graph_cls_name).abstract:
            continue

        cls_subclasses = schema_graph.get_subclass_set(non_graph_cls_name)
        # No need to add the possible abstract class if it doesn't have subclasses besides itself.
        if len(cls_subclasses) > 1:
            all_non_abstract_subclasses_are_vertices = True

            # Check all non-abstract subclasses are vertices.
            for subclass_name in cls_subclasses:
                subclass = schema_graph.get_element_by_class_name(subclass_name)
                if subclass_name != non_graph_cls_name:
                    if not subclass.abstract and not subclass.is_vertex:
                        all_non_abstract_subclasses_are_vertices = False
                        break

            if all_non_abstract_subclasses_are_vertices:
                # Add abstract class as an interface.
                inherited_field_type_overrides.setdefault(non_graph_cls_name, dict())
                field_type_overrides = inherited_field_type_overrides[non_graph_cls_name]
                field_specification_lambda = _create_field_specification(
                    schema_graph, graphql_types, field_type_overrides, hidden_classes,
                    non_graph_cls_name)
                graphql_type = GraphQLInterfaceType(non_graph_cls_name,
                                                    fields=field_specification_lambda)
                graphql_types[non_graph_cls_name] = graphql_type

    if not graphql_types:
        raise EmptySchemaError(u'After evaluating all subclasses of V, we were not able to find '
                               u'visible schema data to import into the GraphQL schema object')

    # Create the root query GraphQL type. Consists of all non-union classes, i.e.
    # all non-abstract classes (as GraphQL types) and all abstract classes (as GraphQL interfaces).
    RootSchemaQuery = GraphQLObjectType('RootSchemaQuery', OrderedDict([
        (name, GraphQLField(value))
        for name, value in sorted(six.iteritems(graphql_types), key=lambda x: x[0])
        if not isinstance(value, GraphQLUnionType)
    ]))

    schema = GraphQLSchema(RootSchemaQuery, directives=DIRECTIVES)

    # Note that the GraphQLSchema reconstructs the set of types in the schema by recursively
    # searching through the fields of the RootSchemaQuery. Since union types can only appear in the
    # fields of other types as edges, union types with no in or out edges will not appear in the
    # schema. Therefore, we remove these unions and their keys from the type equivalence hints.
    return schema, _get_referenced_type_equivalences(graphql_types,
                                                     type_equivalence_hints)