Example #1
0
    def test_unknown_impossible(self):
        class DoesntWork:
            pass

        some_value = 'something'
        with self.assertRaises(ValueError):
            readstr(some_value, DoesntWork)
Example #2
0
    def test_unknown(self):
        @dataclass(frozen=True)
        class Something:
            value: str

        some_value = 'something'
        self.assertEqual(Something(some_value), readstr(some_value, Something))
Example #3
0
def read_value(value, type):
    if value is None:
        return None
    try:
        return readstr(value, type)
    except ValueError:
        return None
Example #4
0
    def test_extensibility(self):
        @reads
        def read_specialized_dict(str_value: str) -> typing.Dict[str, int]:
            import json
            return json.loads(str_value)

        self.assertEqual({
            'foo': 100,
            'bar': 200,
        }, readstr('{"foo": 100, "bar": 200}', typing.Dict[str, int]))

        @dataclass
        @reads
        class Foo:
            bar: str

        self.assertEqual(Foo('meow'), readstr('meow', Foo))
Example #5
0
 def test_read_dict(self):
     expected = {
         'a': uuid.UUID('BD47C33A-E430-4556-AF9F-7FD08CCC69ED'),
         'b': uuid.UUID('BD47C33A-E430-4556-AF9F-7FD08CCC69ED'),
     }
     self.assertEqual(
         expected,
         readstr('a=vUfDOuQwRVavn3_QjMxp7Q,b=vUfDOuQwRVavn3_QjMxp7Q==', typing.Dict[str, uuid.UUID]),
     )
Example #6
0
def from_env(
        t: ty.Type[T],
        *,
        getenv: ty.Callable[[str], ty.Optional[str]] = os.environ.get) -> T:
    """
    Read a dataclass from environment variables.

    :param t: A dataclass type
    :param getenv: A function that allows retrieving environment variables by name and defaults to None
    :return: An instance of `t`
    """
    values = dict()
    missing = dict()
    errors = dict()
    for field in dataclasses.fields(t):
        env_name = field.name.upper()
        value = getenv(env_name)
        if value is None:
            if field.default_factory is dataclasses.MISSING:
                if field.default is dataclasses.MISSING:
                    missing[env_name] = field
                    continue
                else:
                    value = field.default
            else:
                value = field.default_factory()
        else:
            try:
                value = readstr(value, field.type)
            except ValueError as exc:
                errors[env_name] = exc
                continue
        values[field.name] = value

    messages = []
    if missing:
        messages.append(
            f"missing required config for {', '.join(sorted(missing.keys()))}")
    for env_name, exc in errors.items():
        messages.append(f"invalid value for {env_name}: {exc}")
    if messages:
        raise ReadException('; '.join(messages))

    return t(**values)
Example #7
0
 def test_sequence(self):
     value = '1,2,3'
     expected = [1, 2, 3]
     self.assertEqual(expected, readstr(value, typing.Sequence[int]))
Example #8
0
 def test_read_date(self):
     self.assertEqual(datetime.date(2020, 1, 18), readstr('2020-01-18', datetime.date))
Example #9
0
 def test_mapping_abc(self):
     value = 'foo=3'
     expected = {'foo': 3}
     # noinspection PyUnresolvedReferences
     self.assertEqual(expected, readstr(value, collections.abc.Mapping[str, int]))
Example #10
0
 def test_read_union(self):
     self.assertEqual(7, readstr('7', typing.Union[int, float]))
     self.assertEqual(7.0, readstr('7.0', typing.Union[int, float]))
     with self.assertRaises(ValueError):
         readstr('fail', typing.Union[int, float])
Example #11
0
 def test_read_tuple(self):
     self.assertEqual((1, 'qux'), readstr('1,qux', typing.Tuple[int, str]))
     self.assertEqual((1, 'foo'), readstr('1,foo', typing.Tuple[int, typing.Literal['foo', 'bar']]))
     with self.assertRaises(ValueError):
         self.assertEqual((1, 'qux'), readstr('1,qux', typing.Tuple[int, typing.Literal['foo', 'bar']]))
Example #12
0
 def test_read_literal_set(self):
     self.assertEqual({'foo', 'bar'}, readstr('foo,bar', typing.Set[typing.Literal['foo', 'bar', 'qux']]))
     with self.assertRaises(ValueError):
         readstr('foo,bar,quuz', typing.Set[typing.Literal['foo', 'bar', 'qux']])
Example #13
0
 def test_read_optional(self):
     self.assertEqual(7, readstr('7', typing.Optional[int]))
     self.assertEqual(None, readstr('fail', typing.Optional[int]))
Example #14
0
 def test_mapping(self):
     value = 'foo=3'
     expected = {'foo': 3}
     self.assertEqual(expected, readstr(value, typing.Mapping[str, int]))
Example #15
0
 def test_read_uuid(self):
     expected = uuid.UUID('BD47C33A-E430-4556-AF9F-7FD08CCC69ED')
     self.assertEqual(expected, readstr('BD47C33A-E430-4556-AF9F-7FD08CCC69ED', uuid.UUID))
     self.assertEqual(expected, readstr('vUfDOuQwRVavn3_QjMxp7Q', uuid.UUID))
     self.assertEqual(expected, readstr('vUfDOuQwRVavn3_QjMxp7Q==', uuid.UUID))
Example #16
0
 def test_read_enum(self):
     self.assertEqual(Shape.DIAMOND, readstr('rhomb', Shape))
     with self.assertRaises(ValueError):
         readstr('rectangle', Shape)
Example #17
0
 def test_read_set(self):
     self.assertEqual({1, 3, 5}, readstr("1,3,3,5", typing.Set[int]))
Example #18
0
 def test_read_list(self):
     self.assertEqual([1, 3, 5], readstr("1,3,5", typing.List[int]))
Example #19
0
 def test_read_uuid_from_empty_string(self):
     value = readstr('', uuid.UUID)
     self.assertEqual(uuid.UUID('00000000-0000-0000-0000-000000000000'),
                      value)
Example #20
0
 def test_read_literal(self):
     self.assertEqual('foo', readstr('foo', typing.Literal['foo', 'bar']))
     with self.assertRaises(ValueError):
         readstr('qux', typing.Literal['foo', 'bar'])