configaroo 0.2.3__tar.gz → 0.3.0__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.
Files changed (25) hide show
  1. {configaroo-0.2.3/src/configaroo.egg-info → configaroo-0.3.0}/PKG-INFO +2 -2
  2. {configaroo-0.2.3 → configaroo-0.3.0}/pyproject.toml +6 -7
  3. {configaroo-0.2.3 → configaroo-0.3.0}/src/configaroo/__init__.py +1 -1
  4. {configaroo-0.2.3 → configaroo-0.3.0}/src/configaroo/configuration.py +21 -7
  5. {configaroo-0.2.3 → configaroo-0.3.0/src/configaroo.egg-info}/PKG-INFO +2 -2
  6. {configaroo-0.2.3 → configaroo-0.3.0}/tests/test_configuration.py +7 -1
  7. {configaroo-0.2.3 → configaroo-0.3.0}/tests/test_environment.py +15 -1
  8. {configaroo-0.2.3 → configaroo-0.3.0}/LICENSE +0 -0
  9. {configaroo-0.2.3 → configaroo-0.3.0}/README.md +0 -0
  10. {configaroo-0.2.3 → configaroo-0.3.0}/setup.cfg +0 -0
  11. {configaroo-0.2.3 → configaroo-0.3.0}/src/configaroo/exceptions.py +0 -0
  12. {configaroo-0.2.3 → configaroo-0.3.0}/src/configaroo/loaders/__init__.py +0 -0
  13. {configaroo-0.2.3 → configaroo-0.3.0}/src/configaroo/loaders/json.py +0 -0
  14. {configaroo-0.2.3 → configaroo-0.3.0}/src/configaroo/loaders/toml.py +0 -0
  15. {configaroo-0.2.3 → configaroo-0.3.0}/src/configaroo/py.typed +0 -0
  16. {configaroo-0.2.3 → configaroo-0.3.0}/src/configaroo.egg-info/SOURCES.txt +0 -0
  17. {configaroo-0.2.3 → configaroo-0.3.0}/src/configaroo.egg-info/dependency_links.txt +0 -0
  18. {configaroo-0.2.3 → configaroo-0.3.0}/src/configaroo.egg-info/requires.txt +0 -0
  19. {configaroo-0.2.3 → configaroo-0.3.0}/src/configaroo.egg-info/top_level.txt +0 -0
  20. {configaroo-0.2.3 → configaroo-0.3.0}/tests/test_dynamic.py +0 -0
  21. {configaroo-0.2.3 → configaroo-0.3.0}/tests/test_json.py +0 -0
  22. {configaroo-0.2.3 → configaroo-0.3.0}/tests/test_loaders.py +0 -0
  23. {configaroo-0.2.3 → configaroo-0.3.0}/tests/test_print.py +0 -0
  24. {configaroo-0.2.3 → configaroo-0.3.0}/tests/test_toml.py +0 -0
  25. {configaroo-0.2.3 → configaroo-0.3.0}/tests/test_validation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: configaroo
3
- Version: 0.2.3
3
+ Version: 0.3.0
4
4
  Summary: Bouncy handling of configuration files
5
5
  Author-email: Geir Arne Hjelle <geirarne@gmail.com>
6
6
  Maintainer-email: Geir Arne Hjelle <geirarne@gmail.com>
@@ -8,7 +8,7 @@ License-Expression: MIT
8
8
  Project-URL: homepage, https://github.com/gahjelle/configaroo
9
9
  Project-URL: github, https://github.com/gahjelle/configaroo
10
10
  Project-URL: issues, https://github.com/gahjelle/configaroo/issues
11
- Project-URL: changelog, https://github.com/gahjelle/configaroo/blob/main/CHANGELOG.md
11
+ Project-URL: changelog, https://github.com/gahjelle/configaroo/releases
12
12
  Keywords: configuration,configuration-management,toml,json
13
13
  Classifier: Development Status :: 5 - Production/Stable
14
14
  Classifier: Intended Audience :: Developers
@@ -32,20 +32,19 @@ dynamic = ["version"]
32
32
  homepage = "https://github.com/gahjelle/configaroo"
33
33
  github = "https://github.com/gahjelle/configaroo"
34
34
  issues = "https://github.com/gahjelle/configaroo/issues"
35
- changelog = "https://github.com/gahjelle/configaroo/blob/main/CHANGELOG.md"
35
+ changelog = "https://github.com/gahjelle/configaroo/releases"
36
36
 
37
37
  [dependency-groups]
38
38
  build = ["build>=1.2.2.post1", "twine>=6.1.0"]
39
+ ci = ["mypy>=1.17.1", "pytest>=8.4.1", "rich>=14.1.0", "ruff>=0.12.7", "tomli-w>=1.2.0"]
39
40
  dev = [
41
+ { include-group = "ci" },
42
+ { include-group = "test" },
40
43
  "bumpver>=2024.1130",
41
44
  "ipython>=8.36.0",
42
- "mypy>=1.17.1",
43
45
  "pre-commit>=4.2.0",
44
- "pytest>=8.3.5",
45
- "rich>=14.1.0",
46
- "ruff>=0.11.11",
47
- "tomli-w>=1.2.0",
48
46
  ]
47
+ test = ["pytest>=8.4.1", "pytest-cov>=6.2.1", "tomli-w>=1.2.0"]
49
48
 
50
49
 
51
50
  [tool.setuptools.dynamic]
@@ -80,7 +79,7 @@ python_version = "3.11"
80
79
  strict = true
81
80
 
82
81
  [tool.bumpver]
83
- current_version = "v0.2.3"
82
+ current_version = "v0.3.0"
84
83
  version_pattern = "vMAJOR.MINOR.PATCH"
85
84
  commit_message = "bump version {old_version} -> {new_version}"
86
85
  tag_message = "{new_version}"
@@ -15,4 +15,4 @@ __all__ = [
15
15
  "print_configuration",
16
16
  ]
17
17
 
18
- __version__ = "0.2.3"
18
+ __version__ = "0.3.0"
@@ -112,8 +112,22 @@ class Configuration(UserDict[str, Any]):
112
112
  cls = type(self)
113
113
  return self | {prefix: cls(self.setdefault(prefix, {})).add(rest, value)}
114
114
 
115
- def add_envs(self, envs: dict[str, str], prefix: str = "") -> Self:
116
- """Add environment variables to configuration."""
115
+ def add_envs(self, envs: dict[str, str] | None = None, prefix: str = "") -> Self:
116
+ """Add environment variables to configuration.
117
+
118
+ If you don't specify which environment variables to read, you'll
119
+ automatically add any that matches a top-level value of the
120
+ configuration.
121
+ """
122
+ if envs is None:
123
+ # Automatically add top-level configuration items
124
+ envs = {
125
+ re.sub(r"\W", "_", key).upper(): key
126
+ for key, value in self.data.items()
127
+ if isinstance(value, str | int | float)
128
+ }
129
+
130
+ # Read environment variables
117
131
  for env, key in envs.items():
118
132
  env_key = f"{prefix}{env}"
119
133
  if env_value := os.getenv(env_key):
@@ -129,7 +143,7 @@ class Configuration(UserDict[str, Any]):
129
143
  cls = type(self)
130
144
  variables = (
131
145
  (self.to_flat_dict() if _include_self else {})
132
- | {"project_path": _find_pyproject_toml()}
146
+ | {"project_path": find_pyproject_toml()}
133
147
  | ({} if extra is None else extra)
134
148
  )
135
149
  parsed = cls(
@@ -196,7 +210,7 @@ def print_configuration(config: Configuration | BaseModel, indent: int = 4) -> N
196
210
  )
197
211
 
198
212
 
199
- def _get_rich_print() -> Callable[[str], None]:
213
+ def _get_rich_print() -> Callable[[str], None]: # pragma: no cover
200
214
  """Initialize a Rich console if Rich is installed, otherwise use built-in print."""
201
215
  try:
202
216
  from rich.console import Console # noqa: PLC0415
@@ -222,13 +236,13 @@ def _print_dict_as_tree(
222
236
  value,
223
237
  indent=indent,
224
238
  current_indent=current_indent + indent,
225
- _print=print,
239
+ _print=_print,
226
240
  )
227
241
  else:
228
242
  _print(" " * current_indent + f"- {key}: {value!r}")
229
243
 
230
244
 
231
- def _find_pyproject_toml(
245
+ def find_pyproject_toml(
232
246
  path: Path | None = None, _file_name: str = "pyproject.toml"
233
247
  ) -> Path:
234
248
  """Find a directory that contains a pyproject.toml file.
@@ -241,7 +255,7 @@ def _find_pyproject_toml(
241
255
  if (path / _file_name).exists() or path == path.parent:
242
256
  return path.resolve()
243
257
 
244
- return _find_pyproject_toml(path.parent, _file_name=_file_name)
258
+ return find_pyproject_toml(path.parent, _file_name=_file_name)
245
259
 
246
260
 
247
261
  def _get_foreign_path() -> Path:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: configaroo
3
- Version: 0.2.3
3
+ Version: 0.3.0
4
4
  Summary: Bouncy handling of configuration files
5
5
  Author-email: Geir Arne Hjelle <geirarne@gmail.com>
6
6
  Maintainer-email: Geir Arne Hjelle <geirarne@gmail.com>
@@ -8,7 +8,7 @@ License-Expression: MIT
8
8
  Project-URL: homepage, https://github.com/gahjelle/configaroo
9
9
  Project-URL: github, https://github.com/gahjelle/configaroo
10
10
  Project-URL: issues, https://github.com/gahjelle/configaroo/issues
11
- Project-URL: changelog, https://github.com/gahjelle/configaroo/blob/main/CHANGELOG.md
11
+ Project-URL: changelog, https://github.com/gahjelle/configaroo/releases
12
12
  Keywords: configuration,configuration-management,toml,json
13
13
  Classifier: Development Status :: 5 - Production/Stable
14
14
  Classifier: Intended Audience :: Developers
@@ -64,6 +64,12 @@ def test_get_nested_values(config: Configuration) -> None:
64
64
  assert config.get("with_dot.org.num") == 1234
65
65
 
66
66
 
67
+ def test_get_with_default(config: Configuration) -> None:
68
+ """Test that .get() falls back on default if the key doesn't exist."""
69
+ assert config.get("word", default="kangaroo") == "platypus"
70
+ assert config.get("another word", default="kangaroo") == "kangaroo"
71
+
72
+
67
73
  def test_update_preserves_type(config: Configuration) -> None:
68
74
  """Test that an update operation gives a Configuration."""
69
75
  assert isinstance(config | {"new": 1}, Configuration)
@@ -122,7 +128,7 @@ def test_contains_with_dotted_key(config: Configuration) -> None:
122
128
 
123
129
  def test_find_pyproject_toml() -> None:
124
130
  """Test that the pyproject.toml file can be located."""
125
- assert configuration._find_pyproject_toml() == Path(__file__).parent.parent
131
+ assert configuration.find_pyproject_toml() == Path(__file__).parent.parent
126
132
 
127
133
 
128
134
  def test_find_foreign_caller() -> None:
@@ -45,7 +45,7 @@ def test_missing_env_ok_if_optional(config: Configuration) -> None:
45
45
 
46
46
 
47
47
  def test_env_prefix(config: Configuration, monkeypatch: pytest.MonkeyPatch) -> None:
48
- """Test that a common prefix can be used for environment_variables."""
48
+ """Test that a common prefix can be used for environment variables."""
49
49
  monkeypatch.setenv("EXAMPLE_NUMBER", "14")
50
50
  monkeypatch.setenv("EXAMPLE_WORD", "platypus")
51
51
  config_w_env = config.add_envs(
@@ -53,3 +53,17 @@ def test_env_prefix(config: Configuration, monkeypatch: pytest.MonkeyPatch) -> N
53
53
  )
54
54
  assert config_w_env.number == "14"
55
55
  assert config_w_env.nested.word == "platypus"
56
+
57
+
58
+ def test_env_automatic(config: Configuration, monkeypatch: pytest.MonkeyPatch) -> None:
59
+ """Test that top-level keys can be automatically filled by env variables."""
60
+ monkeypatch.setenv("NUMBER", "28")
61
+ monkeypatch.setenv("WORD", "kangaroo")
62
+ monkeypatch.setenv("A_B_D_KEY_", "works")
63
+ monkeypatch.setenv("NESTED", "should not be replaced")
64
+ config_w_env = (config | {"A b@d-key!": ""}).add_envs()
65
+
66
+ assert config_w_env.number == "28"
67
+ assert config_w_env.word == "kangaroo"
68
+ assert config_w_env["A b@d-key!"] == "works"
69
+ assert config_w_env.nested != "should not be replaced"
File without changes
File without changes
File without changes