dataconf 3.7.0__tar.gz → 3.7.1__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,24 +1,26 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: dataconf
3
- Version: 3.7.0
3
+ Version: 3.7.1
4
4
  Summary: Simple dataclasses configuration management for Python with hocon/json/yaml/properties/env-vars/dict support.
5
- Home-page: https://github.com/zifeo/dataconf
6
5
  License: MPL-2.0
6
+ License-File: LICENSE
7
7
  Keywords: configuration,dataclasses,yaml,hocon,json,properties,cli,environment variables
8
- Requires-Python: >=3.9,<4.0
8
+ Requires-Python: >=3.10,<4.0
9
9
  Classifier: License :: OSI Approved
10
10
  Classifier: Programming Language :: Python
11
11
  Classifier: Programming Language :: Python :: 3
12
- Classifier: Programming Language :: Python :: 3.9
13
12
  Classifier: Programming Language :: Python :: 3.10
14
13
  Classifier: Programming Language :: Python :: 3.11
15
14
  Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Classifier: Programming Language :: Python :: 3.15
16
18
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
19
  Requires-Dist: PyYAML (>=6.0.2,<7.0.0)
18
20
  Requires-Dist: isodate (>=0.7.2,<0.8.0)
19
21
  Requires-Dist: pyhocon (>=0.3.61,<0.4.0)
20
22
  Requires-Dist: pyparsing (>=3.2.3,<4.0.0)
21
- Requires-Dist: python-dateutil (>=2.9.0.post0,<3.0.0)
23
+ Requires-Dist: python-dateutil (>=3.0.0,<4.0.0)
22
24
  Project-URL: Repository, https://github.com/zifeo/dataconf
23
25
  Description-Content-Type: text/markdown
24
26
 
@@ -32,7 +34,7 @@ hocon/json/yaml/properties/env-vars/dict/cli support.
32
34
 
33
35
  ## Getting started
34
36
 
35
- Requires at least Python 3.9.
37
+ Requires at least Python 3.10.
36
38
 
37
39
  ```bash
38
40
  # pypi
@@ -8,7 +8,7 @@ hocon/json/yaml/properties/env-vars/dict/cli support.
8
8
 
9
9
  ## Getting started
10
10
 
11
- Requires at least Python 3.9.
11
+ Requires at least Python 3.10.
12
12
 
13
13
  ```bash
14
14
  # pypi
@@ -1,46 +1,30 @@
1
1
  class TypeConfigException(Exception):
2
2
  """Type mismatch exception."""
3
3
 
4
- pass
5
-
6
4
 
7
5
  class MissingTypeException(Exception):
8
6
  """Missing type exception (e.g. List instead of List[int]."""
9
7
 
10
- pass
11
-
12
8
 
13
9
  class AmbiguousSubclassException(Exception):
14
10
  """Multiple subclasses match the the given parameters. Use '_type' field to disambiguate"""
15
11
 
16
- pass
17
-
18
12
 
19
13
  class MalformedConfigException(Exception):
20
14
  """Missing values exception."""
21
15
 
22
- pass
23
-
24
16
 
25
17
  class UnexpectedKeysException(Exception):
26
18
  """Unexpected keys exception."""
27
19
 
28
- pass
29
-
30
20
 
31
21
  class EnvListOrderException(Exception):
32
22
  """Ordering exception."""
33
23
 
34
- pass
35
-
36
24
 
37
25
  class EnvListFormatException(Exception):
38
26
  """Format exception."""
39
27
 
40
- pass
41
-
42
28
 
43
29
  class ParseException(Exception):
44
30
  """Parsing exception."""
45
-
46
- pass
@@ -1,20 +1,18 @@
1
1
  import contextlib
2
2
  import os
3
- from typing import Any, Optional
4
- from typing import Dict
5
- from typing import List
6
- from typing import Type
3
+ from typing import Any
7
4
  from urllib.parse import urlparse
8
5
  from urllib.request import urlopen
9
6
 
10
- from dataconf import utils
11
- from dataconf.exceptions import MalformedConfigException
7
+ import pyparsing
12
8
  from pyhocon import ConfigFactory
13
9
  from pyhocon import HOCONConverter
14
10
  from pyhocon.config_parser import ConfigTree
15
- import pyparsing
16
11
  from yaml import safe_load
17
12
 
13
+ from dataconf import utils
14
+ from dataconf.exceptions import MalformedConfigException
15
+
18
16
  HOCON = 1
19
17
  YAML = 2
20
18
 
@@ -39,7 +37,7 @@ def cli_parse(*args, **kwargs):
39
37
 
40
38
 
41
39
  class Multi:
42
- def __init__(self, confs: List[ConfigTree], strict: bool = True, **kwargs) -> None:
40
+ def __init__(self, confs: list[ConfigTree], strict: bool = True, **kwargs) -> None:
43
41
  self.confs = confs
44
42
  self.strict = strict
45
43
  self.kwargs = kwargs
@@ -49,7 +47,7 @@ class Multi:
49
47
  strict = kwargs.pop("strict", False)
50
48
  return self.dict(data, strict=strict, **kwargs)
51
49
 
52
- def dict(self, obj: Dict[str, Any], **kwargs) -> "Multi":
50
+ def dict(self, obj: dict[str, Any], **kwargs) -> "Multi":
53
51
  conf = ConfigFactory.from_dict(obj)
54
52
  strict = kwargs.pop("strict", self.strict)
55
53
  return Multi(self.confs + [conf], strict, **kwargs)
@@ -64,7 +62,7 @@ class Multi:
64
62
 
65
63
  def url(self, uri: str, timeout: int = 10, **kwargs) -> "Multi":
66
64
  path = urlparse(uri).path
67
- if path.endswith(".yaml") or path.endswith(".yml"):
65
+ if path.endswith((".yaml", ".yml")):
68
66
  with contextlib.closing(urlopen(uri, timeout=timeout)) as fd:
69
67
  s = fd.read().decode("utf-8")
70
68
  return self.string(s, loader=YAML, **kwargs)
@@ -75,16 +73,14 @@ class Multi:
75
73
  def file(
76
74
  self,
77
75
  path: str,
78
- loader: Optional[str] = None,
76
+ loader: str | None = None,
79
77
  allow_missing: bool = False,
80
78
  **kwargs,
81
79
  ) -> "Multi":
82
80
  if allow_missing and not os.path.exists(path):
83
81
  return self.dict({}, **kwargs)
84
82
 
85
- if loader == YAML or (
86
- loader is None and (path.endswith(".yaml") or path.endswith(".yml"))
87
- ):
83
+ if loader == YAML or (loader is None and path.endswith((".yaml", ".yml"))):
88
84
  with open(path, "r") as f:
89
85
  data = safe_load(f)
90
86
  return self.dict(data, **kwargs)
@@ -92,11 +88,11 @@ class Multi:
92
88
  conf = ConfigFactory.parse_file(path)
93
89
  return Multi(self.confs + [conf], self.strict, **kwargs)
94
90
 
95
- def cli(self, argv: List[str], **kwargs) -> "Multi":
91
+ def cli(self, argv: list[str], **kwargs) -> "Multi":
96
92
  data = cli_parse(argv)
97
93
  return self.dict(data, **kwargs)
98
94
 
99
- def on(self, clazz: Type):
95
+ def on(self, clazz: type):
100
96
  conf, *nxts = self.confs
101
97
  for nxt in nxts:
102
98
  conf = ConfigTree.merge_configs(conf, nxt)
@@ -106,35 +102,35 @@ class Multi:
106
102
  multi = Multi([])
107
103
 
108
104
 
109
- def env(prefix: str, clazz: Type, **kwargs):
105
+ def env(prefix: str, clazz: type, **kwargs):
110
106
  return multi.env(prefix, **kwargs).on(clazz)
111
107
 
112
108
 
113
- def dict(obj: Dict[str, Any], clazz: Type, **kwargs):
109
+ def dict(obj: dict[str, Any], clazz: type, **kwargs):
114
110
  return multi.dict(obj, **kwargs).on(clazz)
115
111
 
116
112
 
117
- def string(s: str, clazz: Type, **kwargs):
113
+ def string(s: str, clazz: type, **kwargs):
118
114
  return multi.string(s, **kwargs).on(clazz)
119
115
 
120
116
 
121
- def url(uri: str, clazz: Type, **kwargs):
117
+ def url(uri: str, clazz: type, **kwargs):
122
118
  return multi.url(uri, **kwargs).on(clazz)
123
119
 
124
120
 
125
- def file(path: str, clazz: Type, **kwargs):
121
+ def file(path: str, clazz: type, **kwargs):
126
122
  return multi.file(path, **kwargs).on(clazz)
127
123
 
128
124
 
129
- def cli(argv: List[str], clazz: Type, **kwargs):
125
+ def cli(argv: list[str], clazz: type, **kwargs):
130
126
  return multi.cli(argv, **kwargs).on(clazz)
131
127
 
132
128
 
133
- def load(path: str, clazz: Type, **kwargs):
129
+ def load(path: str, clazz: type, **kwargs):
134
130
  return file(path, clazz, **kwargs)
135
131
 
136
132
 
137
- def loads(s: str, clazz: Type, **kwargs):
133
+ def loads(s: str, clazz: type, **kwargs):
138
134
  return string(s, clazz, **kwargs)
139
135
 
140
136
 
@@ -1,4 +1,4 @@
1
- from dataclasses import _MISSING_TYPE
1
+ from dataclasses import MISSING
2
2
  from dataclasses import asdict
3
3
  from dataclasses import fields
4
4
  from dataclasses import is_dataclass
@@ -8,37 +8,30 @@ from enum import Enum
8
8
  from enum import IntEnum
9
9
  from inspect import isclass
10
10
  from pathlib import Path
11
-
12
- from typing import Any, Literal
13
- from typing import Dict
11
+ from types import UnionType
12
+ from typing import Any
13
+ from typing import Literal
14
+ from typing import Union
14
15
  from typing import get_args
15
16
  from typing import get_origin
16
- from typing import List
17
- from typing import Type
18
- from typing import Union
19
17
 
20
- from dataconf.exceptions import AmbiguousSubclassException, EnvListFormatException
21
- from dataconf.exceptions import EnvListOrderException
22
- from dataconf.exceptions import MalformedConfigException
23
- from dataconf.exceptions import MissingTypeException
24
- from dataconf.exceptions import ParseException
25
- from dataconf.exceptions import TypeConfigException
26
- from dataconf.exceptions import UnexpectedKeysException
18
+ import pyparsing
27
19
  from dateutil.parser import isoparse
28
20
  from dateutil.relativedelta import relativedelta
21
+ from isodate import Duration
22
+ from isodate import parse_duration
29
23
  from pyhocon import ConfigFactory
30
24
  from pyhocon.config_tree import ConfigList
31
25
  from pyhocon.config_tree import ConfigTree
32
- from isodate import parse_duration
33
- from isodate import Duration
34
- import pyparsing
35
-
36
- from dataconf.version import PY310up
37
-
38
-
39
- if PY310up:
40
- from types import UnionType
41
26
 
27
+ from dataconf.exceptions import AmbiguousSubclassException
28
+ from dataconf.exceptions import EnvListFormatException
29
+ from dataconf.exceptions import EnvListOrderException
30
+ from dataconf.exceptions import MalformedConfigException
31
+ from dataconf.exceptions import MissingTypeException
32
+ from dataconf.exceptions import ParseException
33
+ from dataconf.exceptions import TypeConfigException
34
+ from dataconf.exceptions import UnexpectedKeysException
42
35
 
43
36
  NoneType = type(None)
44
37
 
@@ -58,7 +51,7 @@ def __parse_bool(value: Any):
58
51
  return None
59
52
 
60
53
 
61
- def __parse_type(value: Any, clazz: Type, path: str, check: bool):
54
+ def __parse_type(value: Any, clazz: type, path: str, check: bool):
62
55
  try:
63
56
  if check:
64
57
  return value
@@ -71,15 +64,15 @@ def __parse_type(value: Any, clazz: Type, path: str, check: bool):
71
64
 
72
65
 
73
66
  def is_union(origin):
74
- return origin is Union or (PY310up and origin is UnionType)
67
+ return origin is Union or origin is UnionType
75
68
 
76
69
 
77
- def is_optional(type: Type | Any | str):
70
+ def is_optional(type: type | Any | str):
78
71
  # Optional = Union[T, NoneType]
79
72
  return is_union(get_origin(type)) and NoneType in get_args(type)
80
73
 
81
74
 
82
- def __parse(value: any, clazz: Type, path: str, strict: bool, ignore_unexpected: bool):
75
+ def __parse(value: any, clazz: type, path: str, strict: bool, ignore_unexpected: bool):
83
76
  if is_dataclass(clazz):
84
77
  if not isinstance(value, ConfigTree):
85
78
  raise TypeConfigException(
@@ -104,7 +97,7 @@ def __parse(value: any, clazz: Type, path: str, strict: bool, ignore_unexpected:
104
97
  # if val is a dataclass, convert to ConfigTree
105
98
  val = ConfigFactory.from_dict(asdict(val))
106
99
 
107
- if not isinstance(val, _MISSING_TYPE):
100
+ if val is not MISSING:
108
101
  fs[f.name] = __parse(
109
102
  val, f.type, f"{path}.{f.name}", strict, ignore_unexpected
110
103
  )
@@ -115,8 +108,8 @@ def __parse(value: any, clazz: Type, path: str, strict: bool, ignore_unexpected:
115
108
 
116
109
  elif is_dataclass(f.type):
117
110
  if all(
118
- not isinstance(field.default, _MISSING_TYPE)
119
- or not isinstance(field.default_factory, _MISSING_TYPE)
111
+ field.default is not MISSING
112
+ or field.default_factory is not MISSING
120
113
  or is_optional(field.type)
121
114
  for field in fields(f.type)
122
115
  ):
@@ -137,7 +130,7 @@ def __parse(value: any, clazz: Type, path: str, strict: bool, ignore_unexpected:
137
130
  f'expected type {clazz} at {path_to_str(path)}, no field "{f.name}" found'
138
131
  )
139
132
 
140
- unexpected_keys = value.keys() - {renamings.get(k, k) for k in fs.keys()}
133
+ unexpected_keys = value.keys() - {renamings.get(k, k) for k in fs}
141
134
  if len(unexpected_keys) > 0 and not ignore_unexpected:
142
135
  raise UnexpectedKeysException(
143
136
  f'unexpected key(s) "{", ".join(unexpected_keys)}" detected for type {clazz} at {path_to_str(path)}'
@@ -148,30 +141,30 @@ def __parse(value: any, clazz: Type, path: str, strict: bool, ignore_unexpected:
148
141
  origin = get_origin(clazz)
149
142
  args = get_args(clazz)
150
143
 
151
- if origin is list:
144
+ if clazz is list or origin is list:
145
+ if len(args) != 1:
146
+ raise MissingTypeException("expected list with type information: List[?]")
147
+
152
148
  if value is None:
153
149
  raise MalformedConfigException(
154
150
  f"expected list at {path_to_str(path)} but received None"
155
151
  )
156
152
 
157
- if len(args) != 1:
158
- raise MissingTypeException("expected list with type information: List[?]")
159
-
160
153
  parse_candidate = args[0]
161
154
  return [
162
155
  __parse(v, parse_candidate, f"{path}[]", strict, ignore_unexpected)
163
156
  for v in value
164
157
  ]
165
158
 
166
- if origin is tuple:
159
+ if clazz is tuple or origin is tuple:
160
+ if len(args) < 1:
161
+ raise MissingTypeException("expected tuple with type information: Tuple[?]")
162
+
167
163
  if value is None:
168
164
  raise MalformedConfigException(
169
165
  f"expected tuple at {path_to_str(path)} but received None"
170
166
  )
171
167
 
172
- if len(args) < 1:
173
- raise MissingTypeException("expected tuple with type information: Tuple[?]")
174
-
175
168
  has_ellipsis = args[-1] == Ellipsis
176
169
  if has_ellipsis and len(args) != 2:
177
170
  raise MissingTypeException(
@@ -187,7 +180,7 @@ def __parse(value: any, clazz: Type, path: str, strict: bool, ignore_unexpected:
187
180
  for v, arg in zip(value, _args)
188
181
  )
189
182
 
190
- if origin is dict:
183
+ if clazz is dict or origin is dict:
191
184
  if len(args) != 2:
192
185
  raise MissingTypeException(
193
186
  "expected dict with type information: Dict[?, ?]"
@@ -246,9 +239,7 @@ def __parse(value: any, clazz: Type, path: str, strict: bool, ignore_unexpected:
246
239
  value = float(value)
247
240
  except ValueError:
248
241
  pass
249
- return __parse_type(
250
- value, clazz, path, isinstance(value, float) or isinstance(value, int)
251
- )
242
+ return __parse_type(value, clazz, path, isinstance(value, (float, int)))
252
243
 
253
244
  if clazz is str:
254
245
  return __parse_type(value, clazz, path, isinstance(value, str))
@@ -332,7 +323,7 @@ def __parse(value: any, clazz: Type, path: str, strict: bool, ignore_unexpected:
332
323
  if len(child_successes) == 1:
333
324
  return child_successes[0][1]
334
325
  elif len(child_successes) > 1:
335
- matching_classes = "\n- ".join(map(lambda x: x[0].__name__, child_successes))
326
+ matching_classes = "\n- ".join(x[0].__name__ for x in child_successes)
336
327
  raise AmbiguousSubclassException(
337
328
  f"""multiple subtypes of {clazz} matched at {path_to_str(path)}, use '_type' to disambiguate:\n- {matching_classes}"""
338
329
  )
@@ -369,7 +360,7 @@ def __generate(value: object, path: str):
369
360
  return value
370
361
 
371
362
 
372
- def __env_vars_parse(prefix: str, obj: Dict[str, Any]):
363
+ def __env_vars_parse(prefix: str, obj: dict[str, Any]):
373
364
  ret = {}
374
365
 
375
366
  def set_lens(p, focus, v):
@@ -447,11 +438,11 @@ def path_to_str(path: str) -> str:
447
438
  return path if len(path) > 0 else "root"
448
439
 
449
440
 
450
- def __cli_parse(argv: List[str]):
441
+ def __cli_parse(argv: list[str]):
451
442
  kvs = {}
452
443
  i = 0
453
444
 
454
- if not argv[0].startswith("--"):
445
+ if argv and not argv[0].startswith("--"):
455
446
  i += 1
456
447
 
457
448
  while i < len(argv):
@@ -0,0 +1,5 @@
1
+ import sys
2
+
3
+ __version__ = "3.7.1"
4
+
5
+ PY311up = sys.version_info >= (3, 11)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "dataconf"
3
- version = "3.7.0"
3
+ version = "3.7.1"
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"
@@ -12,10 +12,12 @@ include = [
12
12
  classifiers = [
13
13
  "Topic :: Software Development :: Libraries :: Python Modules",
14
14
  "Programming Language :: Python",
15
- "Programming Language :: Python :: 3.9",
16
15
  "Programming Language :: Python :: 3.10",
17
16
  "Programming Language :: Python :: 3.11",
18
- "Programming Language :: Python :: 3.12"
17
+ "Programming Language :: Python :: 3.12",
18
+ "Programming Language :: Python :: 3.13",
19
+ "Programming Language :: Python :: 3.14",
20
+ "Programming Language :: Python :: 3.15",
19
21
  ]
20
22
  keywords = [
21
23
  "configuration",
@@ -32,8 +34,8 @@ keywords = [
32
34
  dataconf = 'dataconf.cli:run'
33
35
 
34
36
  [tool.poetry.dependencies]
35
- python = "^3.9"
36
- python-dateutil = "^2.9.0.post0"
37
+ python = "^3.10"
38
+ python-dateutil = "^3.0.0"
37
39
  PyYAML = "^6.0.2"
38
40
  isodate = "^0.7.2"
39
41
  pyhocon = "^0.3.61"
@@ -48,7 +50,7 @@ ruff = ">=0.12.7"
48
50
 
49
51
  [tool.commitizen]
50
52
  name = "cz_conventional_commits"
51
- version = "3.7.0"
53
+ version = "3.7.1"
52
54
  tag_format = "v$major.$minor.$patch$prerelease"
53
55
  version_files = [
54
56
  "pyproject.toml:version",
@@ -1,6 +0,0 @@
1
- import sys
2
-
3
- __version__ = "3.7.0"
4
-
5
- PY310up = sys.version_info >= (3, 10)
6
- PY311up = sys.version_info >= (3, 11)
File without changes
@@ -1,33 +1,33 @@
1
+ from dataconf.main import HOCON
2
+ from dataconf.main import YAML
1
3
  from dataconf.main import cli
2
4
  from dataconf.main import dict
3
5
  from dataconf.main import dump
4
6
  from dataconf.main import dumps
5
7
  from dataconf.main import env
6
8
  from dataconf.main import file
7
- from dataconf.main import HOCON
8
9
  from dataconf.main import load
9
10
  from dataconf.main import loads
10
11
  from dataconf.main import multi
11
12
  from dataconf.main import parse
12
13
  from dataconf.main import string
13
14
  from dataconf.main import url
14
- from dataconf.main import YAML
15
15
  from dataconf.version import __version__
16
16
 
17
17
  __all__ = [
18
- "load",
19
- "loads",
18
+ "HOCON",
19
+ "YAML",
20
+ "__version__",
21
+ "cli",
22
+ "dict",
20
23
  "dump",
21
24
  "dumps",
22
25
  "env",
23
- "url",
24
- "dict",
25
26
  "file",
26
- "string",
27
- "cli",
27
+ "load",
28
+ "loads",
28
29
  "multi",
29
30
  "parse",
30
- "YAML",
31
- "HOCON",
32
- "__version__",
31
+ "string",
32
+ "url",
33
33
  ]
File without changes
File without changes