dataconf 2.2.0__tar.gz → 2.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dataconf
3
- Version: 2.2.0
3
+ Version: 2.2.2
4
4
  Summary: Simple dataclasses configuration management for Python with hocon/json/yaml/properties/env-vars/dict support.
5
5
  Home-page: https://github.com/zifeo/dataconf
6
6
  License: MPL-2.0
@@ -15,8 +15,8 @@ Classifier: Programming Language :: Python :: 3.10
15
15
  Classifier: Programming Language :: Python :: 3.11
16
16
  Classifier: Programming Language :: Python :: 3.8
17
17
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
- Requires-Dist: PyYAML (>=6.0,<7.0)
19
- Requires-Dist: pyhocon (==0.3.60)
18
+ Requires-Dist: PyYAML (>=6.0.1,<7.0.0)
19
+ Requires-Dist: pyhocon (==0.3.59)
20
20
  Requires-Dist: python-dateutil (>=2.8.2,<3.0.0)
21
21
  Project-URL: Repository, https://github.com/zifeo/dataconf
22
22
  Description-Content-Type: text/markdown
@@ -6,6 +6,7 @@ from datetime import datetime
6
6
  from enum import Enum
7
7
  from enum import IntEnum
8
8
  from inspect import isclass
9
+
9
10
  from typing import Any
10
11
  from typing import Dict
11
12
  from typing import get_args
@@ -28,6 +29,12 @@ from pyhocon.config_tree import ConfigList
28
29
  from pyhocon.config_tree import ConfigTree
29
30
  import pyparsing
30
31
 
32
+ from dataconf.version import PY310up
33
+
34
+
35
+ if PY310up:
36
+ from types import UnionType
37
+
31
38
 
32
39
  NoneType = type(None)
33
40
 
@@ -42,9 +49,13 @@ def __parse_type(value: Any, clazz: Type, path: str, check: bool):
42
49
  raise TypeConfigException(f"expected type {clazz} at {path}, got {type(value)}")
43
50
 
44
51
 
52
+ def is_union(origin):
53
+ return origin is Union or (PY310up and origin is UnionType)
54
+
55
+
45
56
  def is_optional(type: Type):
46
57
  # Optional = Union[T, NoneType]
47
- return get_origin(type) is Union and NoneType in get_args(type)
58
+ return is_union(get_origin(type)) and NoneType in get_args(type)
48
59
 
49
60
 
50
61
  def __parse(value: any, clazz: Type, path: str, strict: bool, ignore_unexpected: bool):
@@ -68,6 +79,9 @@ def __parse(value: any, clazz: Type, path: str, strict: bool, ignore_unexpected:
68
79
  val = f.default_factory()
69
80
  else:
70
81
  val = f.default
82
+ if is_dataclass(val):
83
+ # if val is a dataclass, convert to ConfigTree
84
+ val = ConfigTree(asdict(val))
71
85
 
72
86
  if not isinstance(val, _MISSING_TYPE):
73
87
  fs[f.name] = __parse(
@@ -120,33 +134,29 @@ def __parse(value: any, clazz: Type, path: str, strict: bool, ignore_unexpected:
120
134
  }
121
135
  return None
122
136
 
123
- if is_optional(clazz):
124
- left, right = args
125
- try:
126
- return __parse(
127
- value,
128
- left if right is NoneType else right,
129
- path,
130
- strict,
131
- ignore_unexpected,
132
- )
133
- except TypeConfigException:
134
- # cannot parse Optional
135
- return None
136
-
137
- if origin is Union:
137
+ if is_union(origin):
138
+ # Optional = Union[T, NoneType]
139
+ has_none = False
138
140
  for parse_candidate in args:
139
- try:
140
- return __parse(value, parse_candidate, path, strict, ignore_unexpected)
141
- except TypeConfigException:
142
- continue
141
+ if parse_candidate is NoneType:
142
+ has_none = True
143
+ else:
144
+ try:
145
+ return __parse(
146
+ value, parse_candidate, path, strict, ignore_unexpected
147
+ )
148
+ except TypeConfigException:
149
+ continue
150
+
151
+ if has_none:
152
+ return None
143
153
 
144
154
  raise TypeConfigException(
145
155
  f"expected one of {', '.join(map(str, args))} at {path}, got {type(value)}"
146
156
  )
147
157
 
148
158
  if clazz is bool:
149
- if not strict:
159
+ if not strict and value is not None:
150
160
  try:
151
161
  value = bool(value)
152
162
  except ValueError:
@@ -154,7 +164,7 @@ def __parse(value: any, clazz: Type, path: str, strict: bool, ignore_unexpected:
154
164
  return __parse_type(value, clazz, path, isinstance(value, bool))
155
165
 
156
166
  if clazz is int:
157
- if not strict:
167
+ if not strict and value is not None:
158
168
  try:
159
169
  value = int(value)
160
170
  except ValueError:
@@ -162,7 +172,7 @@ def __parse(value: any, clazz: Type, path: str, strict: bool, ignore_unexpected:
162
172
  return __parse_type(value, clazz, path, isinstance(value, int))
163
173
 
164
174
  if clazz is float:
165
- if not strict:
175
+ if not strict and value is not None:
166
176
  try:
167
177
  value = float(value)
168
178
  except ValueError:
@@ -0,0 +1,6 @@
1
+ import sys
2
+
3
+ __version__ = "2.2.2"
4
+
5
+ PY310up = sys.version_info >= (3, 10)
6
+ PY311up = sys.version_info >= (3, 11)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "dataconf"
3
- version = "2.2.0"
3
+ version = "2.2.2"
4
4
  description = "Simple dataclasses configuration management for Python with hocon/json/yaml/properties/env-vars/dict support."
5
5
  authors = []
6
6
  license = "MPL-2.0"
@@ -30,20 +30,20 @@ dataconf = 'dataconf.cli:run'
30
30
 
31
31
  [tool.poetry.dependencies]
32
32
  python = "^3.8"
33
- pyhocon = "0.3.60"
33
+ pyhocon = "0.3.59"
34
34
  python-dateutil = "^2.8.2"
35
- PyYAML = "^6.0"
35
+ PyYAML = "^6.0.1"
36
36
 
37
37
  [tool.poetry.group.dev.dependencies]
38
- pytest = "^7.3.1"
38
+ pytest = "^7.4.2"
39
39
  pre-commit = ">=2.21,<4.0"
40
40
  commitizen = ">=2.42.1,<4.0.0"
41
- pytest-httpserver = "^1.0.6"
42
- ruff = ">=0.0.247,<0.0.264"
41
+ pytest-httpserver = "^1.0.8"
42
+ ruff = ">=0.0.247,<0.0.287"
43
43
 
44
44
  [tool.commitizen]
45
45
  name = "cz_conventional_commits"
46
- version = "2.2.0"
46
+ version = "2.2.2"
47
47
  tag_format = "v$major.$minor.$patch$prerelease"
48
48
  version_files = [
49
49
  "pyproject.toml:version",
@@ -8,14 +8,14 @@ package_data = \
8
8
  {'': ['*']}
9
9
 
10
10
  install_requires = \
11
- ['PyYAML>=6.0,<7.0', 'pyhocon==0.3.60', 'python-dateutil>=2.8.2,<3.0.0']
11
+ ['PyYAML>=6.0.1,<7.0.0', 'pyhocon==0.3.59', 'python-dateutil>=2.8.2,<3.0.0']
12
12
 
13
13
  entry_points = \
14
14
  {'console_scripts': ['dataconf = dataconf.cli:run']}
15
15
 
16
16
  setup_kwargs = {
17
17
  'name': 'dataconf',
18
- 'version': '2.2.0',
18
+ 'version': '2.2.2',
19
19
  'description': 'Simple dataclasses configuration management for Python with hocon/json/yaml/properties/env-vars/dict support.',
20
20
  'long_description': '# Dataconf\n\n[![Actions Status](https://github.com/zifeo/dataconf/workflows/CI/badge.svg)](https://github.com/zifeo/dataconf/actions)\n[![PyPI version](https://badge.fury.io/py/dataconf.svg)](https://badge.fury.io/py/dataconf)\n\nSimple dataclasses configuration management for Python with\nhocon/json/yaml/properties/env-vars/dict/cli support.\n\n## Getting started\n\nRequires at least Python 3.8.\n\n```bash\n# pypi\npip install dataconf\npoetry add dataconf\n\n# remote master\npip install --upgrade git+https://github.com/zifeo/dataconf.git\npoetry add git+https://github.com/zifeo/dataconf.git\n\n# local repo/dev\npoetry install\npre-commit install\n```\n\n## Usage\n\n```python\nimport os\nfrom dataclasses import dataclass, field\nfrom typing import List, Dict, Text, Union\nfrom dateutil.relativedelta import relativedelta\nfrom datetime import datetime\nimport dataconf\n\nconf = """\nstr_name = test\nstr_name = ${?HOME}\ndash-to-underscore = true\nfloat_num = 2.2\niso_datetime = "2000-01-01T20:00:00"\n# this is a comment\nlist_data = [\n a\n b\n]\nnested {\n a = test\n b : 1\n}\nnested_list = [\n {\n a = test1\n b : 2.5\n }\n]\nduration = 2s\nunion = 1\npeople {\n name = Thailand\n}\nzone {\n area_code = 42\n}\n"""\n\nclass AbstractBaseClass:\n pass\n \n@dataclass\nclass Person(AbstractBaseClass):\n name: Text\n \n@dataclass\nclass Zone(AbstractBaseClass):\n area_code: int\n\n@dataclass\nclass Nested:\n a: Text\n b: float\n\n@dataclass\nclass Config:\n str_name: Text\n dash_to_underscore: bool\n float_num: float\n iso_datetime: datetime\n list_data: List[Text]\n nested: Nested\n nested_list: List[Nested]\n duration: relativedelta\n union: Union[Text, int]\n people: AbstractBaseClass\n zone: AbstractBaseClass\n default: Text = \'hello\'\n default_factory: Dict[Text, Text] = field(default_factory=dict)\n\nprint(dataconf.string(conf, Config))\n# Config(\n# str_name=\'/users/root\',\n# dash_to_underscore=True,\n# float_num=2.2,\n# list_data=[\'a\', \'b\'],\n# nested=Nested(a=\'test\'),\n# nested_list=[Nested(a=\'test1\', b=2.5)],\n# duration=relativedelta(seconds=+2), \n# union=1, \n# people=Person(name=\'Thailand\'), \n# zone=Zone(area_code=42),\n# default=\'hello\', \n# default_factory={}\n# )\n\n@dataclass\nclass Example:\n hello: str\n world: str\n foo: List[str]\n\nos.environ[\'DC_WORLD\'] = \'monde\'\n\nprint(\n dataconf\n .multi\n .url(\'https://raw.githubusercontent.com/zifeo/dataconf/main/confs/simple.hocon\')\n .env(\'DC\')\n .on(Example)\n)\n# Example(hello=\'bonjour\',world=\'monde\')\n```\n\n## API\n\n```python\nimport dataconf\n\nconf = dataconf.string(\'{ name: Test }\', Config)\nconf = dataconf.string(\'name:\\n\\tvalue: Test\', Config, loader=dataconf.YAML) # dataconf.HOCON by default\nconf = dataconf.env(\'PREFIX_\', Config)\nconf = dataconf.dict({\'name\': \'Test\'}, Config)\nconf = dataconf.url(\'https://raw.githubusercontent.com/zifeo/dataconf/master/confs/test.hocon\', Config) # hocon, json, yaml, properties\nconf = dataconf.file(\'confs/test.hocon\', Config) # hocon, json, yaml, properties\nconf = dataconf.cli(sys.argv, Config)\n\n# Aggregation\nconf = dataconf.multi.string(...).env(...).url(...).file(...).dict(...).cli(...).on(Config)\n\n# Same api as Python json/yaml packages (e.g. `load`, `loads`, `dump`, `dumps`)\nconf = dataconf.load(\'confs/test.hocon\', Config) # hocon, json, yaml, properties\nconf = dataconf.load(\'confs/test.yaml\', Config, loader=dataconf.YAML) # dataconf.HOCON by default\ndataconf.dump(\'confs/test.hocon\', conf, out=\'hocon\')\ndataconf.dump(\'confs/test.json\', conf, out=\'json\')\ndataconf.dump(\'confs/test.yaml\', conf, out=\'yaml\')\ndataconf.dump(\'confs/test.properties\', conf, out=\'properties\')\n```\n\nFor full HOCON capabilities see\n[here](https://github.com/chimpler/pyhocon/#example-of-hocon-file).\n\n## Parse env vars\n\n```bash\nPREFIX_VAR=a\nPREFIX_VAR_NAME=b\nPREFIX_TEST__NAME=c\nPREFIX_LS_0=d\nPREFIX_LS_1=e\nPREFIX_LSLS_0_0=f\nPREFIX_LSOB_0__NAME=g\nPREFIX_NESTED_="{ name: Test }"\nPREFIX_SUB_="{ value: ${PREFIX_VAR} }"\n```\n\nis equivalent to\n\n```\n{\n var = a\n var_name = b\n test {\n name = c\n }\n ls = [\n d\n e\n ]\n lsls = [\n [\n f\n ]\n ]\n lsob = [\n {\n name = g\n }\n ]\n nested {\n # parse nested config by suffixing env var with `_`\n name: Test\n }\n sub {\n # will have value "a" at parsing, useful for aliases\n value = ${PREFIX_VAR}\n }\n}\n```\n\nNote that when using `.env` source, the strict mode is disabled and value might\nbe casted.\n\n## Parse CLI arguments\n\nSame as env vars parse (dashes are converted to underscore, e.g. `TEST_A` →\n`--test-a`).\n\n## CLI usage\n\nCan be used for validation or converting between supported file formats (`-o`).\n\n```shell\ndataconf -c confs/test.hocon -m tests.configs -d TestConf -o hocon\n# dataconf.exceptions.TypeConfigException: expected type <class \'datetime.timedelta\'> at .duration, got <class \'int\'>\n```\n',
21
21
  'author': 'None',
@@ -1 +0,0 @@
1
- __version__ = "2.2.0"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes