text: import datetime
  from collections import OrderedDict
  
  import pandas as pd
  import pytest
  import pytz
  from multipledispatch.conflict import ambiguities
  from pytest import param
  
  import ibis
  import ibis.expr.datatypes as dt
  from ibis.common.exceptions import IbisTypeError
  
  
  def test_validate_type():
      assert dt.validate_type is dt.dtype
  
  
  @pytest.mark.parametrize(
      ('spec', 'expected'),
      [
          ('ARRAY<DOUBLE>', dt.Array(dt.double)),
          ('array<array<string>>', dt.Array(dt.Array(dt.string))),
          ('map<string, double>', dt.Map(dt.string, dt.double)),
          (
              'map<int64, array<map<string, int8>>>',
              dt.Map(dt.int64, dt.Array(dt.Map(dt.string, dt.int8))),
          ),
          ('set<uint8>', dt.Set(dt.uint8)),
          ([dt.uint8], dt.Array(dt.uint8)),
          ([dt.float32, dt.float64], dt.Array(dt.float64)),
          ({dt.string}, dt.Set(dt.string)),
          ('point', dt.point),
          ('point;4326', dt.point),
          ('point;4326:geometry', dt.point),
          ('point;4326:geography', dt.point),
          ('linestring', dt.linestring),
          ('linestring;4326', dt.linestring),
          ('linestring;4326:geometry', dt.linestring),
          ('linestring;4326:geography', dt.linestring),
          ('polygon', dt.polygon),
          ('polygon;4326', dt.polygon),
          ('polygon;4326:geometry', dt.polygon),
          ('polygon;4326:geography', dt.polygon),
          ('multilinestring', dt.multilinestring),
          ('multilinestring;4326', dt.multilinestring),
          ('multilinestring;4326:geometry', dt.multilinestring),
          ('multilinestring;4326:geography', dt.multilinestring),
          ('multipoint', dt.multipoint),
          ('multipoint;4326', dt.multipoint),
          ('multipoint;4326:geometry', dt.multipoint),
          ('multipoint;4326:geography', dt.multipoint),
          ('multipolygon', dt.multipolygon),
          ('multipolygon;4326', dt.multipolygon),
          ('multipolygon;4326:geometry', dt.multipolygon),
          ('multipolygon;4326:geography', dt.multipolygon),
      ],
  )
  def test_dtype(spec, expected):
      assert dt.dtype(spec) == expected
  
  
  def test_array_with_string_value_type():
      assert dt.Array('int32') == dt.Array(dt.int32)
      assert dt.Array(dt.Array('array<map<string, double>>')) == (
          dt.Array(dt.Array(dt.Array(dt.Map(dt.string, dt.double))))
      )
  
  
  def test_map_with_string_value_type():
      assert dt.Map('int32', 'double') == dt.Map(dt.int32, dt.double)
      assert dt.Map('int32', 'array<double>') == dt.Map(
          dt.int32, dt.Array(dt.double)
      )
  
  
  def test_map_does_not_allow_non_primitive_keys():
      with pytest.raises(IbisTypeError):
          dt.dtype('map<array<string>, double>')
  
  
  def test_token_error():
      with pytest.raises(IbisTypeError):
          dt.dtype('array<string>>')
  
  
  def test_empty_complex_type():
      with pytest.raises(IbisTypeError):
          dt.dtype('map<>')
  
  
  def test_struct():
      orders = """array<struct<
                      oid: int64,
                      status: string,
                      totalprice: decimal(12, 2),
                      order_date: string,
                      items: array<struct<
                          iid: int64,
                          name: string,
                          price: decimal(12, 2),
                          discount_perc: decimal(12, 2),
                          shipdate: string
                      >>
                  >>"""
      expected = dt.Array(
          dt.Struct.from_tuples(
              [
                  ('oid', dt.int64),
                  ('status', dt.string),
                  ('totalprice', dt.Decimal(12, 2)),
                  ('order_date', dt.string),
                  (
                      'items',
                      dt.Array(
                          dt.Struct.from_tuples(
                              [
                                  ('iid', dt.int64),
                                  ('name', dt.string),
                                  ('price', dt.Decimal(12, 2)),
                                  ('discount_perc', dt.Decimal(12, 2)),
                                  ('shipdate', dt.string),
                              ]
                          )
                      ),
                  ),
              ]
          )
      )
  
      assert dt.dtype(orders) == expected
  
  
  def test_struct_with_string_types():
      result = dt.Struct.from_tuples(
          [
              ('a', 'map<double, string>'),
              ('b', 'array<map<string, array<int32>>>'),
              ('c', 'array<string>'),
              ('d', 'int8'),
          ]
      )
  
      assert result == dt.Struct.from_tuples(
          [
              ('a', dt.Map(dt.double, dt.string)),
              ('b', dt.Array(dt.Map(dt.string, dt.Array(dt.int32)))),
              ('c', dt.Array(dt.string)),
              ('d', dt.int8),
          ]
      )
  
  
  @pytest.mark.parametrize(
      'case',
      [
          'decimal(',
          'decimal()',
          'decimal(3)',
          'decimal(,)',
          'decimal(3,)',
          'decimal(3,',
      ],
  )
  def test_decimal_failure(case):
      with pytest.raises(IbisTypeError):
          dt.dtype(case)
  
  
  @pytest.mark.parametrize(
      'spec', ['varchar', 'varchar(10)', 'char', 'char(10)']
  )
  def test_char_varchar(spec):
      assert dt.dtype(spec) == dt.string
  
  
  @pytest.mark.parametrize(
      'spec', ['varchar(', 'varchar)', 'varchar()', 'char(', 'char)', 'char()']
  )
  def test_char_varchar_invalid(spec):
      with pytest.raises(IbisTypeError):
          dt.dtype(spec)
  
  
  @pytest.mark.parametrize(
      ('spec', 'expected'),
      [
          ('any', dt.any),
          ('null', dt.null),
          ('boolean', dt.boolean),
          ('int8', dt.int8),
          ('int16', dt.int16),
          ('int32', dt.int32),
          ('int64', dt.int64),
          ('uint8', dt.uint8),
          ('uint16', dt.uint16),
          ('uint32', dt.uint32),
          ('uint64', dt.uint64),
          ('float16', dt.float16),
          ('float32', dt.float32),
          ('float64', dt.float64),
          ('float', dt.float),
          ('halffloat', dt.float16),
          ('double', dt.double),
          ('string', dt.string),
          ('binary', dt.binary),
          ('date', dt.date),
          ('time', dt.time),
          ('timestamp', dt.timestamp),
          ('interval', dt.interval),
          ('point', dt.point),
          ('linestring', dt.linestring),
          ('polygon', dt.polygon),
          ('multilinestring', dt.multilinestring),
          ('multipoint', dt.multipoint),
          ('multipolygon', dt.multipolygon),
      ],
  )
  def test_primitive_from_string(spec, expected):
      assert dt.dtype(spec) == expected
  
  
  def test_literal_mixed_type_fails():
      data = [1, 'a']
      with pytest.raises(TypeError):
          ibis.literal(data)
  
  
  def test_timestamp_literal_without_tz():
      now_raw = datetime.datetime.utcnow()
      assert now_raw.tzinfo is None
      assert ibis.literal(now_raw).type().timezone is None
  
  
  def test_timestamp_literal_with_tz():
      now_raw = datetime.datetime.utcnow()
      now_utc = pytz.utc.localize(now_raw)
      assert now_utc.tzinfo == pytz.UTC
      assert ibis.literal(now_utc).type().timezone == str(pytz.UTC)
  
  
  def test_array_type_not_equals():
      left = dt.Array(dt.string)
      right = dt.Array(dt.int32)
  
      assert not left.equals(right)
      assert left != right
      assert not (left == right)
  
  
  def test_array_type_equals():
      left = dt.Array(dt.string)
      right = dt.Array(dt.string)
  
      assert left.equals(right)
      assert left == right
      assert not (left != right)
  
  
  def test_timestamp_with_timezone_parser_single_quote():
      t = dt.dtype("timestamp('US/Eastern')")
      assert isinstance(t, dt.Timestamp)
      assert t.timezone == 'US/Eastern'
  
  
  def test_timestamp_with_timezone_parser_double_quote():
      t = dt.dtype("timestamp('US/Eastern')")
      assert isinstance(t, dt.Timestamp)
      assert t.timezone == 'US/Eastern'
  
  
  def test_timestamp_with_timezone_parser_invalid_timezone():
      ts = dt.dtype("timestamp('US/Ea')")
      assert str(ts) == "timestamp('US/Ea')"
  
  
  @pytest.mark.parametrize(
      'unit',
      [
          'Y',
          'Q',
          'M',
          'W',
          'D',  # date units
          'h',
          'm',
          's',
          'ms',
          'us',
          'ns',  # time units
      ],
  )
  def test_interval(unit):
      definition = "interval('{}')".format(unit)
      dt.Interval(unit, dt.int32) == dt.dtype(definition)
  
      definition = "interval<uint16>('{}')".format(unit)
      dt.Interval(unit, dt.uint16) == dt.dtype(definition)
  
      definition = "interval<int64>('{}')".format(unit)
      dt.Interval(unit, dt.int64) == dt.dtype(definition)
  
  
  def test_interval_invalid_type():
      with pytest.raises(TypeError):
          dt.Interval('m', dt.float32)
  
      with pytest.raises(TypeError):
          dt.dtype("interval<float>('s')")
  
  
  @pytest.mark.parametrize('unit', ['H', 'unsupported'])
  def test_interval_unvalid_unit(unit):
      definition = "interval('{}')".format(unit)
  
      with pytest.raises(ValueError):
          dt.dtype(definition)
  
      with pytest.raises(ValueError):
          dt.Interval(dt.int32, unit)
  
  
  @pytest.mark.parametrize(
      'case',
      [
          "timestamp(US/Ea)",
          "timestamp('US/Eastern\")",
          'timestamp("US/Eastern\')',
          "interval(Y)",
          "interval('Y\")",
          'interval("Y\')',
      ],
  )
  def test_string_argument_parsing_failure_mode(case):
      with pytest.raises(IbisTypeError):
          dt.dtype(case)
  
  
  def test_timestamp_with_invalid_timezone():
      ts = dt.Timestamp('Foo/Bar&234')
      assert str(ts) == "timestamp('Foo/Bar&234')"
  
  
  def test_timestamp_with_timezone_repr():
      ts = dt.Timestamp('UTC')
      assert repr(ts) == "Timestamp(timezone='UTC', nullable=True)"
  
  
  def test_timestamp_with_timezone_str():
      ts = dt.Timestamp('UTC')
      assert str(ts) == "timestamp('UTC')"
  
  
  def test_time():
      ts = dt.time
      assert str(ts) == "time"
  
  
  def test_time_valid():
      assert dt.dtype('time').equals(dt.time)
  
  
  @pytest.mark.parametrize(
      ('value', 'expected_dtype'),
      [
          (None, dt.null),
          (False, dt.boolean),
          (True, dt.boolean),
          ('foo', dt.string),
          (datetime.date.today(), dt.date),
          (datetime.datetime.now(), dt.timestamp),
          (datetime.timedelta(days=3), dt.Interval(unit='D')),
          (pd.Timedelta('5 hours'), dt.Interval(unit='h')),
          (pd.Timedelta('7 minutes'), dt.Interval(unit='m')),
          (datetime.timedelta(seconds=9), dt.Interval(unit='s')),
          (pd.Timedelta('11 milliseconds'), dt.Interval(unit='ms')),
          (datetime.timedelta(microseconds=15), dt.Interval(unit='us')),
          (pd.Timedelta('17 nanoseconds'), dt.Interval(unit='ns')),
          # numeric types
          (5, dt.int8),
          (5, dt.int8),
          (127, dt.int8),
          (128, dt.int16),
          (32767, dt.int16),
          (32768, dt.int32),
          (2147483647, dt.int32),
          (2147483648, dt.int64),
          (-5, dt.int8),
          (-128, dt.int8),
          (-129, dt.int16),
          (-32769, dt.int32),
          (-2147483649, dt.int64),
          (1.5, dt.double),
          # parametric types
          (list('abc'), dt.Array(dt.string)),
          (set('abc'), dt.Set(dt.string)),
          ({1, 5, 5, 6}, dt.Set(dt.int8)),
          (frozenset(list('abc')), dt.Set(dt.string)),
          ([1, 2, 3], dt.Array(dt.int8)),
          ([1, 128], dt.Array(dt.int16)),
          ([1, 128, 32768], dt.Array(dt.int32)),
          ([1, 128, 32768, 2147483648], dt.Array(dt.int64)),
          ({'a': 1, 'b': 2, 'c': 3}, dt.Map(dt.string, dt.int8)),
          ({1: 2, 3: 4, 5: 6}, dt.Map(dt.int8, dt.int8)),
          (
              {'a': [1.0, 2.0], 'b': [], 'c': [3.0]},
              dt.Map(dt.string, dt.Array(dt.double)),
          ),
          (
              OrderedDict(
                  [
                      ('a', 1),
                      ('b', list('abc')),
                      ('c', OrderedDict([('foo', [1.0, 2.0])])),
                  ]
              ),
              dt.Struct.from_tuples(
                  [
                      ('a', dt.int8),
                      ('b', dt.Array(dt.string)),
                      (
                          'c',
                          dt.Struct.from_tuples([('foo', dt.Array(dt.double))]),
                      ),
                  ]
              ),
          ),
          param(
              datetime.timedelta(hours=5),
              dt.Interval(unit='h'),
              id='dateime hours',
              marks=pytest.mark.xfail(
                  reason='Hour conversion from datetime.timedelta to ibis '
                  'interval not supported'
              ),
          ),
          param(
              datetime.timedelta(minutes=7),
              dt.Interval(unit='m'),
              id='dateime minutes',
              marks=pytest.mark.xfail(
                  reason='Minute conversion from datetime.timedelta to ibis '
                  'interval not supported'
              ),
          ),
          param(
              datetime.timedelta(milliseconds=11),
              dt.Interval(unit='ms'),
              id='dateime milliseconds',
              marks=pytest.mark.xfail(
                  reason='Millisecond conversion from datetime.timedelta to '
                  'ibis interval not supported'
              ),
          ),
          param(
              pd.Timedelta('3W'),
              dt.Interval(unit='W'),
              id='weeks',
              marks=pytest.mark.xfail(
                  reason='Week conversion from Timedelta to ibis interval '
                  'not supported'
              ),
          ),
          param(
              None,
              dt.Interval(unit='Y'),
              id='years',
              marks=pytest.mark.xfail(
                  reason='Year conversion from Timedelta to ibis interval '
                  'not supported'
              ),
          ),
      ],
  )
  def test_infer_dtype(value, expected_dtype):
      assert dt.infer(value) == expected_dtype
      # test literal creation
      value = ibis.literal(value, type=expected_dtype)
      assert value.type() == expected_dtype
  
  
  @pytest.mark.parametrize(
      ('source', 'target'),
      [
          (dt.any, dt.string),
          (dt.null, dt.date),
          (dt.null, dt.any),
          (dt.int8, dt.int64),
          (dt.int8, dt.Decimal(12, 2)),
          (dt.int32, dt.int32),
          (dt.int32, dt.int64),
          (dt.uint32, dt.uint64),
          (dt.uint32, dt.Decimal(12, 2)),
          (dt.uint32, dt.float32),
          (dt.uint32, dt.float64),
          (dt.Interval('s', dt.int16), dt.Interval('s', dt.int32)),
      ],
  )
  def test_implicit_castable(source, target):
      assert dt.castable(source, target)
  
  
  @pytest.mark.parametrize(
      ('source', 'target'),
      [
          (dt.string, dt.null),
          (dt.int32, dt.int16),
          (dt.int16, dt.uint64),
          (dt.Decimal(12, 2), dt.int32),
          (dt.timestamp, dt.boolean),
          (dt.boolean, dt.interval),
          (dt.Interval('s', dt.int64), dt.Interval('s', dt.int16)),
      ],
  )
  def test_implicitly_uncastable(source, target):
      assert not dt.castable(source, target)
  
  
  @pytest.mark.parametrize(
      ('source', 'target', 'value'),
      [(dt.int8, dt.boolean, 0), (dt.int8, dt.boolean, 1)],
  )
  def test_implicit_castable_values(source, target, value):
      assert dt.castable(source, target, value=value)
  
  
  @pytest.mark.parametrize(
      ('source', 'target', 'value'),
      [(dt.int8, dt.boolean, 3), (dt.int8, dt.boolean, -1)],
  )
  def test_implicitly_uncastable_values(source, target, value):
      assert not dt.castable(source, target, value=value)
  
  
  def test_no_infer_ambiguities():
      assert not ambiguities(dt.infer.funcs)
  
  
  def test_struct_datatype_subclass_from_tuples():
      class MyStruct(dt.Struct):
          pass
  
      dtype = MyStruct.from_tuples([('a', 'int64')])
      assert isinstance(dtype, MyStruct)
  