text: '''
  GraphQL Types in Python
  =======================
  
  This module fulfill two purposes:
  
   - declare GraphQL schema in Python, just declare classes inheriting
     :class:`Type`, :class:`Interface` and fill them with
     :class:`Field` (or base types: ``str``, ``int``, ``float``,
     ``bool``). You may as well declare :class:`Enum` with
     ``__choices__`` or :class:`Union` and ``__types__``. Then
     ``__str__()`` will provide nice printout and ``__repr__()`` will
     return the GraphQL declarations (which can be tweaked with
     ``__to_graphql__()``, giving indent details). ``__bytes__()`` is
     also provided, mapping to a compact ``__to_graphql__()`` version,
     without indent.
  
   - Interpret GraphQL JSON data, by instantiating the declared classes
     with such information. While for scalar types it's just a
     pass-thru, for :class:`Type` and :class:`Interface` these will use
     the fields to provide native object with attribute or key access
     mapping to JSON, instead of ``json_data['key']['other']`` you may
     use ``obj.key.other``. Newly declared types, such as ``DateTime``
     will take care to generate native Python objects (ie:
     ``datetime.datetime``). Setting such attributes will also update
     the backing store object, including converting back to valid JSON
     values.
  
  These two improve usability of GraphQL **a lot**, pretty much like
  Django's Model helps to access data bases.
  
  :class:`Field` may be created explicitly, with information such as
  target type, arguments and GraphQL name. However, more commonly these
  are auto-generated by the container: GraphQL name, usually
  ``aFieldName`` will be created from Python name, usually
  ``a_field_name``. Basic types such as ``int``, ``str``, ``float`` or
  ``bool`` will map to ``Int``, ``String``, ``Float`` and ``Boolean``.
  
  The end-user classes and functions provided by this module are:
  
   - :class:`Schema`: top level object that will contain all
     declarations. For single-schema applications, you don't have to
     care about this since types declared without an explicit
     ``__schema__ = SchemaInstance`` member will end in the
     ``global_schema``.
  
   - :class:`Scalar`: "pass thru" everything received. Base for other
     scalar types:
  
      * :class:`Int`: ``int``
      * :class:`Float`: ``float``
      * :class:`String`: ``str``
      * :class:`Boolean`: ``bool``
      * :class:`ID`: ``str``
  
   - :class:`Enum`: also handled as a ``str``, but GraphQL syntax needs
     them without the quotes, so special handling is done. Validation is
     done using ``__choices__`` member, which is either a string (which
     will be splitted using ``str.split()``) or a list/tuple of
     strings with values.
  
   - :class:`Union`: defines the target type of a field may be one of
     the given ``__types__``.
  
   - Container types: :class:`Type`, :class:`Interface` and
     :class:`Input`. These are similar in usage, but GraphQL needs them
     defined differently. They are composed of :class:`Field`. A field
     may have arguments (:class:`ArgDict`), which is a set of
     :class:`Arg`. Arguments may contain default values or
     :class:`Variable`, which will be sent alongside the query (this
     allows to generate the query once and use variables, letting the
     server to use both together).
  
   - :func:`non_null()`, maps to GraphQL ``Type!`` and enforces the
     object is not ``None``.
  
   - :func:`list_of()`, maps to GraphQL ``[Type]`` and enforces the
     object is a list of ``Type``.
  
  This module only provide built-in scalar types. However, two other
  modules will extend the behavior for common conventions:
  
   - :mod:`sgqlc.types.datetime` will declare ``DateTime``, ``Date`` and
     ``Time``, mapping to Python's :mod:`datetime`. This also allows
     fields to be declared as ``my_date = datetime.date``,
  
   - :mod:`sgqlc.types.relay` will declare ``Node`` and ``Connection``,
     matching `Relay <https://facebook.github.io/relay/>`_ `Global
     Object Identification
     <https://facebook.github.io/relay/graphql/objectidentification.htm>`_
     and `Cursor Connections
     <https://facebook.github.io/relay/graphql/connections.htm>`_, which
     are widely used.
  
  
  Examples
  --------
  
  Common Usage
  ~~~~~~~~~~~~
  
  Common usage is to create :class:`Type` subclasses with fields without
  arguments and do not use an explicit ``__schema__``, resulting in the
  types being added to the ``global_schema``. Built-in scalars can be
  declared using the Python classes, with :mod:`sgqlc.types` classes or
  with explicit :class:`Field` instances, the :class:`ContainerTypeMeta`
  takes care to make sure they are all instance of :class:`Field` at the
  final class:
  
  >>> class TypeUsingPython(Type):
  ...     a_int = int
  ...     a_float = float
  ...     a_string = str
  ...     a_boolean = bool
  ...     a_id = id
  ...     not_a_field = 1 # not a BaseType subclass or mapped python class
  ...
  >>> TypeUsingPython  # or repr(TypeUsingPython), prints out GraphQL!
  type TypeUsingPython {
    aInt: Int
    aFloat: Float
    aString: String
    aBoolean: Boolean
    aId: ID
  }
  >>> TypeUsingPython.a_int  # or repr(Field), prints out GraphQL!
  aInt: Int
  >>> TypeUsingPython.a_int.name
  'a_int'
  >>> TypeUsingPython.a_int.graphql_name  # auto-generated from name
  'aInt'
  >>> TypeUsingPython.a_int.type  # always a :mod:`sgqlc.types` class
  scalar Int
  >>> TypeUsingPython.__schema__ is global_schema
  True
  >>> global_schema  # or repr(Schema), prints out GraphQL!
  schema {
    scalar Int
    scalar Float
    scalar String
    scalar Boolean
    scalar ID
    scalar Time
    scalar Date
    scalar DateTime
    interface Node {
      id: ID!
    }
    type PageInfo {
      endCursor: String
      startCursor: String
      hasNextPage: Boolean!
      hasPreviousPage: Boolean!
    }
    type TypeUsingPython {
      aInt: Int
      aFloat: Float
      aString: String
      aBoolean: Boolean
      aId: ID
    }
  }
  
  You can then use some standard Python operators to check fields in a
  :class:`Type`:
  
  >>> 'a_float' in TypeUsingPython
  True
  >>> 'x' in TypeUsingPython
  False
  >>> for field in TypeUsingPython:  # iterates over :class:`Field`
  ...     print(repr(field))
  ...
  aInt: Int
  aFloat: Float
  aString: String
  aBoolean: Boolean
  aId: ID
  
  As mentioned, fields can be created with basic Python types (simpler),
  with :mod:`sgqlc.types` or with :class:`Field` directly:
  
  >>> class TypeUsingSGQLC(Type):
  ...     a_int = Int
  ...     a_float = Float
  ...     a_string = String
  ...     a_boolean = Boolean
  ...     a_id = ID
  ...
  >>> TypeUsingSGQLC  # or repr(TypeUsingSGQLC), prints out GraphQL!
  type TypeUsingSGQLC {
    aInt: Int
    aFloat: Float
    aString: String
    aBoolean: Boolean
    aId: ID
  }
  
  Using :class:`Field` instances
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  
  Allows for greater control, such as explicitly define the
  ``graphql_name`` instead of generating one from the Python
  ``name``. It is also used to declare field arguments:
  
  >>> class TypeUsingFields(Type):
  ...     a_int = Field(int)         # can use Python classes
  ...     a_float = Field(float)
  ...     a_string = Field(String)   # or sgqlc.types classes
  ...     a_boolean = Field(Boolean)
  ...     a_id = Field(ID, graphql_name='anotherName') # allows customizations
  ...     pow = Field(int, args={'base': int, 'exp': int}) # with arguments
  ...     # more than 3 arguments renders each into new line
  ...     many = Field(int, args={'a': int, 'b': int, 'c': int, 'd': int})
  ...
  >>> TypeUsingFields  # or repr(TypeUsingFields), prints out GraphQL!
  type TypeUsingFields {
    aInt: Int
    aFloat: Float
    aString: String
    aBoolean: Boolean
    anotherName: ID
    pow(base: Int, exp: Int): Int
    many(
      a: Int
      b: Int
      c: Int
      d: Int
    ): Int
  }
  
  Adding types to specific :class:`Schema`
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  
  Create a schema instance and assign as ``__schema__`` class
  member. Note that previously defined types in ``base_schema`` are
  inherited, and by default ``global_schema`` is used as base schema:
  
  >>> my_schema = Schema(global_schema)
  >>> class MySchemaType(Type):
  ...     __schema__ = my_schema
  ...     i = int
  ...
  >>> class MyOtherType(Type):
  ...     i = int
  ...
  >>> 'TypeUsingPython' in my_schema
  True
  >>> 'MySchemaType' in global_schema
  False
  >>> 'MySchemaType' in my_schema
  True
  >>> 'MyOtherType' in global_schema
  True
  >>> 'MyOtherType' in my_schema  # added after my_schema was created!
  False
  >>> my_schema.MySchemaType  # access types as schema attributes
  type MySchemaType {
    i: Int
  }
  >>> my_schema['MySchemaType']  # access types as schema items
  type MySchemaType {
    i: Int
  }
  >>> for t in my_schema:  # doctest: +ELLIPSIS
  ...     print(repr(t))
  ...
  scalar Int
  scalar Float
  ...
  type MySchemaType {
    i: Int
  }
  
  
  Inheritance and Interfaces
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  
  Inheriting another type inherits all fields:
  
  >>> class MySubclass(TypeUsingPython):
  ...     sub_field = int
  ...
  >>> MySubclass
  type MySubclass {
    aInt: Int
    aFloat: Float
    aString: String
    aBoolean: Boolean
    aId: ID
    subField: Int
  }
  
  Interfaces are similar, however they emit ``implements IfaceName``:
  
  >>> class MyIface(Interface):
  ...     sub_field = int
  ...
  >>> MyIface
  interface MyIface {
    subField: Int
  }
  >>> class MySubclassWithIface(TypeUsingPython, MyIface):
  ...     pass
  ...
  >>> MySubclassWithIface
  type MySubclassWithIface implements MyIface {
    aInt: Int
    aFloat: Float
    aString: String
    aBoolean: Boolean
    aId: ID
    subField: Int
  }
  
  Although usually types are declared first, they can be declared after
  interfaces as well. Note order of fields respect inheritance order:
  
  >>> class MySubclassWithIface2(MyIface, TypeUsingPython):
  ...     pass
  ...
  >>> MySubclassWithIface2
  type MySubclassWithIface2 implements MyIface {
    subField: Int
    aInt: Int
    aFloat: Float
    aString: String
    aBoolean: Boolean
    aId: ID
  }
  
  Cross References (Loops)
  ~~~~~~~~~~~~~~~~~~~~~~~~
  
  If types link to themselves, declare as strings so they are
  lazy-evaluated:
  
  >>> class LinkToItself(Type):
  ...    other = Field('LinkToItself')
  ...    non_null_other = non_null('LinkToItself')
  ...    list_other = list_of('LinkToItself')
  ...    list_other_non_null = list_of(non_null('LinkToItself'))
  ...    non_null_list_other_non_null = non_null(list_of(non_null(
  ...        'LinkToItself')))
  ...
  >>> LinkToItself
  type LinkToItself {
    other: LinkToItself
    nonNullOther: LinkToItself!
    listOther: [LinkToItself]
    listOtherNonNull: [LinkToItself!]
    nonNullListOtherNonNull: [LinkToItself!]!
  }
  >>> LinkToItself.other.type
  type LinkToItself {
    other: LinkToItself
    nonNullOther: LinkToItself!
    listOther: [LinkToItself]
    listOtherNonNull: [LinkToItself!]
    nonNullListOtherNonNull: [LinkToItself!]!
  }
  
  Also works for two unrelated types:
  
  >>> class CrossLinkA(Type):
  ...    other = Field('CrossLinkB')
  ...    non_null_other = non_null('CrossLinkB')
  ...    list_other = list_of('CrossLinkB')
  ...    list_other_non_null = list_of(non_null('CrossLinkB'))
  ...    non_null_list_other_non_null = non_null(list_of(non_null(
  ...        'CrossLinkB')))
  ...
  >>> class CrossLinkB(Type):
  ...    other = Field('CrossLinkA')
  ...    non_null_other = non_null('CrossLinkA')
  ...    list_other = list_of('CrossLinkA')
  ...    list_other_non_null = list_of(non_null('CrossLinkA'))
  ...    non_null_list_other_non_null = non_null(list_of(non_null(
  ...        'CrossLinkA')))
  ...
  >>> CrossLinkA
  type CrossLinkA {
    other: CrossLinkB
    nonNullOther: CrossLinkB!
    listOther: [CrossLinkB]
    listOtherNonNull: [CrossLinkB!]
    nonNullListOtherNonNull: [CrossLinkB!]!
  }
  >>> CrossLinkB
  type CrossLinkB {
    other: CrossLinkA
    nonNullOther: CrossLinkA!
    listOther: [CrossLinkA]
    listOtherNonNull: [CrossLinkA!]
    nonNullListOtherNonNull: [CrossLinkA!]!
  }
  >>> CrossLinkA.other.type
  type CrossLinkB {
    other: CrossLinkA
    nonNullOther: CrossLinkA!
    listOther: [CrossLinkA]
    listOtherNonNull: [CrossLinkA!]
    nonNullListOtherNonNull: [CrossLinkA!]!
  }
  >>> CrossLinkB.other.type
  type CrossLinkA {
    other: CrossLinkB
    nonNullOther: CrossLinkB!
    listOther: [CrossLinkB]
    listOtherNonNull: [CrossLinkB!]
    nonNullListOtherNonNull: [CrossLinkB!]!
  }
  
  Special Attribute Names
  ~~~~~~~~~~~~~~~~~~~~~~~
  
  Attributes starting with ``_`` are ignored, however if for some reason
  you must use such attribute name, then declare **ALL** attributes
  in that class (no need to repeat inherited attributes from interfaces)
  using the ``__field_names__``, which should have a tuple of strings:
  
  >>> class TypeUsingSpecialAttributes(Type):
  ...     __field_names__ = ('_int', '_two_words')
  ...     _int = int
  ...     _two_words = str
  ...     not_handled = float # not declared!
  ...
  >>> TypeUsingSpecialAttributes  # or repr(TypeUsingSpecialAttributes)
  type TypeUsingSpecialAttributes {
    _int: Int
    _twoWords: String
  }
  >>> TypeUsingSpecialAttributes._int  # or repr(Field), prints out GraphQL!
  _int: Int
  >>> TypeUsingSpecialAttributes._int.name
  '_int'
  >>> TypeUsingSpecialAttributes._int.graphql_name  # auto-generated from name
  '_int'
  
  Note that while the leading underscores (``_``) are preserved, the rest of
  internal underscores are converted to camel case:
  
  >>> TypeUsingSpecialAttributes._two_words
  _twoWords: String
  >>> TypeUsingSpecialAttributes._two_words.name
  '_two_words'
  >>> TypeUsingSpecialAttributes._two_words.graphql_name
  '_twoWords'
  
      .. note::
  
          Take care with the double underscores ``__`` as Python mangles
          the name with the class name in order to "protect" and it will
          result in ``AttributeError``
  
  Note that undeclared fields won't be handled, but they still exist as regular
  python attributes, in this case it references the ``float`` class:
  
  >>> TypeUsingSpecialAttributes.not_handled
  <class 'float'>
  
  Non GraphQL Attributes
  ~~~~~~~~~~~~~~~~~~~~~~
  
  The ``__field_names__`` may also be used to allow non-GraphQL
  attributes that would otherwise be handled as such, this
  explicitly limits the scope where SGQLC will handle.
  
  Utilities
  ~~~~~~~~~
  
  One can obtain fields as container attributes or items:
  
  >>> TypeUsingPython.a_int
  aInt: Int
  >>> TypeUsingPython['a_int']
  aInt: Int
  
  However they raise exceptions if doesn't exist:
  
  >>> TypeUsingPython.does_not_exist
  Traceback (most recent call last):
    ...
  AttributeError: TypeUsingPython has no field does_not_exist
  >>> TypeUsingPython['does_not_exist']
  Traceback (most recent call last):
    ...
  KeyError: 'TypeUsingPython has no field does_not_exist'
  
  Fields show in ``dir()`` alongside with non-fields (sorted):
  
  >>> for name in dir(TypeUsingPython):
  ...     if not name.startswith('_'):
  ...         print(name)
  a_boolean
  a_float
  a_id
  a_int
  a_string
  not_a_field
  
  Unless :func:`non_null` is used, containers can be created for
  ``None``:
  
  >>> TypeUsingPython(None)
  TypeUsingPython()
  
  >>> TypeUsingPython.__to_json_value__(None) # returns None
  
  For instances, field values can be obtained or set attributes or
  items, when setting a known field, it also updates the backing store:
  
  >>> json_data = {'aInt': 1}
  >>> obj = TypeUsingPython(json_data)
  >>> obj.a_int
  1
  >>> obj['a_int']
  1
  >>> obj.a_int = 2
  >>> json_data['aInt']
  2
  >>> obj['a_int'] = 3
  >>> json_data['aInt']
  3
  >>> obj['a_float'] = 2.1 # known field!
  >>> json_data['aFloat']
  2.1
  >>> obj.a_float = 3.3 # known field!
  >>> json_data['aFloat']
  3.3
  
  Unknown fields raise exceptions when obtained, but are allowed to be
  set, however doesn't update the backing store:
  
  >>> obj.does_not_exist
  Traceback (most recent call last):
    ...
  AttributeError: 'TypeUsingPython' object has no attribute 'does_not_exist'
  >>> obj['does_not_exist']
  Traceback (most recent call last):
    ...
  KeyError: 'TypeUsingPython(a_int=3, a_float=3.3) has no field does_not_exist'
  >>> obj['does_not_exist'] = 'abc' # unknown field, no updates to json_data
  >>> json_data['does_not_exist']
  Traceback (most recent call last):
    ...
  KeyError: 'does_not_exist'
  
  While ``repr()`` prints out summary in Python-friendly syntax, ``bytes()``
  can be used to get compressed JSON with sorted keys:
  
  >>> print(repr(obj))
  TypeUsingPython(a_int=3, a_float=3.3)
  >>> print(bytes(obj).decode('utf-8'))
  {"aFloat":3.3,"aInt":3}
  
  :license: ISC
  '''
  
  __docformat__ = 'reStructuredText en'
  
  import json
  import keyword
  import re
  from collections import OrderedDict
  
  __all__ = (
      'Schema', 'Scalar', 'Enum', 'Union', 'Variable', 'Arg', 'ArgDict',
      'Field', 'Type', 'Interface', 'Input', 'Int', 'Float', 'String',
      'Boolean', 'ID', 'non_null', 'list_of',
  )
  
  
  class ODict(OrderedDict):
      def __getattr__(self, name):
          try:
              return self[name]
          except KeyError as exc:
              raise AttributeError('%s has no field %s' % (self, name)) from exc
  
  
  class Schema:
      '''The schema will contain declared types.
  
      There is a default schema called ``global_schema``, a singleton
      that is automatically assigned to every type that does not provide
      its own schema.
  
      Once types are constructed, they are automatically added to the
      schema as properties of the same name, for example
      :class:`Int` is exposed as ``schema.Int``,
      ``schema['Int']`` or ``schema.scalar['Int']``.
  
      New schema will inherit the types defined at ``base_schema``,
      which defaults to ``global_schema``, at the time of their
      creation. However types added to ``base_schema`` after the schema
      creation are not automatically picked by existing schema. The copy
      happens at construction time.
  
      New types may be added to schema using ``schema += type`` and
      removed with ``schema -= type``. However those will not affect
      their member ``type.__schema__``, which remains the same (where they
      where originally created).
  
      The schema is an iterator that will report all registered types.
      '''
      __slots__ = ('__all', '__kinds', '__cache__',
                   'query_type', 'mutation_type', 'subscription_type')
  
      def __init__(self, base_schema=None):
          self.__all = OrderedDict()
          self.__kinds = {}
          self.__cache__ = {}
          self.query_type = None
          self.mutation_type = None
          self.subscription_type = None
  
          if base_schema is None:
              try:
                  # on the first execution, global_schema is created, thus it's
                  # not available.
                  base_schema = global_schema
              except NameError:
                  pass
  
          if base_schema is not None:
              self.__all.update(base_schema.__all)
              for k, v in base_schema.__kinds.items():
                  self.__kinds.setdefault(k, ODict()).update(v)
  
      def __contains__(self, key):
          '''Checks if the type name is known in this schema.
  
          Considering ``TypeUsingPython``, previously declared in the
          module documentation:
  
          >>> 'TypeUsingPython' in global_schema
          True
          >>> 'UnknownTypeName' in global_schema
          False
          '''
          return key in self.__all
  
      def __getitem__(self, key):
          '''Get the type given its name.
  
          Considering ``TypeUsingPython``, previously declared in the
          module documentation:
  
          >>> global_schema['TypeUsingPython']
          type TypeUsingPython {
            aInt: Int
            aFloat: Float
            aString: String
            aBoolean: Boolean
            aId: ID
          }
  
          >>> global_schema['UnknownTypeName']
          Traceback (most recent call last):
            ...
          KeyError: 'UnknownTypeName'
          '''
          return self.__all[key]
  
      def __getattr__(self, key):
          '''Get the type using schema attribute.
  
          Considering ``TypeUsingPython``, previously declared in the
          module documentation:
  
          >>> global_schema.TypeUsingPython
          type TypeUsingPython {
            aInt: Int
            aFloat: Float
            aString: String
            aBoolean: Boolean
            aId: ID
          }
  
          >>> global_schema.UnknownTypeName
          Traceback (most recent call last):
            ...
          AttributeError: UnknownTypeName
  
          One can use ``Schema.kind.Type`` syntax as well, it exposes an
          :class:`ODict` object:
  
          >>> global_schema.scalar.Int
          scalar Int
          >>> global_schema.scalar['Int']
          scalar Int
          >>> global_schema.scalar.UnknownTypeName  # doctest: +ELLIPSIS
          Traceback (most recent call last):
            ...
          AttributeError: ... has no field UnknownTypeName
          >>> global_schema.type.TypeUsingPython  # doctest: +ELLIPSIS
          type TypeUsingPython {
          ...
          >>> for t in global_schema.type.values():  # doctest: +ELLIPSIS
          ...     print(repr(t))
          ...
          type PageInfo {
          ...
          type TypeUsingPython {
          ...
          type TypeUsingSGQLC {
          ...
          type TypeUsingFields {
          ...
          type MyOtherType {
          ...
          type MyType {
          ...
          }
          '''
          try:
              return self.__kinds[key]  # .type, .scalar, etc...
          except KeyError:
              pass
          try:
              return self.__all[key]
          except KeyError as exc:
              raise AttributeError(key) from exc
  
      def __iter__(self):
          '''Schema provides an iterator over :class:`BaseType`
          subclasses:
  
          >>> for t in global_schema:  # doctest: +ELLIPSIS
          ...     print(repr(t))
          ...
          scalar Int
          scalar Float
          ...
          type MyType {
          ...
          }
          '''
          return iter(self.__all.values())
  
      def __iadd__(self, typ):
          '''Manually add a type to the schema.
  
          Types are automatically once their class is created. Only use
          this if you're copying a type from one schema to another.
  
          Note that the type name ``str(typ)`` must not exist in the
          schema, otherwise :class:`ValueError` is raised.
  
          To remove a type, use ``schema -= typ``.
  
          As explained in the :mod:`sgqlc.types` documentation, the
          newly created schema will inherit types from the base schema
          only at creation time:
  
          >>> my_schema = Schema(global_schema)
          >>> class MySchemaType(Type):
          ...     __schema__ = my_schema
          ...     i = int
          ...
          >>> 'MySchemaType' in global_schema
          False
          >>> 'MySchemaType' in my_schema
          True
  
          But ``__iadd__`` and ``__isub__`` can be used to add or remove
          types:
  
          >>> global_schema += MySchemaType
          >>> 'MySchemaType' in global_schema
          True
          >>> global_schema -= MySchemaType
          >>> 'MySchemaType' in global_schema
          False
  
          Note that different type with the same name can't be added:
  
          >>> my_schema2 = Schema(global_schema)
          >>> class MySchemaType(Type):   # redefining, different schema: ok
          ...     __schema__ = my_schema2
          ...     f = float
          ...
          >>> my_schema += MySchemaType
          Traceback (most recent call last):
            ...
          ValueError: Schema already has MySchemaType=MySchemaType
          '''
          name = typ.__name__
          t = self.__all.setdefault(name, typ)
          if t is not typ:
              raise ValueError('%s already has %s=%s' %
                               (self.__class__.__name__, name, typ))
          self.__kinds.setdefault(typ.__kind__, ODict()).update({name: typ})
          if self.query_type is None and name == 'Query':
              self.query_type = t
          if self.mutation_type is None and name == 'Mutation':
              self.mutation_type = t
          if self.subscription_type is None and name == 'Subscription':
              self.subscription_type = t  # pragma: no cover
          return self
  
      def __isub__(self, typ):
          '''Remove a type from the schema.
  
          This may be of use to override some type, such as
          :class:`sgqlc.types.datetime.Date` or
          :class:`sgqlc.types.datetime.DateTime`.
          '''
          name = typ.__name__
          del self.__all[name]
          del self.__kinds[typ.__kind__][name]
          if self.query_type is typ:
              self.query_type = None  # pragma: no cover
          elif self.mutation_type is typ:
              self.mutation_type = None  # pragma: no cover
          elif self.subscription_type is typ:
              self.subscription_type = None  # pragma: no cover
          return self
  
      def __str__(self):
          '''Short schema, using only type names.
  
          Instead of declaring the whole schema in GraphQL notation as
          done by ``repr()``, just list the type names:
  
          >>> print(str(global_schema))  # doctest: +ELLIPSIS
          {Int, Float, String, Boolean, ID, ...}
  
          '''
          return '{' + ', '.join(str(e) for e in self) + '}'
  
      def __to_graphql__(self, indent=0, indent_string='  '):
          prefix = indent_string * indent
          s = [prefix + 'schema {']
          s.extend(e.__to_graphql__(indent + 1, indent_string) for e in self)
          s.append(prefix + '}')
          return '\n'.join(s)
  
      def __repr__(self):
          return self.__to_graphql__()
  
      def __bytes__(self):
          '''GraphQL schema without indentation.
  
          >>> print(bytes(global_schema).decode('utf-8'))  # doctest: +ELLIPSIS
          schema {
          scalar Int
          scalar Float
          scalar String
          ...
          }
  
          '''
          return bytes(self.__to_graphql__(indent_string=''), 'utf-8')
  
  
  global_schema = Schema()
  
  
  class BaseMeta(type):
      'Automatically adds class to its schema'
      def __init__(cls, name, bases, namespace):
          super(BaseMeta, cls).__init__(name, bases, namespace)
          if not bases or BaseType in bases or \
                  BaseTypeWithTypename in bases or ContainerType in bases:
              return
  
          auto_register_name = '_%s__auto_register' % (name,)
          auto_register = getattr(cls, auto_register_name, True)
  
          if auto_register:
              cls.__schema__ += cls
  
      def __str__(cls):
          return cls.__name__
  
      def __to_graphql__(cls, indent=0, indent_string='  '):
          prefix = indent_string * indent
          return '%s%s %s' % (prefix, cls.__kind__, cls.__name__)
  
      def __repr__(cls):
          return cls.__to_graphql__()
  
      def __bytes__(cls):
          return bytes(cls.__to_graphql__(indent_string=''), 'utf-8')
  
      def __ensure__(cls, t):
          '''Checks if ``t`` is subclass of ``BaseType`` or if a mapping
          is known.
  
          >>> BaseType.__ensure__(Int)
          scalar Int
          >>> BaseType.__ensure__(int)
          scalar Int
          >>> BaseType.__ensure__(bytes)
          Traceback (most recent call last):
             ...
          TypeError: Not BaseType or mapped: <class 'bytes'>
          '''
          if isinstance(t, type) and issubclass(t, cls):
              return t
          try:
              return map_python_to_graphql[t]
          except KeyError as exc:
              raise TypeError('Not %s or mapped: %s' % (cls, t)) from exc
  
  
  class BaseType(metaclass=BaseMeta):
      '''Base shared by all GraphQL classes.
  
      '''
      __schema__ = global_schema
      __kind__ = None
  
  
  class BaseMetaWithTypename(BaseMeta):
      'BaseMeta with ``__typename`` field (containers and union).'
      def __init__(cls, name, bases, namespace):
          super(BaseMetaWithTypename, cls).__init__(name, bases, namespace)
  
          if not bases or BaseType in bases or BaseTypeWithTypename in bases:
              return
  
          cls.__populate_meta_fields()
  
      def __populate_meta_fields(cls):
          field = Field(non_null('String'), '__typename')
          field._set_container(cls.__schema__, cls, '__typename__')
          cls.__meta_fields__ = {
              '__typename__': field,
          }
  
  
  class BaseTypeWithTypename(BaseType, metaclass=BaseMetaWithTypename):
      'BaseType with ``__typename`` field (containers and union).'
  
  
  def _create_non_null_wrapper(name, t):
      'creates type wrapper for non-null of given type'
      def realize_type(v, selection_list=None):
          if isinstance(v, (t, Variable)):
              return v
          return t(v, selection_list)
  
      def __new__(cls, json_data, selection_list=None):
          if json_data is None:
              raise ValueError(name + ' received null value')
          return realize_type(json_data, selection_list)
  
      def __to_graphql_input__(value, indent=0, indent_string='  '):
          value = realize_type(value)
          return t.__to_graphql_input__(value, indent, indent_string)
  
      return type(name, (t,), {
          '__new__': __new__,
          '_%s__auto_register' % name: False,
          '__to_graphql_input__': __to_graphql_input__,
      })
  
  
  def _create_list_of_wrapper(name, t):
      'creates type wrapper for list of given type'
      def realize_type(v, selection_list=None):
          return t(v, selection_list)
  
      def __new__(cls, json_data, selection_list=None):
          if json_data is None:
              return None
  
          if isinstance(json_data, (t, Variable)):  # pragma: no cover
              return json_data
  
          return [realize_type(v, selection_list) for v in json_data]
  
      def __to_graphql_input__(value, indent=0, indent_string='  '):
          if isinstance(value, Variable):
              return value.__to_graphql_input__(value, indent, indent_string)
  
          r = []
          for v in value:
              v = realize_type(v)
              r.append(t.__to_graphql_input__(v, indent, indent_string))
          return '[' + ', '.join(r) + ']'
  
      def __to_json_value__(value):
          if value is None:
              return None
          return [t.__to_json_value__(v) for v in value]
  
      return type(name, (t,), {
          '__new__': __new__,
          '_%s__auto_register' % name: False,
          '__to_graphql_input__': __to_graphql_input__,
          '__to_json_value__': __to_json_value__,
      })
  
  
  class Lazy:
      '''Holds a type name until it's created.
  
      This is used to solve cross reference problems, the fields will
      store an instance of this until it's evaluated.
      '''
      __slots__ = ('name', 'target_name', 'ready_cb', 'inner_lazy')
  
      def __init__(self, name, target_name, ready_cb, inner_lazy=None):
          self.name = name
          self.target_name = target_name
          self.ready_cb = ready_cb
          self.inner_lazy = inner_lazy
  
      def __repr__(self):
          '''Print out pending Lazy
  
          >>> Lazy('a', 'a!', lambda x: x)
          <Lazy name='a' target_name='a!'>
  
          '''
          return str(self)
  
      def __str__(self):
          return '<Lazy name=%r target_name=%r>' % (self.name, self.target_name)
  
      def resolve(self, schema):
          trail = []
          lazy = self
          while lazy.inner_lazy is not None:
              trail.append(lazy)
              lazy = lazy.inner_lazy
  
          t = lazy.ready_cb(schema[lazy.name])
          while trail:
              lazy = trail.pop()
              t = lazy.ready_cb(t)
  
          return t
  
  
  def non_null(t):
      '''Generates non-null type (t!)
  
      >>> class TypeWithNonNullFields(Type):
      ...     a_int = non_null(int)
      ...     a_float = non_null(Float)
      ...     a_string = Field(non_null(String))
      ...
      >>> TypeWithNonNullFields
      type TypeWithNonNullFields {
        aInt: Int!
        aFloat: Float!
        aString: String!
      }
  
      Giving proper JSON data:
  
      >>> json_data = {'aInt': 1, 'aFloat': 2.1, 'aString': 'hello'}
      >>> obj = TypeWithNonNullFields(json_data)
      >>> obj
      TypeWithNonNullFields(a_int=1, a_float=2.1, a_string='hello')
  
      Giving incorrect JSON data:
  
      >>> json_data = {'aInt': None, 'aFloat': 2.1, 'aString': 'hello'}
      >>> obj = TypeWithNonNullFields(json_data)  # doctest: +ELLIPSIS
      Traceback (most recent call last):
        ...
      ValueError: TypeWithNonNullFields selection 'a_int': ...
      >>> json_data = {'aInt': 1, 'aFloat': None, 'aString': 'hello'}
      >>> obj = TypeWithNonNullFields(json_data)  # doctest: +ELLIPSIS
      Traceback (most recent call last):
        ...
      ValueError: TypeWithNonNullFields selection 'a_float': ...
      >>> json_data = {'aInt': 1, 'aFloat': 2.1, 'aString': None}
      >>> obj = TypeWithNonNullFields(json_data)  # doctest: +ELLIPSIS
      Traceback (most recent call last):
        ...
      ValueError: TypeWithNonNullFields selection 'a_string': ...
  
      .. note::
  
         Note that **missing** keys in JSON data are not considered
         ``None``, and they won't show in ``iter(obj)``, ``__str__()``
         or ``__repr__()``
  
         >>> json_data = {'aInt': 1, 'aFloat': 2.1}
         >>> obj = TypeWithNonNullFields(json_data)
         >>> obj # repr()
         TypeWithNonNullFields(a_int=1, a_float=2.1)
         >>> for field_name in obj:
         ...     print(field_name, repr(obj[field_name]))
         ...
         a_int 1
         a_float 2.1
  
      '''
      if isinstance(t, str):
          return Lazy(t, t + '!', non_null)
      elif isinstance(t, Lazy):
          return Lazy(t.target_name, t.target_name + '!', non_null, t)
  
      t = BaseType.__ensure__(t)
      name = t.__name__ + '!'
      try:
          return t.__schema__.__cache__[name]
      except KeyError:
          pass
  
      wrapper = _create_non_null_wrapper(name, t)
      t.__schema__.__cache__[name] = wrapper
      return wrapper
  
  
  def list_of(t):
      '''Generates list of types ([t])
  
      The example below highlights the usage including its usage with
      lists:
  
      - ``non_null_list_of_int`` means it must be a list, not ``None``,
        however list elements may be ``None``, ie: ``[None, 1, None, 2]``;
  
      - ``list_of_non_null_int`` means it may be ``None`` or be a list,
        however list elements must not be ``None``, ie: ``None`` or
        ``[1, 2]``;
  
      - ``non_null_list_of_non_null_int`` means it must be a list, not
        ``None`` **and** the list elements must not be ``Non``, ie:
        ``[1, 2]``.
  
      >>> class TypeWithListFields(Type):
      ...     list_of_int = list_of(int)
      ...     list_of_float = list_of(Float)
      ...     list_of_string = Field(list_of(String))
      ...     non_null_list_of_int = non_null(list_of(int))
      ...     list_of_non_null_int = list_of(non_null(int))
      ...     non_null_list_of_non_null_int = non_null(list_of(non_null(int)))
      ...
      >>> TypeWithListFields
      type TypeWithListFields {
        listOfInt: [Int]
        listOfFloat: [Float]
        listOfString: [String]
        nonNullListOfInt: [Int]!
        listOfNonNullInt: [Int!]
        nonNullListOfNonNullInt: [Int!]!
      }
  
      It takes care to enforce proper type, including non-null checking
      on its elements when creating instances. Giving proper JSON data:
  
      >>> json_data = {
      ...     'listOfInt': [1, 2],
      ...     'listOfFloat': [1.1, 2.1],
      ...     'listOfString': ['hello', 'world'],
      ...     'nonNullListOfInt': [None, 1, None, 2],
      ...     'listOfNonNullInt': [1, 2, 3],
      ...     'nonNullListOfNonNullInt': [1, 2, 3, 4],
      ... }
      >>> obj = TypeWithListFields(json_data)
      >>> for field_name in obj:
      ...     print(field_name, repr(obj[field_name]))
      ...
      list_of_int [1, 2]
      list_of_float [1.1, 2.1]
      list_of_string ['hello', 'world']
      non_null_list_of_int [None, 1, None, 2]
      list_of_non_null_int [1, 2, 3]
      non_null_list_of_non_null_int [1, 2, 3, 4]
  
      Note that lists that are **not** enclosed in ``non_null()`` can be
      ``None``:
  
      >>> json_data = {
      ...     'listOfInt': None,
      ...     'listOfFloat': None,
      ...     'listOfString': None,
      ...     'nonNullListOfInt': [None, 1, None, 2],
      ...     'listOfNonNullInt': None,
      ...     'nonNullListOfNonNullInt': [1, 2, 3],
      ... }
      >>> obj = TypeWithListFields(json_data)
      >>> for field_name in obj:
      ...     print(field_name, repr(obj[field_name]))
      ...
      list_of_int None
      list_of_float None
      list_of_string None
      non_null_list_of_int [None, 1, None, 2]
      list_of_non_null_int None
      non_null_list_of_non_null_int [1, 2, 3]
  
      Types will be converted, so although not usual (since GraphQL
      gives you the proper JSON type), this can be done:
  
      >>> json_data = {
      ...     'listOfInt': ['1', '2'],
      ...     'listOfFloat': [1, '2.1'],
      ...     'listOfString': ['hello', 2],
      ...     'nonNullListOfInt': [None, '1', None, 2.1],
      ...     'listOfNonNullInt': ['1', 2.1, 3],
      ...     'nonNullListOfNonNullInt': ['1', 2.1, 3, 4],
      ... }
      >>> obj = TypeWithListFields(json_data)
      >>> for field_name in obj:
      ...     print(field_name, repr(obj[field_name]))
      ...
      list_of_int [1, 2]
      list_of_float [1.0, 2.1]
      list_of_string ['hello', '2']
      non_null_list_of_int [None, 1, None, 2]
      list_of_non_null_int [1, 2, 3]
      non_null_list_of_non_null_int [1, 2, 3, 4]
  
      Giving incorrect (nonconvertible) JSON data will raise exceptions:
  
      >>> json_data = { 'listOfInt': 1 }
      >>> obj = TypeWithListFields(json_data)  # doctest: +ELLIPSIS
      Traceback (most recent call last):
         ...
      ValueError: TypeWithListFields selection 'list_of_int': ...
  
      >>> json_data = { 'listOfInt': ['x'] }
      >>> obj = TypeWithListFields(json_data)  # doctest: +ELLIPSIS
      Traceback (most recent call last):
         ...
      ValueError: TypeWithListFields selection 'list_of_int': ...
  
      >>> json_data = { 'listOfNonNullInt': [1, None] }
      >>> obj = TypeWithListFields(json_data)  # doctest: +ELLIPSIS
      Traceback (most recent call last):
         ...
      ValueError: TypeWithListFields selection 'list_of_non_null_int': ...
  
  
      Lists are usable as input types as well:
  
      >>> class TypeWithListInput(Type):
      ...     a = Field(str, args={'values': Arg(list_of(int), default=[1, 2])})
      ...     b = Field(str, args={'values': Arg(list_of(int))})
      ...
      >>> TypeWithListInput
      type TypeWithListInput {
        a(values: [Int] = [1, 2]): String
        b(values: [Int]): String
      }
  
      >>> print(json.dumps(list_of(int).__to_json_value__([1, 2])))
      [1, 2]
      >>> print(json.dumps(list_of(int).__to_json_value__(None)))
      null
  
      '''
      if isinstance(t, str):
          return Lazy(t, '[' + t + ']', list_of)
      elif isinstance(t, Lazy):
          return Lazy(t.target_name, '[' + t.target_name + ']', list_of, t)
  
      t = BaseType.__ensure__(t)
      name = '[' + t.__name__ + ']'
      try:
          return t.__schema__.__cache__[name]
      except KeyError:
          pass
  
      wrapper = _create_list_of_wrapper(name, t)
      t.__schema__.__cache__[name] = wrapper
      return wrapper
  
  
  class Scalar(BaseType):
      '''Basic scalar types, passed thru (no conversion).
  
      This may be used directly if no special checks or conversions are
      needed. Otherwise use subclasses, like :class:`Int`,
      :class:`Float`, :class:`String`, :class:`Boolean`, :class:`ID`...
  
      Scalar classes will never produce instance of themselves, rather
      return the converted value (int, bool...)
  
      >>> class MyTypeWithScalar(Type):
      ...     v = Scalar
      ...
      >>> MyTypeWithScalar({'v': 1}).v
      1
      >>> MyTypeWithScalar({'v': 'abc'}).v
      'abc'
  
      Variables are passed thru:
  
      >>> MyTypeWithScalar({'v': Variable('var')}).v
      $var
  
      '''
      __kind__ = 'scalar'
      __json_dump_args__ = {}  # given to json.dumps(obj, **args)
  
      def converter(value):
          return value
  
      def __new__(cls, json_data, selection_list=None):
          if json_data is None:
              return None
          if isinstance(json_data, Variable):
              return json_data
          return cls.converter(json_data)
  
      @classmethod
      def __to_graphql_input__(cls, value, indent=0, indent_string='  '):
          if hasattr(value, '__to_graphql_input__'):
              return value.__to_graphql_input__(value, indent, indent_string)
          return json.dumps(cls.__to_json_value__(value),
                            **cls.__json_dump_args__)
  
      @classmethod
      def __to_json_value__(cls, value):
          return value
  
  
  class EnumMeta(BaseMeta):
      'meta class to set enumeration attributes, __contains__, __iter__...'
      def __init__(cls, name, bases, namespace):
          super(EnumMeta, cls).__init__(name, bases, namespace)
          if isinstance(cls.__choices__, str):
              cls.__choices__ = tuple(cls.__choices__.split())
          else:
              cls.__choices__ = tuple(cls.__choices__)
  
          for v in cls.__choices__:
              setattr(cls, v, v)
  
      def __contains__(cls, v):
          return v in cls.__choices__
  
      def __iter__(cls):
          return iter(cls.__choices__)
  
      def __len__(cls):
          return len(cls.__choices__)
  
      def __to_graphql__(cls, indent=0, indent_string='  '):
          s = [BaseMeta.__to_graphql__(cls, indent, indent_string)]
          prefix = indent_string * (indent + 1)
          for c in cls:
              s.append(prefix + str(c))
          s.append(indent_string * indent + '}')
          return '\n'.join(s)
  
      def __to_graphql_input__(cls, value, indent=0, indent_string='  '):
          return value
  
      def __to_json_value__(cls, value):
          return value
  
  
  class Enum(BaseType, metaclass=EnumMeta):
      '''This is an abstract class that enumerations should inherit
      and define ``__choices__`` class member with a list of strings
      matching the choices allowed by this enumeration. A single string
      may also be used, in such case it will be split using
      ``str.split()``.
  
      Note that ``__choices__`` is not set in the final class, the
      metaclass will use that to build members and provide the
      ``__iter__``, ``__contains__`` and ``__len__`` instead.
  
      The instance constructor will never return instance of
      :class:`Enum`, rather the string, if that matches.
  
      Examples:
  
      >>> class Colors(Enum):
      ...     __choices__ = ('RED', 'GREEN', 'BLUE')
      ...
      >>> Colors('RED')
      'RED'
      >>> Colors(None) # returns None
      >>> Colors('MAGENTA')
      Traceback (most recent call last):
        ...
      ValueError: Colors does not accept value MAGENTA
  
      Using a string will automatically split and convert to tuple:
  
      >>> class Fruits(Enum):
      ...     __choices__ = 'APPLE ORANGE BANANA'
      ...
      >>> Fruits.__choices__
      ('APPLE', 'ORANGE', 'BANANA')
      >>> len(Fruits)
      3
  
      Enumerations have a special syntax in GraphQL, no quotes:
  
      >>> print(Fruits.__to_graphql_input__(Fruits.APPLE))
      APPLE
  
      And for JSON it's a string as well (so JSON encoder adds quotes):
  
      >>> print(json.dumps(Fruits.__to_json_value__(Fruits.APPLE)))
      "APPLE"
  
      Variables are passed thru:
  
      >>> Fruits(Variable('var'))
      $var
      '''
      __kind__ = 'enum'
      __choices__ = ()
  
      def __new__(cls, json_data, selection_list=None):
          if json_data is None:
              return None
          if isinstance(json_data, Variable):
              return json_data
          if json_data not in cls:
              raise ValueError('%s does not accept value %s' % (cls, json_data))
          return json_data
  
  
  class UnionMeta(BaseMetaWithTypename):
      'meta class to set __types__ as :class:`BaseType` instances'
  
      def __init__(cls, name, bases, namespace):
          super(UnionMeta, cls).__init__(name, bases, namespace)
          if not cls.__types__ and BaseTypeWithTypename not in bases:
              raise ValueError(name + ': missing __types__')
  
          types = []
          for t in cls.__types__:
              if isinstance(t, str):
                  t = cls.__schema__[t]
              else:
                  t = BaseType.__ensure__(t)
              types.append(t)
  
          cls.__types__ = tuple(types)
          cls.__typename_to_type__ = {t.__name__: t for t in types}
  
      def __contains__(cls, name_or_type):
          if isinstance(name_or_type, str):
              name_or_type = cls.__schema__[name_or_type]
          else:
              name_or_type = BaseType.__ensure__(name_or_type)
          return name_or_type in cls.__types__
  
      def __iter__(cls):
          return iter(cls.__types__)
  
      def __len__(cls):
          return len(cls.__types__)
  
      def __to_graphql__(cls, indent=0, indent_string='  '):
          suffix = ' = ' + ' | '.join(str(c) for c in cls.__types__)
          return BaseMeta.__to_graphql__(cls, indent, indent_string) + suffix
  
      def __getitem__(cls, key):
          return cls.__meta_fields__[key]
  
  
  class Union(BaseTypeWithTypename, metaclass=UnionMeta):
      '''This is an abstract class that union of multiple types should
      inherit and define ``__types__``, a list of pre-defined
      :class:`Type`.
  
      >>> class IntOrFloatOrString(Union):
      ...     __types__ = (Int, float, 'String')
      ...
      >>> IntOrFloatOrString # or repr(), prints out GraphQL!
      union IntOrFloatOrString = Int | Float | String
      >>> Int in IntOrFloatOrString
      True
      >>> 'Int' in IntOrFloatOrString  # may use type names as well
      True
      >>> int in IntOrFloatOrString  # may use native Python types as well
      True
      >>> ID in IntOrFloatOrString
      False
      >>> len(IntOrFloatOrString)
      3
      >>> for t in IntOrFloatOrString:
      ...     print(repr(t))
      scalar Int
      scalar Float
      scalar String
  
      Failing to define types will raise exception:
  
      >>> class FailureUnion(Union):
      ...     pass
      Traceback (most recent call last):
        ...
      ValueError: FailureUnion: missing __types__
  
      Whenever instantiating the type, pass a JSON object with
      ``__typename`` (done automatically using fragments via ``__as__``):
  
      >>> class TypeA(Type):
      ...     i = int
      ...
      >>> class TypeB(Type):
      ...     s = str
      ...
      >>> class TypeU(Union):
      ...     __types__ = (TypeA, TypeB)
      ...
      >>> data = {'__typename': 'TypeA', 'i': 1}
      >>> TypeU(data)
      TypeA(i=1)
      >>> data = {'__typename': 'TypeB', 's': 'hi'}
      >>> TypeU(data)
      TypeB(s='hi')
  
      It nicely handles unknown types:
  
      >>> data = {'v': 123}
      >>> TypeU(data) # no __typename
      UnknownType()
      >>> data = {'__typename': 'TypeUnknown', 'v': 123}
      >>> TypeU(data) # auto-generates empty types
      TypeUnknown()
      >>> data = None
      >>> TypeU(data)
  
      Variables are passed thru:
  
      >>> TypeU(Variable('var'))
      $var
      '''
  
      __kind__ = 'union'
      __types__ = ()
  
      def __new__(cls, json_data, selection_list=None):
          if json_data is None:
              return
          if isinstance(json_data, Variable):
              return json_data
          type_name = json_data.get('__typename')
          if not type_name:
              t = UnknownType
          else:
              t = cls.__typename_to_type__.get(type_name)
              if t is None:
                  t = type(type_name, (UnknownType,), {})
                  cls.__typename_to_type__[type_name] = t
  
          return t(json_data, selection_list)
  
  
  class ContainerTypeMeta(BaseMetaWithTypename):
      '''Creates container types, ensures fields are instance of Field.
      '''
      def __init__(cls, name, bases, namespace):
          super(ContainerTypeMeta, cls).__init__(name, bases, namespace)
          cls.__fields = OrderedDict()
          cls.__interfaces__ = ()
  
          if not bases or BaseTypeWithTypename in bases or \
                  ContainerType in bases:
              return
  
          if cls.__kind__ == 'interface':
              cls.__fix_type_kind(bases)
              if cls.__kind__ == 'interface':
                  cls.__possible_types__ = {}
  
          cls.__populate_interfaces(bases)
          cls.__inherit_fields(bases)
          cls.__create_own_fields()
  
      def __fix_type_kind(cls, bases):
          for b in bases:
              if b.__kind__ == 'type':
                  cls.__kind__ = 'type'
                  break
  
      def __populate_interfaces(cls, bases):
          ifaces = []
          for b in bases:
              if b in (Type, Interface, Input):
                  continue
              if getattr(b, '__kind__', '') == 'interface':
                  ifaces.append(b)
              for i in getattr(b, '__interfaces__', []):
                  if i not in ifaces:
                      ifaces.append(i)
  
          cls.__interfaces__ = tuple(ifaces)
          for i in ifaces:
              i.__possible_types__[cls.__name__] = cls
  
      def __inherit_fields(cls, bases):
          for b in bases:
              cls.__fields.update(b.__fields)
  
      def __get_field_names(cls):
          try:
              return getattr(cls, "__field_names__")
          except AttributeError:
              all_fields = super(ContainerTypeMeta, cls).__dir__()
              return (
                  name
                  for name in all_fields
                  if not name.startswith("_")
              )
  
      def __create_own_fields(cls):
          # call the parent __dir__(), we don't want our overridden version
          # that reports fields we're just deleting
          for name in cls.__get_field_names():
              field = getattr(cls, name)
              if not isinstance(field, Field):
                  if not isinstance(field, Lazy):
                      try:
                          field = BaseType.__ensure__(field)
                      except TypeError:
                          continue
  
                  field = Field(field)
  
              field._set_container(cls.__schema__, cls, name)
              cls.__fields[name] = field
              try:
                  # let fallback to cls.__fields using getitem
                  delattr(cls, name)
              except AttributeError:  # pragma: no cover
                  pass  # if may be defined in a parent class and already deleted
  
      def __getitem__(cls, key):
          if key.startswith('_'):
              try:
                  return cls.__meta_fields__[key]
              except KeyError:
                  pass
  
          try:
              return cls.__fields[key]
          except KeyError as exc:
              raise KeyError('%s has no field %s' % (cls, key)) from exc
  
      def __getattr__(cls, key):
          try:
              return cls.__fields[key]
          except KeyError as exc:
              raise AttributeError('%s has no field %s' % (cls, key)) from exc
  
      def __dir__(cls):
          original_dir = super(ContainerTypeMeta, cls).__dir__()
          fields = list(cls.__fields.keys())
          return sorted(original_dir + fields)
  
      def __iter__(cls):
          return iter(cls.__fields.values())
  
      def __contains__(cls, field_name):
          return field_name in cls.__fields
  
      def __to_graphql__(cls, indent=0, indent_string='  '):
          d = BaseMeta.__to_graphql__(cls, indent, indent_string)
          if hasattr(cls, '__interfaces__') and cls.__interfaces__:
              d += ' implements ' + ', '.join(str(i) for i in cls.__interfaces__)
  
          s = [d + ' {']
          prefix = indent_string * (indent + 1)
          for f in cls:
              s.append(prefix + f.__to_graphql__(indent, indent_string))
          s.append(indent_string * indent + '}')
          return '\n'.join(s)
  
      def __to_json_value__(cls, value):
          if value is None:
              return None
          d = {}
          for name, f in cls.__fields.items():
              # elements may not exist since not queried and would
              # trigger exception for non-null fields
              if name in value:
                  d[f.graphql_name] = f.type.__to_json_value__(value[name])
          return d
  
  
  class ContainerType(BaseTypeWithTypename, metaclass=ContainerTypeMeta):
      '''Container of :class:`Field`.
  
      For ease of use, fields can be declared by sub classes in the
      following ways:
  
       - ``name = str`` to create a simple string field. Other basic
         types are allowed as well: ``int``, ``float``, ``str``,
         ``bool``, ``datetime.time``, ``datetime.date`` and
         ``datetime.datetime``. These are only used as identifiers to
         translate using ``map_python_to_graphql`` dict. Note that
         ``id``, although is not a type, maps to ``ID``.
  
       - ``name = TypeName`` for subclasses of ``BaseType``, such
         as pre-defined scalars (:class:`Int`, etc) or your own defined
         types, from :class:`Type`.
  
       - ``name = Field(TypeName, graphql_name='differentName',
         args={...})`` to explicitly define more field information,
         such as GraphQL JSON name, query parameters, etc.
  
      The metaclass :class:`ContainerTypeMeta` will normalize all of those
      members to be instances of :class:`Field`, as well as provide
      useful container protocol such as ``__contains__``,
      ``__getitem__``, ``__iter__`` and so on.
  
      Fields from all bases (interfaces, etc) are merged.
  
      Members started with underscore (``_``) are not processed.
      '''
  
      __json_dump_args__ = {
          # given to json.dumps() in __bytes__()
          'sort_keys': True,
          'separators': (',', ':'),
      }
  
      def __init__(self, json_data, selection_list=None):
          assert json_data is None or isinstance(json_data, dict), \
              '%r (%s) is not a JSON Object' % (
                  json_data, type(json_data).__name__)
          object.__setattr__(self, '__selection_list__', selection_list)
          self.__populate_fields(json_data)
  
      def __populate_fields(self, json_data):
          cache = OrderedDict()
          object.__setattr__(self, '__fields_cache__', cache)
          if json_data is None:
              # backing store, changed by setattr()
              object.__setattr__(self, '__json_data__', {})
              return
  
          if self.__selection_list__ is not None:
              self.__populate_fields_from_selection_list(
                  self.__selection_list__, json_data)
          else:
              for field in self.__class__:
                  self.__populate_field_data(field, field.type, None, json_data)
  
          # backing store, changed by setattr()
          object.__setattr__(self, '__json_data__', json_data)
  
      def __populate_field_data(self, field, ftype, sel, json_data):
          name = field.name
          graphql_name = field.graphql_name
          if graphql_name not in json_data:
              return
  
          value = None
          try:
              value = json_data[graphql_name]
              value = ftype(value, sel)
              setattr(self, name, value)
              self.__fields_cache__[name] = field
          except Exception as exc:
              raise ValueError('%s selection %r: %r (%s)' % (
                  self.__class__, name, value, exc)) from exc
  
      def __populate_fields_from_selection_list(self, sl, json_data):
          for sel in sl.__get_selections_or_auto_select__():
              field = sel.__field__
              ftype = self.__get_type_for_selection(sel, json_data)
              if sel.__alias__ is not None:
                  alias = sel.__alias__
                  field = Field(ftype, alias, field.args)
                  field._set_container(self.__schema__, self, alias)
              self.__populate_field_data(field, ftype, sel, json_data)
  
          casts = sl.__casts__
          if casts:
              tname = json_data.get('__typename')
              csl = casts.get(tname)
              if csl:
                  self.__populate_fields_from_selection_list(csl, json_data)
  
          fragments = sl.__fragments__
          if fragments:
              tname = json_data.get('__typename')
              fl = fragments.get(tname)
              if fl:
                  for f in fl:
                      self.__populate_fields_from_selection_list(f, json_data)
  
      @staticmethod
      def __get_type_for_selection(sel, json_data):
          field = sel.__field__
          ftype = field.type
          casts = sel.__casts__
          if not casts:
              return ftype
  
          graphql_name = field.graphql_name
          try:
              tname = json_data.get(graphql_name, {}).get('__typename')
          except AttributeError:
              # The selection returned something other than a dict, e.g. a list.
              # Nothing to worry about, it just means this object doesn't contain
              # a typename.
              tname = None
  
          if not tname:
              return ftype
  
          sl = casts.get(tname)
          if sl is None:
              return ftype
  
          return sl.__type__
  
      def __setattr__(self, name, value):
          '''Sets the attribute value, if a :class:`Field` updates backing store.
  
          Considering ``TypeUsingPython``, previously declared in the
          module documentation:
  
          >>> json_data = {'aInt': 1,  'aFloat': 2.1}
          >>> obj = global_schema.TypeUsingPython(json_data)
          >>> obj.a_int, obj.a_float
          (1, 2.1)
          >>> obj.a_int = 123
          >>> obj.a_int, obj.a_float
          (123, 2.1)
          >>> json_data['aInt']
          123
  
          However that's valid for known :class:`Field` for the given
          :class:`ContainerType` subclasses:
  
          >>> obj.new_attr = 'some value'  # no field, no backing store updates
          >>> obj.new_attr
          'some value'
          >>> json_data['new_attr']
          Traceback (most recent call last):
            ...
          KeyError: 'new_attr'
          >>> json_data['newAttr']
          Traceback (most recent call last):
            ...
          KeyError: 'newAttr'
  
          '''
          object.__setattr__(self, name, value)
          if not hasattr(self, '__json_data__'):  # still populating
              return
          # apply changes to json backing store, if name is known
          field = self.__fields_cache__.get(name)
          if field is None:
              field = getattr(self.__class__, name, None)
              if field is None:
                  return
              self.__fields_cache__[name] = field
  
          json_value = field.type.__to_json_value__(value)
          self.__json_data__[field.graphql_name] = json_value
  
      def __getitem__(self, name):
          '''Get the field given its name.
  
          Considering ``TypeUsingPython``, previously declared in the
          module documentation:
  
          >>> global_schema.TypeUsingPython['a_int']
          aInt: Int
  
          >>> global_schema.TypeUsingPython['unknown_field']
          Traceback (most recent call last):
            ...
          KeyError: 'TypeUsingPython has no field unknown_field'
          '''
          try:
              return getattr(self, name)
          except AttributeError as exc:
              raise KeyError('%s has no field %s' % (self, name)) from exc
  
      def __setitem__(self, name, value):
          '''Set the item, maps to ``setattr(self, name, value)``'''
          setattr(self, name, value)
  
      def __iter__(self):
          '''Iterate over known fields of the **instance**.
  
          Unlike ``iter(SubclassOfType)``, which iterates over all
          declared fields, this iterator matches only fields that exist
          in the object, based on the ``json_data`` used to create the
          object, and the one that provides the backing store:
  
          >>> json_data = { 'aInt': 1, 'aFloat': 2.1 }
          >>> obj = global_schema.TypeUsingPython(json_data)
          >>> for field_name in obj:
          ...    print(field_name, repr(obj[field_name]))
          a_int 1
          a_float 2.1
          >>> for field in obj.__class__:
          ...    print(repr(field))
          aInt: Int
          aFloat: Float
          aString: String
          aBoolean: Boolean
          aId: ID
  
          After it's set for the given instance, then it's included in
          the iterator:
  
          >>> obj.a_string = 'hello world'  # known field
          >>> for field_name in obj:
          ...    print(field_name, repr(obj[field_name]))
          a_int 1
          a_float 2.1
          a_string 'hello world'
  
          However that's valid for known :class:`Field` for the given
          :class:`ContainerType` subclasses:
  
          >>> obj.new_attr = 'some value'  # unknown field, not in 'iter'
          >>> for field_name in obj:
          ...    print(field_name, repr(obj[field_name]))
          a_int 1
          a_float 2.1
          a_string 'hello world'
  
          '''
          return iter(self.__fields_cache__.keys())
  
      def __contains__(self, name):
          '''Checks if for a known field name in the **instance**.
  
          Unlike ``name in SubclassOfType``, which checks amongst all
          declared fields, this matches only fields that exist
          in the object, based on the ``json_data`` used to create the
          object, and the one that provides the backing store:
  
          >>> json_data = { 'aInt': 1, 'aFloat': 2.1 }
          >>> obj = global_schema.TypeUsingPython(json_data)
          >>> 'a_int' in obj
          True
          >>> 'a_float' in obj
          True
          >>> 'a_string' in obj  # in class, but not instance
          False
          >>> 'a_string' in obj.__class__
          True
  
          After it's set for the given instance, then becomes true:
  
          >>> obj.a_string = 'hello world'  # known field
          >>> 'a_string' in obj  # now in instance
          True
          '''
          return hasattr(self, name)
  
      def __len__(self):
          '''Checks how many fields are set in the **instance**.
  
          >>> json_data = { 'aInt': 1, 'aFloat': 2.1 }
          >>> obj = global_schema.TypeUsingPython(json_data)
          >>> len(obj)
          2
  
          >>> obj.a_string = 'hello world'  # known field
          >>> len(obj)
          3
          '''
          i = 0
          for name in self:
              i += 1
          return i
  
      def __str__(self):
          r = []
          for k in self:
              r.append('%s=%s' % (k, self[k]))
          return '%s(%s)' % (self.__class__.__name__, ', '.join(r))
  
      def __repr__(self):
          r = []
          for k in self:
              r.append('%s=%r' % (k, self[k]))
          return '%s(%s)' % (self.__class__.__name__, ', '.join(r))
  
      def __to_json_value__(self):
          return ContainerTypeMeta.__to_json_value__(self.__class__, self)
  
      def __bytes__(self):
          return bytes(json.dumps(
              self.__to_json_value__(), **self.__json_dump_args__),
              'utf-8')
  
  
  class BaseItem:
      '''Base item for :class:`Arg` and :class:`Field`.
  
      Each parameter has a GraphQL type, such as a derived class from
      :class:`Scalar` or :class:`Type`, this is used for nesting,
      conversion to native Python types, generating queries, etc.
      '''
  
      __slots__ = (
          '_type', 'graphql_name', 'name', 'schema', 'container',
      )
  
      def __init__(self, typ, graphql_name=None):
          '''
          :param typ: the :class:`Scalar` or :class:`Type` derived
            class. If this would cause a cross reference and the other
            type is not declared yet, then use the string name to query
            in the schema.
          :type typ: :class:`Scalar`, :class:`Type` or str
  
          :param graphql_name: the name to use in JSON object, usually ``aName``.
            If ``None`` or empty, will be created from python, converting
            ``a_name`` to ``aName`` using
            ``Arg._to_graphql_name()``
          :type graphql_name: str
          '''
          if isinstance(typ, str):
              self._type = Lazy(typ, typ, lambda x: x)
          elif isinstance(typ, Lazy):
              self._type = typ
          else:
              self._type = BaseType.__ensure__(typ)
          self.graphql_name = graphql_name
          self.name = None
          self.schema = None
          self.container = None
  
      def _set_container(self, schema, container, name):
          self.schema = schema
          self.container = container
          self.name = name
          if not self.graphql_name:
              self.graphql_name = self._to_graphql_name(name)
  
      @property  # noqa: A003
      def type(self):  # noqa: A003
          if not isinstance(self._type, Lazy):
              return self._type
          self._type = self._type.resolve(self.schema)
          return self._type
  
      @staticmethod
      def _to_python_name(graphql_name):
          '''Converts a GraphQL name, ``aName`` to Python: ``a_name``.
  
          Note that an underscore is appended if the name is a Python keyword.
  
          >>> BaseItem._to_python_name('aName')
          'a_name'
          >>> BaseItem._to_python_name('for')
          'for_'
          '''
          s = []
          rgx = re.compile('([^A-Z]+|[A-Z]+[^A-Z]*)')
          for w in rgx.findall(graphql_name):
              s.append(w.lower())
          name = '_'.join(s)
          if keyword.iskeyword(name):
              return name + '_'
          return name
  
      @staticmethod
      def _to_graphql_name(name):
          '''Converts a Python name, ``a_name`` to GraphQL: ``aName``.
  
          Note that leading underscores (``_``) are preserved.
          '''
          prefix = ''
          while name.startswith('_'):
              prefix += '_'
              name = name[1:]
          parts = name.split('_')
          return prefix + ''.join(parts[:1] + [p.title() for p in parts[1:]])
  
      def __str__(self):
          return self.name
  
      def __to_graphql__(self, indent=0, indent_string='  '):
          return '%s: %s' % (self.graphql_name, self.type)
  
      def __repr__(self):
          return self.__to_graphql__()
  
      def __bytes__(self):
          return bytes(self.__to_graphql__(indent_string=''), 'utf-8')
  
  
  class Variable:
      '''GraphQL variable: ``$varName``
  
      Usually given as :class:`Arg` default value:
  
      >>> class MyTypeWithVariable(Type):
      ...     f = Field(str, args={'first': Arg(int, default=Variable('var'))})
      ...
      >>> MyTypeWithVariable
      type MyTypeWithVariable {
        f(first: Int = $var): String
      }
      >>> print(repr(MyTypeWithVariable.f.args['first'].default))
      $var
      >>> print(str(MyTypeWithVariable.f.args['first'].default))
      $var
      >>> print(bytes(MyTypeWithVariable.f.args['first'].default).decode('utf8'))
      $var
  
      '''
  
      __slots__ = ('name', 'graphql_name')
  
      def __init__(self, name, graphql_name=None):
          self.name = name
          self.graphql_name = graphql_name or self._to_graphql_name(name)
  
      def __str__(self):
          return self.__to_graphql__()
  
      def __repr__(self):
          return self.__to_graphql__()
  
      def __bytes__(self):
          return bytes(self.__to_graphql__(indent_string=''), 'utf-8')
  
      @staticmethod
      def _to_graphql_name(name):
          '''Converts a Python name, ``a_name`` to GraphQL: ``aName``.
          '''
          parts = name.split('_')
          return ''.join(parts[:1] + [p.title() for p in parts[1:]])
  
      def __to_graphql__(self, indent=0, indent_string='  '):
          return '$' + self.graphql_name
  
      @classmethod
      def __to_graphql_input__(cls, value, indent=0, indent_string='  '):
          return '$' + value.graphql_name
  
  
  class Arg(BaseItem):
      '''GraphQL :class:`Field` argument.
  
      >>> class MyTypeWithArgument(Type):
      ...     a = Field(str, args={'arg_name': int}) # implicit
      ...     b = Field(str, args={'arg': Arg(int)})  # explicit + Python
      ...     c = Field(str, args={'arg': Arg(Int)})  # explicit + sgqlc.types
      ...     d = Field(str, args={'arg': Arg(int, default=1)})
      ...
      >>> MyTypeWithArgument
      type MyTypeWithArgument {
        a(argName: Int): String
        b(arg: Int): String
        c(arg: Int): String
        d(arg: Int = 1): String
      }
  
      '''
      __slots__ = ('default',)
  
      def __init__(self, typ, graphql_name=None, default=None):
          '''
          :param typ: the :class:`Scalar` or :class:`Type` derived
            class. If this would cause a cross reference and the other
            type is not declared yet, then use the string name to query
            in the schema.
          :type typ: :class:`Scalar`, :class:`Type` or str
  
          :param graphql_name: the name to use in JSON object, usually ``aName``.
            If ``None`` or empty, will be created from python, converting
            ``a_name`` to ``aName`` using
            :func:`BaseItem._to_graphql_name()`
          :type graphql_name: str
  
          :param default: The default value for field. May be a value or
            :class:`Variable`.
          '''
          super(Arg, self).__init__(typ, graphql_name)
          self.default = default
          if default is not None and not isinstance(default, Variable):
              typ(default)
  
      def __to_graphql__(self, indent=0, indent_string='  '):
          default = ''
          if self.default is not None:
              if isinstance(self.default, Variable):
                  default = self.default.__to_graphql__(indent, indent_string)
              else:
                  default = self.type.__to_graphql_input__(
                      self.default, indent, indent_string)
              default = ' = ' + default
          return super(Arg, self).__to_graphql__(indent, indent_string) + default
  
      def __to_graphql_input__(self, value, indent=0, indent_string='  '):
          if not isinstance(value, (self.type, Variable)):
              value = self.type(value)
          v = self.type.__to_graphql_input__(value, indent, indent_string)
          return '%s: %s' % (self.graphql_name, v)
  
  
  class ArgDict(OrderedDict):
      '''The :class:`Field` Argument Dict.
  
      Common usage is inside :class:`Field`:
  
      >>> class MyType(Type):
      ...     a = Field(Int, args={'argument1': String})     # implicit
      ...     b = Field(Int, args=ArgDict(argument1=String)) # explicit
      ...
      >>> print(repr(MyType))
      type MyType {
        a(argument1: String): Int
        b(argument1: String): Int
      }
      >>> print(repr(MyType.a))
      a(argument1: String): Int
      >>> print(repr(MyType.a.args))
      (argument1: String)
      >>> print(repr(MyType.b))
      b(argument1: String): Int
      >>> print(repr(MyType.b.args))
      (argument1: String)
      >>> print(repr(MyType.b.args['argument1']))
      argument1: String
      >>> print(bytes(MyType.b.args['argument1']).decode('utf-8'))
      argument1: String
  
      This takes care to ensure values are :class:`Arg`. In the example
      above, we're not passing :class:`Arg`, rather just a type
      (:class:`String`) and it's working internally to create
      :class:`Arg`. For ease of use, can be created in various
      forms. Note they must be added to a container field to be useful,
      which would call :func:`ArgDict._set_container()` for you, here
      called manually for testing purposes:
  
      >>> ad = ArgDict(name=str)
      >>> ad._set_container(global_schema, None)  # done automatically by Field
      >>> print(ad)
      (name: String)
  
      >>> ad = ArgDict(name=String)
      >>> ad._set_container(global_schema, None)  # done automatically by Field
      >>> print(ad)
      (name: String)
  
      >>> ad = ArgDict({'name': str})
      >>> ad._set_container(global_schema, None)  # done automatically by Field
      >>> print(ad)
      (name: String)
  
      >>> ad = ArgDict(('name', str), ('other', int))
      >>> ad._set_container(global_schema, None)  # done automatically by Field
      >>> print(ad)
      (name: String, other: Int)
  
      >>> ad = ArgDict((('name', str), ('other', int)))
      >>> ad._set_container(global_schema, None)  # done automatically by Field
      >>> print(ad)
      (name: String, other: Int)
  
      Note that for better understanding, more than 3 arguments are
      printed in multiple lines:
  
      >>> ad = ArgDict(a=int, b=float, c=non_null(str), d=list_of(int))
      >>> ad._set_container(global_schema, None)  # done automatically by Field
      >>> print(ad)
      (
        a: Int
        b: Float
        c: String!
        d: [Int]
      )
      >>> print(bytes(ad).decode('utf-8'))
      (
      a: Int
      b: Float
      c: String!
      d: [Int]
      )
  
      This is also the case for input values:
  
      >>> print('fieldName' + ad.__to_graphql_input__({
      ...     'a': 1, 'b': 2.2, 'c': 'hi', 'd': [1, 2],
      ... }))
      fieldName(
          a: 1
          b: 2.2
          c: "hi"
          d: [1, 2]
        )
  
      Variables can be handled using :class:`Variable` instances:
  
      >>> print('fieldName' + ad.__to_graphql_input__({
      ...     'a': Variable('a'),
      ...     'b': Variable('b'),
      ...     'c': Variable('c'),
      ...     'd': Variable('d'),
      ... }))
      fieldName(
          a: $a
          b: $b
          c: $c
          d: $d
        )
  
      '''
  
      def __init__(self, *lst, **mapping):
          super(ArgDict, self).__init__()
  
          if not lst and not mapping:
              return
  
          if len(lst) == 1:
              if lst[0] is None:
                  lst = []
              elif isinstance(lst[0], (tuple, list)):
                  lst = lst[0]
              elif isinstance(lst[0], dict):
                  mapping.update(lst[0])
                  lst = []
  
          for k, v in lst:
              if not isinstance(v, Arg):
                  v = Arg(v)
              self[k] = v
  
          for k, v in mapping.items():
              if not isinstance(v, Arg):
                  v = Arg(v)
              self[k] = v
  
      def _set_container(self, schema, container):
          for k, v in self.items():
              v._set_container(schema, container, k)
  
      def __to_graphql__(self, indent=0, indent_string='  '):
          n = len(self)
          if n == 0:
              return ''
  
          s = ['(']
          if n <= 3:
              args = (p.__to_graphql__(indent, indent_string)
                      for p in self.values())
              s.extend((', '.join(args), ')'))
          else:
              s.append('\n')
              prefix = indent_string * (indent + 1)
              for p in self.values():
                  s.extend((prefix,
                            p.__to_graphql__(indent, indent_string),
                            '\n'))
  
              s.extend((indent_string * indent, ')'))
          return ''.join(s)
  
      def __to_graphql_input__(self, values, indent=0, indent_string='  '):
          n = len(values)
          if n == 0:
              return ''
  
          s = ['(']
          if n <= 3:
              args = []
              for k, v in values.items():
                  p = self[k]
                  args.append(p.__to_graphql_input__(v))
              s.extend((', '.join(args), ')'))
          else:
              s.append('\n')
              prefix = indent_string * (indent + 2)
              for k, v in values.items():
                  p = self[k]
                  s.extend((prefix,
                            p.__to_graphql_input__(v, indent, indent_string),
                            '\n'))
  
              s.extend((indent_string * (indent + 1), ')'))
          return ''.join(s)
  
      def __str__(self):
          return self.__to_graphql__()
  
      def __repr__(self):
          return self.__to_graphql__()
  
      def __bytes__(self):
          return bytes(self.__to_graphql__(indent_string=''), 'utf-8')
  
  
  class Field(BaseItem):
      '''Field in a :class:`Type` container.
  
      Each field has a GraphQL type, such as a derived class from
      :class:`Scalar` or :class:`Type`, this is used for nesting,
      conversion to native Python types, generating queries, etc.
      '''
  
      __slots__ = ('args',)
  
      def __init__(self, typ, graphql_name=None, args=None):
          '''
          :param typ: the :class:`Scalar` or :class:`Type` derived
            class. If this would cause a cross reference and the other
            type is not declared yet, then use the string name to query
            in the schema.
          :type typ: :class:`Scalar`, :class:`Type` or str
  
          :param graphql_name: the name to use in JSON object, usually ``aName``.
            If ``None`` or empty, will be created from python, converting
            ``a_name`` to ``aName`` using
            :func:`BaseItem._to_graphql_name()`
          :type graphql_name: str
  
          :param args: The field parameters as a :class:`ArgDict` or
            compatible type (dict, or iterable of key-value pairs). The
            value may be a mapped Python type (ie: ``str``), explicit
            type (ie: ``String``), type name (ie: ``"String"``, to allow
            cross references) or :class:`Arg` instances.
          :type args: :class:`ArgDict`
          '''
          super(Field, self).__init__(typ, graphql_name)
          self.args = ArgDict(args)
  
      def _set_container(self, schema, container, name):
          super(Field, self)._set_container(schema, container, name)
          for k, v in self.args.items():
              v._set_container(schema, container, k)
  
      def __to_graphql__(self, indent=0, indent_string='  '):
          args = self.args.__to_graphql__(indent + 1, indent_string)
          return '%s%s: %s' % (self.graphql_name, args, self.type)
  
      def __bytes__(self):
          '''Prints GraphQL without indentation.
  
          >>> print(repr(global_schema.TypeUsingFields.many))
          many(
              a: Int
              b: Int
              c: Int
              d: Int
            ): Int
          >>> print(bytes(global_schema.TypeUsingFields.many).decode('utf-8'))
          many(
          a: Int
          b: Int
          c: Int
          d: Int
          ): Int
          '''
          return bytes(self.__to_graphql__(indent_string=''), 'utf-8')
  
  
  class Type(ContainerType):
      '''GraphQL ``type Name``.
  
      If the subclass also adds :class:`Interface` to the class
      declarations, then it will emit ``type Name implements Iface1, Iface2``,
      also making their fields automatically available in the final
      class.
      '''
      __kind__ = 'type'
  
  
  class Interface(ContainerType):
      '''GraphQL ``interface Name``.
  
      If the subclass also adds :class:`Interface` to the class
      declarations, then it will emit
      ``interface Name implements Iface1, Iface2``,
      also making their fields automatically available in the final
      class.
  
      Whenever interfaces are instantiated, if there is a ``__typename``
      in ``json_data`` and the type is known, it will automatically
      create the more specific type. Otherwise it instantiates the interface
      itself:
  
      >>> class SomeIface(Interface):
      ...     i = int
      ...
      >>> class TypeWithIface(Type, SomeIface):
      ...     pass
      ...
      >>> data = {'__typename': 'TypeWithIface', 'i': 123}
      >>> SomeIface(data)
      TypeWithIface(i=123)
      >>> data = {'__typename': 'UnknownType', 'i': 123}
      >>> SomeIface(data)
      SomeIface(i=123)
      '''
      __kind__ = 'interface'
  
      def __new__(cls, *args, **kwargs):
          if len(args) > 0 and isinstance(args[0], dict):
              type_name = args[0].get('__typename')
              if type_name:
                  t = cls.__possible_types__.get(type_name)
                  if t is not None and t is not cls:
                      return t(*args, **kwargs)
  
          return ContainerType.__new__(cls)
  
  
  class Input(ContainerType):
      '''GraphQL ``input Name``.
  
      Input types are similar to :class:`Type`, but they are used as
      argument values. They have more restrictions, such as no
      :class:`Interface`, :class:`Union` or :class:`Type` are allowed as
      field types. Only scalars or :class:`Input`.
  
      .. note::
  
         SGQLC currently doesn't enforce the field type restrictions
         imposed by the server.
  
      >>> class MyInput(Input):
      ...     a_int = int
      ...     a_float = float
      ...
      >>> MyInput
      input MyInput {
        aInt: Int
        aFloat: Float
      }
      >>> print(MyInput.__to_graphql_input__({'a_int': 1, 'a_float': 2.2}))
      {aInt: 1, aFloat: 2.2}
  
      >>> a_var = Variable('input')
      >>> print(MyInput.__to_graphql_input__(a_var))
      $input
  
      '''
      __kind__ = 'input'
  
      def __init__(self, _json_obj=None, _selection_list=None, **kwargs):
          '''Create the type given a json object or keyword arguments.
  
          >>> class AnotherInput(Input):
          ...     a_str = str
          ...
          >>> class TheInput(Input):
          ...     a_int = int
          ...     a_float = float
          ...     a_nested = AnotherInput
          ...     a_nested_list = list_of(AnotherInput)
          ...
  
          >>> TheInput(a_int=1, a_float=1.2, a_nested=AnotherInput(a_str='hi'))
          TheInput(a_int=1, a_float=1.2, a_nested=AnotherInput(a_str='hi'))
  
          >>> TheInput({'aInt': 1, 'aFloat': 1.2, 'aNested': {'aStr': 'hi'}})
          TheInput(a_int=1, a_float=1.2, a_nested=AnotherInput(a_str='hi'))
  
          >>> value = TheInput(a_int=1, a_float=1.2,
          ...                  a_nested=AnotherInput(a_str='hi'),
          ...                  a_nested_list=[AnotherInput(a_str='there')])
          >>> print(TheInput.__to_graphql_input__(value))
          {aInt: 1, aFloat: 1.2, aNested: {aStr: "hi"}, aNestedList: [{aStr: "there"}]}
          >>> value = TheInput({'aInt': 1, 'aFloat': 1.2, 'aNested': {'aStr': 'hi'},
          ...           'aNestedList': [{'aStr': 'there'}]})
          >>> print(TheInput.__to_graphql_input__(value))
          {aInt: 1, aFloat: 1.2, aNested: {aStr: "hi"}, aNestedList: [{aStr: "there"}]}
  
          .. note::
  
              ``selection_list`` parameter makes no sense and is ignored,
              it's only provided to cope with the ``ContainerType`` interface.
  
          '''  # noqa: E501
          if _json_obj is None:
              _json_obj = {}
          super().__init__(_json_obj, _selection_list)
          cls = type(self)
          for k, v in kwargs.items():
              f = cls[k]
              if not isinstance(v, (f.type, Variable, list)):
                  v = f.type(v)
              setattr(self, k, v)
  
      @classmethod
      def __to_graphql_input__(cls, value, indent=0, indent_string='  '):
          args = []
          if isinstance(value, Variable):
              return Variable.__to_graphql_input__(value, indent, indent_string)
          elif isinstance(value, Input):
              value = value.__json_data__
  
          for f in cls:
              try:
                  v = value[f.graphql_name]
              except KeyError:
                  try:
                      # previous versions allowed Python name as dict keys
                      v = value[f.name]
                  except KeyError:  # pragma: no cover
                      continue
  
              vs = f.type.__to_graphql_input__(v, indent, indent_string)
              args.append('%s: %s' % (f.graphql_name, vs))
  
          return '{' + ', '.join(args) + '}'
  
  
  ########################################################################
  # Built-in types
  ########################################################################
  
  class Int(Scalar):
      '''Maps GraphQL ``Int`` to Python ``int``.
  
      >>> Int # or repr()
      scalar Int
      >>> str(Int)
      'Int'
      >>> bytes(Int)
      b'scalar Int'
  
      '''
      converter = int
  
  
  class Float(Scalar):
      'Maps GraphQL ``Float`` to Python ``float``.'
      converter = float
  
  
  class String(Scalar):
      'Maps GraphQL ``String`` to Python ``str``.'
      converter = str
  
  
  class Boolean(Scalar):
      'Maps GraphQL ``Boolean`` to Python ``bool``.'
      converter = bool
  
  
  class ID(Scalar):
      'Maps GraphQL ``ID`` to Python ``str``.'
      converter = str
  
  
  class UnknownType(Type):
      'Type found in the response that was not present in schema'
      __auto_register = False  # do not expose this in Schema, just subclasses
  
  
  map_python_to_graphql = {
      int: Int,
      float: Float,
      str: String,
      bool: Boolean,
      id: ID,
  }
  