From 304e85516e336dfe12b6835e601fec5536e40a1c Mon Sep 17 00:00:00 2001 From: Teo Stocco Date: Wed, 1 Jun 2022 23:21:18 +0200 Subject: [PATCH 1/3] fix: add PEP 563 support, see #43 --- dataconf/main.py | 77 +++++++++++++++++++++++++-------- dataconf/utils.py | 54 +++++++++++++++++++---- tests/test_futur_annotations.py | 35 +++++++++++++++ 3 files changed, 140 insertions(+), 26 deletions(-) create mode 100644 tests/test_futur_annotations.py diff --git a/dataconf/main.py b/dataconf/main.py index f05e514..28faab2 100644 --- a/dataconf/main.py +++ b/dataconf/main.py @@ -1,3 +1,4 @@ +import inspect import os from typing import List @@ -9,11 +10,41 @@ import pyparsing +def inject_callee_scope(func): + def inner(*args, **kwargs): + noglobals = "globalns" not in kwargs + nolocals = "localns" not in kwargs + + if noglobals or nolocals: + frame = inspect.stack()[1][0] + + if noglobals: + kwargs["globalns"] = frame.f_globals + if nolocals: + kwargs["localns"] = frame.f_locals + + return func( + *args, + **kwargs, + ) + + return inner + + +@inject_callee_scope def parse( - conf: ConfigTree, clazz, strict: bool = True, ignore_unexpected: bool = False + conf: ConfigTree, + clazz, + strict: bool = True, + ignore_unexpected: bool = False, + globalns=None, + localns=None, ): + try: - return utils.__parse(conf, clazz, "", strict, ignore_unexpected) + return utils.__parse( + conf, clazz, "", strict, ignore_unexpected, globalns, localns + ) except pyparsing.ParseSyntaxException as e: raise MalformedConfigException( f'parsing failure line {e.lineno} character {e.col}, got "{e.line}"' @@ -50,42 +81,52 @@ def file(self, path: str, **kwargs) -> "Multi": conf = ConfigFactory.parse_file(path) return Multi(self.confs + [conf], self.strict, **kwargs) - def on(self, clazz): + @inject_callee_scope + def on(self, clazz, globalns=None, localns=None): conf, *nxts = self.confs for nxt in nxts: conf = ConfigTree.merge_configs(conf, nxt) - return parse(conf, clazz, self.strict, **self.kwargs) + return parse( + conf, clazz, self.strict, globalns=globalns, localns=localns, **self.kwargs + ) multi = Multi([]) -def env(prefix: str, clazz, **kwargs): - return multi.env(prefix, **kwargs).on(clazz) +@inject_callee_scope +def env(prefix: str, clazz, globalns=None, localns=None, **kwargs): + return multi.env(prefix, **kwargs).on(clazz, globalns=globalns, localns=localns) -def dict(obj: str, clazz, **kwargs): - return multi.dict(obj, **kwargs).on(clazz) +@inject_callee_scope +def dict(obj: str, clazz, globalns=None, localns=None, **kwargs): + return multi.dict(obj, **kwargs).on(clazz, globalns=globalns, localns=localns) -def string(s: str, clazz, **kwargs): - return multi.string(s, **kwargs).on(clazz) +@inject_callee_scope +def string(s: str, clazz, globalns=None, localns=None, **kwargs): + return multi.string(s, **kwargs).on(clazz, globalns=globalns, localns=localns) -def url(uri: str, clazz, **kwargs): - return multi.url(uri, **kwargs).on(clazz) +@inject_callee_scope +def url(uri: str, clazz, globalns=None, localns=None, **kwargs): + return multi.url(uri, **kwargs).on(clazz, globalns=globalns, localns=localns) -def file(path: str, clazz, **kwargs): - return multi.file(path, **kwargs).on(clazz) +@inject_callee_scope +def file(path: str, clazz, globalns=None, localns=None, **kwargs): + return multi.file(path, **kwargs).on(clazz, globalns=globalns, localns=localns) -def load(path: str, clazz, **kwargs): - return file(path, clazz, **kwargs) +@inject_callee_scope +def load(path: str, clazz, globalns=None, localns=None, **kwargs): + return file(path, clazz, globalns=globalns, localns=localns, **kwargs) -def loads(s: str, clazz, **kwargs): - return string(s, clazz, **kwargs) +@inject_callee_scope +def loads(s: str, clazz, globalns=None, localns=None, **kwargs): + return string(s, clazz, globalns=globalns, localns=localns, **kwargs) def dump(file: str, instance: object, out: str): diff --git a/dataconf/utils.py b/dataconf/utils.py index cea6272..435b6c8 100644 --- a/dataconf/utils.py +++ b/dataconf/utils.py @@ -5,6 +5,7 @@ from datetime import datetime from typing import get_args from typing import get_origin +from typing import get_type_hints from typing import Union from dataconf.exceptions import EnvListOrderException @@ -39,7 +40,7 @@ def is_optional(type): return get_origin(type) is Union and NoneType in get_args(type) -def __parse(value: any, clazz, path, strict, ignore_unexpected): +def __parse(value: any, clazz, path, strict, ignore_unexpected, globalns, localns): if is_dataclass(clazz): @@ -51,6 +52,7 @@ def __parse(value: any, clazz, path, strict, ignore_unexpected): fs = {} renamings = dict() + type_hints = get_type_hints(clazz, globalns, localns) for f in fields(clazz): if f.name in value: @@ -66,10 +68,16 @@ def __parse(value: any, clazz, path, strict, ignore_unexpected): if not isinstance(val, _MISSING_TYPE): fs[f.name] = __parse( - val, f.type, f"{path}.{f.name}", strict, ignore_unexpected + val, + type_hints[f.name], + f"{path}.{f.name}", + strict, + ignore_unexpected, + globalns, + localns, ) - elif is_optional(f.type): + elif is_optional(type_hints[f.name]): # Optional not found fs[f.name] = None @@ -94,7 +102,15 @@ def __parse(value: any, clazz, path, strict, ignore_unexpected): raise MissingTypeException("expected list with type information: List[?]") if value is not None: return [ - __parse(v, args[0], f"{path}[]", strict, ignore_unexpected) + __parse( + v, + args[0], + f"{path}[]", + strict, + ignore_unexpected, + globalns, + localns, + ) for v in value ] return None @@ -106,7 +122,15 @@ def __parse(value: any, clazz, path, strict, ignore_unexpected): ) if value is not None: return { - k: __parse(v, args[1], f"{path}.{k}", strict, ignore_unexpected) + k: __parse( + v, + args[1], + f"{path}.{k}", + strict, + ignore_unexpected, + globalns, + localns, + ) for k, v in value.items() } return None @@ -120,6 +144,8 @@ def __parse(value: any, clazz, path, strict, ignore_unexpected): path, strict, ignore_unexpected, + globalns, + localns, ) except TypeConfigException: # cannot parse Optional @@ -129,10 +155,14 @@ def __parse(value: any, clazz, path, strict, ignore_unexpected): left, right = args try: - return __parse(value, left, path, strict, ignore_unexpected) + return __parse( + value, left, path, strict, ignore_unexpected, globalns, localns + ) except TypeConfigException as left_failure: try: - return __parse(value, right, path, strict, ignore_unexpected) + return __parse( + value, right, path, strict, ignore_unexpected, globalns, localns + ) except TypeConfigException as right_failure: raise TypeConfigException( f"expected type {clazz} at {path}, failed both:\n- {left_failure}\n- {right_failure}" @@ -186,7 +216,15 @@ def __parse(value: any, clazz, path, strict, ignore_unexpected): for child_clazz in sorted(clazz.__subclasses__(), key=lambda c: c.__name__): if is_dataclass(child_clazz): try: - return __parse(value, child_clazz, path, strict, ignore_unexpected) + return __parse( + value, + child_clazz, + path, + strict, + ignore_unexpected, + globalns, + localns, + ) except ( TypeConfigException, MalformedConfigException, diff --git a/tests/test_futur_annotations.py b/tests/test_futur_annotations.py new file mode 100644 index 0000000..19fe2c6 --- /dev/null +++ b/tests/test_futur_annotations.py @@ -0,0 +1,35 @@ +from __future__ import annotations + +from dataclasses import dataclass +import os +from typing import get_type_hints +from typing import Text + +import dataconf +from dataconf.main import inject_callee_scope + + +@inject_callee_scope +def out_of_scope_assert(clazz, expected, globalns, localns): + assert get_type_hints(clazz, globalns, localns)["a"] is expected + + +class TestFuturAnnotations: + def test_43(self) -> None: + @dataclass + class Model: + token: str + + os.environ["TEST_token"] = "1" + dataconf.env("TEST_", Model) + + def test_repro(self) -> None: + @dataclass + class A: + value: Text + + @dataclass + class B: + a: A + + out_of_scope_assert(B, A, globalns={}) From e1bb09a08f125f11a4eefe0c6e16eeb1b71c70d3 Mon Sep 17 00:00:00 2001 From: Teo Stocco Date: Mon, 25 Jul 2022 01:23:35 +0200 Subject: [PATCH 2/3] chore: backport v2 --- dataconf/main.py | 20 +++- poetry.lock | 242 ++++++-------------------------------- tests/test_annotations.py | 35 ++++++ 3 files changed, 86 insertions(+), 211 deletions(-) create mode 100644 tests/test_annotations.py diff --git a/dataconf/main.py b/dataconf/main.py index 7797425..97008a4 100644 --- a/dataconf/main.py +++ b/dataconf/main.py @@ -66,71 +66,83 @@ def __init__(self, confs: List[ConfigTree], strict: bool = True, **kwargs) -> No self.strict = strict self.kwargs = kwargs + @inject_callee_scope def env(self, prefix: str, **kwargs) -> "Multi": self.strict = False data = env_vars_parse(prefix, os.environ) return self.dict(data, **kwargs) + @inject_callee_scope def dict(self, obj: str, **kwargs) -> "Multi": conf = ConfigFactory.from_dict(obj) return Multi(self.confs + [conf], self.strict, **kwargs) + @inject_callee_scope def string(self, s: str, **kwargs) -> "Multi": conf = ConfigFactory.parse_string(s) return Multi(self.confs + [conf], self.strict, **kwargs) + @inject_callee_scope def url(self, uri: str, **kwargs) -> "Multi": conf = ConfigFactory.parse_URL(uri) return Multi(self.confs + [conf], self.strict, **kwargs) + @inject_callee_scope def file(self, path: str, **kwargs) -> "Multi": conf = ConfigFactory.parse_file(path) return Multi(self.confs + [conf], self.strict, **kwargs) + @inject_callee_scope def cli(self, argv: List[str], **kwargs) -> "Multi": data = cli_parse(argv) return self.dict(data, **kwargs) - def on(self, clazz: Type, globalns=None, localns=None): + def on(self, clazz: Type): conf, *nxts = self.confs for nxt in nxts: conf = ConfigTree.merge_configs(conf, nxt) - return parse( - conf, clazz, self.strict, globalns=globalns, localns=localns, **self.kwargs - ) + return parse(conf, clazz, self.strict, **self.kwargs) multi = Multi([]) +@inject_callee_scope def env(prefix: str, clazz: Type, **kwargs): return multi.env(prefix, **kwargs).on(clazz) +@inject_callee_scope def dict(obj: str, clazz: Type, **kwargs): return multi.dict(obj, **kwargs).on(clazz) +@inject_callee_scope def string(s: str, clazz: Type, **kwargs): return multi.string(s, **kwargs).on(clazz) +@inject_callee_scope def url(uri: str, clazz: Type, **kwargs): return multi.url(uri, **kwargs).on(clazz) +@inject_callee_scope def file(path: str, clazz: Type, **kwargs): return multi.file(path, **kwargs).on(clazz) +@inject_callee_scope def cli(argv: List[str], clazz: Type, **kwargs): return multi.cli(argv, **kwargs).on(clazz) +@inject_callee_scope def load(path: str, clazz: Type, **kwargs): return file(path, clazz, **kwargs) +@inject_callee_scope def loads(s: str, clazz: Type, **kwargs): return string(s, clazz, **kwargs) diff --git a/poetry.lock b/poetry.lock index e7afd59..55932ce 100644 --- a/poetry.lock +++ b/poetry.lock @@ -386,210 +386,38 @@ python-versions = "^3.8" content-hash = "ba6ced0f1f1bcf63330c044b3a6c63206a3a770af804bb65dcf11eb1763024be" [metadata.files] -argcomplete = [ - {file = "argcomplete-1.12.3-py2.py3-none-any.whl", hash = "sha256:291f0beca7fd49ce285d2f10e4c1c77e9460cf823eef2de54df0c0fec88b0d81"}, - {file = "argcomplete-1.12.3.tar.gz", hash = "sha256:2c7dbffd8c045ea534921e63b0be6fe65e88599990d8dc408ac8c542b72a5445"}, -] -atomicwrites = [ - {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, - {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, -] -attrs = [ - {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, - {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, -] -cfgv = [ - {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, - {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, -] -colorama = [ - {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, - {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, -] -commitizen = [ - {file = "commitizen-2.27.1-py3-none-any.whl", hash = "sha256:046d512c5bc795cce625211434721946f21abf713f48753f2353ec1a3e114c3f"}, - {file = "commitizen-2.27.1.tar.gz", hash = "sha256:71a3e1fea37ced781bc440bd7d464abd5b797da8e762c1b9b632e007c2019b50"}, -] -decli = [ - {file = "decli-0.5.2-py3-none-any.whl", hash = "sha256:d3207bc02d0169bf6ed74ccca09ce62edca0eb25b0ebf8bf4ae3fb8333e15ca0"}, - {file = "decli-0.5.2.tar.gz", hash = "sha256:f2cde55034a75c819c630c7655a844c612f2598c42c21299160465df6ad463ad"}, -] -distlib = [ - {file = "distlib-0.3.4-py2.py3-none-any.whl", hash = "sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b"}, - {file = "distlib-0.3.4.zip", hash = "sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579"}, -] -filelock = [ - {file = "filelock-3.7.1-py3-none-any.whl", hash = "sha256:37def7b658813cda163b56fc564cdc75e86d338246458c4c28ae84cabefa2404"}, - {file = "filelock-3.7.1.tar.gz", hash = "sha256:3a0fd85166ad9dbab54c9aec96737b744106dc5f15c0b09a6744a445299fcf04"}, -] -identify = [ - {file = "identify-2.5.1-py2.py3-none-any.whl", hash = "sha256:0dca2ea3e4381c435ef9c33ba100a78a9b40c0bab11189c7cf121f75815efeaa"}, - {file = "identify-2.5.1.tar.gz", hash = "sha256:3d11b16f3fe19f52039fb7e39c9c884b21cb1b586988114fbe42671f03de3e82"}, -] -iniconfig = [ - {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, - {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, -] -jinja2 = [ - {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, - {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, -] -markupsafe = [ - {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, - {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, -] -nodeenv = [ - {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, - {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, -] -packaging = [ - {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, - {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, -] -platformdirs = [ - {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, - {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, -] -pluggy = [ - {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, - {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, -] -pre-commit = [ - {file = "pre_commit-2.19.0-py2.py3-none-any.whl", hash = "sha256:10c62741aa5704faea2ad69cb550ca78082efe5697d6f04e5710c3c229afdd10"}, - {file = "pre_commit-2.19.0.tar.gz", hash = "sha256:4233a1e38621c87d9dda9808c6606d7e7ba0e087cd56d3fe03202a01d2919615"}, -] -prompt-toolkit = [ - {file = "prompt_toolkit-3.0.30-py3-none-any.whl", hash = "sha256:d8916d3f62a7b67ab353a952ce4ced6a1d2587dfe9ef8ebc30dd7c386751f289"}, - {file = "prompt_toolkit-3.0.30.tar.gz", hash = "sha256:859b283c50bde45f5f97829f77a4674d1c1fcd88539364f1b28a37805cfd89c0"}, -] -py = [ - {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, - {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, -] -pyhocon = [ - {file = "pyhocon-0.3.59.tar.gz", hash = "sha256:bb0eee59b57ecea5c30d14efc7ee7e55c935d58f03025ccdfc49237e920aa48c"}, -] -pyparsing = [ - {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, - {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, -] -pytest = [ - {file = "pytest-7.1.2-py3-none-any.whl", hash = "sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c"}, - {file = "pytest-7.1.2.tar.gz", hash = "sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45"}, -] -python-dateutil = [ - {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, - {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, -] -pyyaml = [ - {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, - {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, - {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, - {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, - {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, - {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, - {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, - {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, - {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, - {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, - {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, - {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, - {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, - {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, - {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, - {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, -] -questionary = [ - {file = "questionary-1.10.0-py3-none-any.whl", hash = "sha256:fecfcc8cca110fda9d561cb83f1e97ecbb93c613ff857f655818839dac74ce90"}, - {file = "questionary-1.10.0.tar.gz", hash = "sha256:600d3aefecce26d48d97eee936fdb66e4bc27f934c3ab6dd1e292c4f43946d90"}, -] -setuptools = [ - {file = "setuptools-62.6.0-py3-none-any.whl", hash = "sha256:c1848f654aea2e3526d17fc3ce6aeaa5e7e24e66e645b5be2171f3f6b4e5a178"}, - {file = "setuptools-62.6.0.tar.gz", hash = "sha256:990a4f7861b31532871ab72331e755b5f14efbe52d336ea7f6118144dd478741"}, -] -six = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] -termcolor = [ - {file = "termcolor-1.1.0.tar.gz", hash = "sha256:1d6d69ce66211143803fbc56652b41d73b4a400a2891d7bf7a1cdf4c02de613b"}, -] -toml = [ - {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, - {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, -] -tomli = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] -tomlkit = [ - {file = "tomlkit-0.11.0-py3-none-any.whl", hash = "sha256:0f4050db66fd445b885778900ce4dd9aea8c90c4721141fde0d6ade893820ef1"}, - {file = "tomlkit-0.11.0.tar.gz", hash = "sha256:71ceb10c0eefd8b8f11fe34e8a51ad07812cb1dc3de23247425fbc9ddc47b9dd"}, -] -typing-extensions = [ - {file = "typing_extensions-4.2.0-py3-none-any.whl", hash = "sha256:6657594ee297170d19f67d55c05852a874e7eb634f4f753dbd667855e07c1708"}, - {file = "typing_extensions-4.2.0.tar.gz", hash = "sha256:f1c24655a0da0d1b67f07e17a5e6b2a105894e6824b92096378bb3668ef02376"}, -] -virtualenv = [ - {file = "virtualenv-20.15.0-py2.py3-none-any.whl", hash = "sha256:804cce4de5b8a322f099897e308eecc8f6e2951f1a8e7e2b3598dff865f01336"}, - {file = "virtualenv-20.15.0.tar.gz", hash = "sha256:4c44b1d77ca81f8368e2d7414f9b20c428ad16b343ac6d226206c5b84e2b4fcc"}, -] -wcwidth = [ - {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, - {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, -] +argcomplete = [] +atomicwrites = [] +attrs = [] +cfgv = [] +colorama = [] +commitizen = [] +decli = [] +distlib = [] +filelock = [] +identify = [] +iniconfig = [] +jinja2 = [] +markupsafe = [] +nodeenv = [] +packaging = [] +platformdirs = [] +pluggy = [] +pre-commit = [] +prompt-toolkit = [] +py = [] +pyhocon = [] +pyparsing = [] +pytest = [] +python-dateutil = [] +pyyaml = [] +questionary = [] +setuptools = [] +six = [] +termcolor = [] +toml = [] +tomli = [] +tomlkit = [] +typing-extensions = [] +virtualenv = [] +wcwidth = [] diff --git a/tests/test_annotations.py b/tests/test_annotations.py new file mode 100644 index 0000000..19fe2c6 --- /dev/null +++ b/tests/test_annotations.py @@ -0,0 +1,35 @@ +from __future__ import annotations + +from dataclasses import dataclass +import os +from typing import get_type_hints +from typing import Text + +import dataconf +from dataconf.main import inject_callee_scope + + +@inject_callee_scope +def out_of_scope_assert(clazz, expected, globalns, localns): + assert get_type_hints(clazz, globalns, localns)["a"] is expected + + +class TestFuturAnnotations: + def test_43(self) -> None: + @dataclass + class Model: + token: str + + os.environ["TEST_token"] = "1" + dataconf.env("TEST_", Model) + + def test_repro(self) -> None: + @dataclass + class A: + value: Text + + @dataclass + class B: + a: A + + out_of_scope_assert(B, A, globalns={}) From 6c8130ce314250bcbe1b568ae89edc82324e1bc9 Mon Sep 17 00:00:00 2001 From: Teo Stocco Date: Mon, 25 Jul 2022 01:32:53 +0200 Subject: [PATCH 3/3] chore: refactor --- dataconf/main.py | 50 +++++++++++++++++---------------------- tests/test_annotations.py | 35 --------------------------- 2 files changed, 22 insertions(+), 63 deletions(-) delete mode 100644 tests/test_annotations.py diff --git a/dataconf/main.py b/dataconf/main.py index 97008a4..8e3136e 100644 --- a/dataconf/main.py +++ b/dataconf/main.py @@ -61,47 +61,41 @@ def cli_parse(*args, **kwargs): class Multi: - def __init__(self, confs: List[ConfigTree], strict: bool = True, **kwargs) -> None: + def __init__(self, confs: List[ConfigTree], strict: bool = True) -> None: self.confs = confs self.strict = strict - self.kwargs = kwargs - @inject_callee_scope - def env(self, prefix: str, **kwargs) -> "Multi": + def env(self, prefix: str) -> "Multi": self.strict = False data = env_vars_parse(prefix, os.environ) - return self.dict(data, **kwargs) + return self.dict(data) - @inject_callee_scope - def dict(self, obj: str, **kwargs) -> "Multi": + def dict(self, obj: str) -> "Multi": conf = ConfigFactory.from_dict(obj) - return Multi(self.confs + [conf], self.strict, **kwargs) + return Multi(self.confs + [conf], self.strict) - @inject_callee_scope - def string(self, s: str, **kwargs) -> "Multi": + def string(self, s: str) -> "Multi": conf = ConfigFactory.parse_string(s) - return Multi(self.confs + [conf], self.strict, **kwargs) + return Multi(self.confs + [conf], self.strict) - @inject_callee_scope - def url(self, uri: str, **kwargs) -> "Multi": + def url(self, uri: str) -> "Multi": conf = ConfigFactory.parse_URL(uri) - return Multi(self.confs + [conf], self.strict, **kwargs) + return Multi(self.confs + [conf], self.strict) - @inject_callee_scope - def file(self, path: str, **kwargs) -> "Multi": + def file(self, path: str) -> "Multi": conf = ConfigFactory.parse_file(path) - return Multi(self.confs + [conf], self.strict, **kwargs) + return Multi(self.confs + [conf], self.strict) - @inject_callee_scope - def cli(self, argv: List[str], **kwargs) -> "Multi": + def cli(self, argv: List[str]) -> "Multi": data = cli_parse(argv) - return self.dict(data, **kwargs) + return self.dict(data) - def on(self, clazz: Type): + @inject_callee_scope + def on(self, clazz: Type, **kwargs): conf, *nxts = self.confs for nxt in nxts: conf = ConfigTree.merge_configs(conf, nxt) - return parse(conf, clazz, self.strict, **self.kwargs) + return parse(conf, clazz, self.strict, **kwargs) multi = Multi([]) @@ -109,32 +103,32 @@ def on(self, clazz: Type): @inject_callee_scope def env(prefix: str, clazz: Type, **kwargs): - return multi.env(prefix, **kwargs).on(clazz) + return multi.env(prefix).on(clazz, **kwargs) @inject_callee_scope def dict(obj: str, clazz: Type, **kwargs): - return multi.dict(obj, **kwargs).on(clazz) + return multi.dict(obj).on(clazz, **kwargs) @inject_callee_scope def string(s: str, clazz: Type, **kwargs): - return multi.string(s, **kwargs).on(clazz) + return multi.string(s).on(clazz, **kwargs) @inject_callee_scope def url(uri: str, clazz: Type, **kwargs): - return multi.url(uri, **kwargs).on(clazz) + return multi.url(uri).on(clazz, **kwargs) @inject_callee_scope def file(path: str, clazz: Type, **kwargs): - return multi.file(path, **kwargs).on(clazz) + return multi.file(path).on(clazz, **kwargs) @inject_callee_scope def cli(argv: List[str], clazz: Type, **kwargs): - return multi.cli(argv, **kwargs).on(clazz) + return multi.cli(argv).on(clazz, **kwargs) @inject_callee_scope diff --git a/tests/test_annotations.py b/tests/test_annotations.py deleted file mode 100644 index 19fe2c6..0000000 --- a/tests/test_annotations.py +++ /dev/null @@ -1,35 +0,0 @@ -from __future__ import annotations - -from dataclasses import dataclass -import os -from typing import get_type_hints -from typing import Text - -import dataconf -from dataconf.main import inject_callee_scope - - -@inject_callee_scope -def out_of_scope_assert(clazz, expected, globalns, localns): - assert get_type_hints(clazz, globalns, localns)["a"] is expected - - -class TestFuturAnnotations: - def test_43(self) -> None: - @dataclass - class Model: - token: str - - os.environ["TEST_token"] = "1" - dataconf.env("TEST_", Model) - - def test_repro(self) -> None: - @dataclass - class A: - value: Text - - @dataclass - class B: - a: A - - out_of_scope_assert(B, A, globalns={})