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

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