config-as-json 0.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.
Files changed (29) hide show
  1. config_as_json-0.1/LICENSE.txt +22 -0
  2. config_as_json-0.1/PKG-INFO +98 -0
  3. config_as_json-0.1/README.md +139 -0
  4. config_as_json-0.1/README_pypi.md +77 -0
  5. config_as_json-0.1/config_as_json.egg-info/PKG-INFO +98 -0
  6. config_as_json-0.1/config_as_json.egg-info/SOURCES.txt +27 -0
  7. config_as_json-0.1/config_as_json.egg-info/dependency_links.txt +1 -0
  8. config_as_json-0.1/config_as_json.egg-info/requires.txt +3 -0
  9. config_as_json-0.1/config_as_json.egg-info/top_level.txt +1 -0
  10. config_as_json-0.1/pyproject.toml +24 -0
  11. config_as_json-0.1/setup.cfg +4 -0
  12. config_as_json-0.1/setup.py +21 -0
  13. config_as_json-0.1/src/config_as_json/__init__.py +74 -0
  14. config_as_json-0.1/src/config_as_json/assert_dict_equal.py +82 -0
  15. config_as_json-0.1/src/config_as_json/commontypes.py +21 -0
  16. config_as_json-0.1/src/config_as_json/config.py +959 -0
  17. config_as_json-0.1/src/config_as_json/config_auto_change_hook.py +73 -0
  18. config_as_json-0.1/src/config_as_json/config_factory.py +201 -0
  19. config_as_json-0.1/src/config_as_json/dict_validators.py +330 -0
  20. config_as_json-0.1/src/config_as_json/discriminated_dict_validators.py +292 -0
  21. config_as_json-0.1/src/config_as_json/file_extension.py +42 -0
  22. config_as_json-0.1/src/config_as_json/file_must_exist.py +37 -0
  23. config_as_json-0.1/src/config_as_json/list_validators.py +782 -0
  24. config_as_json-0.1/src/config_as_json/migrate_cfg.py +55 -0
  25. config_as_json-0.1/src/config_as_json/migrate_cfg_warn_hook.py +43 -0
  26. config_as_json-0.1/src/config_as_json/projected_validators.py +162 -0
  27. config_as_json-0.1/src/config_as_json/py.typed +0 -0
  28. config_as_json-0.1/src/config_as_json/str_to_enum.py +50 -0
  29. config_as_json-0.1/src/config_as_json/validator.py +565 -0
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Tom Björkholm
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
@@ -0,0 +1,98 @@
1
+ Metadata-Version: 2.4
2
+ Name: config-as-json
3
+ Version: 0.1
4
+ Summary: Read, write, validate, and migrate JSON-backed config classes.
5
+ Author: Tom Björkholm
6
+ Author-email: Tom Björkholm <klausuler_linnet0q@icloud.com>
7
+ License-Expression: MIT
8
+ Project-URL: Source code, https://bitbucket.org/tom-bjorkholm/config_as_json/
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.12
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE.txt
14
+ Requires-Dist: setuptools>=82.0.1
15
+ Requires-Dist: build>=1.4.2
16
+ Requires-Dist: wheel>=0.46.3
17
+ Dynamic: author
18
+ Dynamic: license-file
19
+ Dynamic: requires-dist
20
+ Dynamic: requires-python
21
+
22
+ # config-as-json
23
+
24
+ `config-as-json` helps an application keep its configuration schema in a
25
+ Python class while storing actual configuration data in JSON files.
26
+
27
+ The intended usage model is:
28
+
29
+ - Derive an application-specific class from `config_as_json.Config`.
30
+ - Add one instance attribute per supported configuration parameter. An
31
+ instance attribute can also be a dict or list, optionally with nested
32
+ dicts and lists.
33
+ - Let the values assigned in the derived constructor act as the default
34
+ configuration.
35
+ - Use the library to write those defaults as JSON and to read JSON back into
36
+ the derived configuration object.
37
+
38
+ The library is designed to support evolving configuration formats by letting
39
+ applications define:
40
+
41
+ - custom parsers for values that should become richer Python types
42
+ - optional keys that receive default values when omitted
43
+ - backward-compatible key renames for older configuration files
44
+ - hooks that can warn or report when automatic compatibility changes were
45
+ needed
46
+
47
+ ## Installation
48
+
49
+ `config-as-json` requires Python 3.12 or newer.
50
+
51
+ ```sh
52
+ pip install --upgrade config-as-json
53
+ ```
54
+
55
+ ## Main entry points
56
+
57
+ - `config_as_json.Config`
58
+ Base class for JSON-backed configuration objects.
59
+ - `config_as_json.config_factory_from_json`
60
+ Select the correct configuration class by inspecting JSON input.
61
+ - `config_as_json.ConfigAutoChangeHook`
62
+ Receive notifications about automatic changes during parsing.
63
+ - `config_as_json.MigrateCfgWarnHook`
64
+ Warn when backward compatibility was used.
65
+ - `config_as_json.migrate_cfg`
66
+ Read an older configuration file and write it back in the newest supported
67
+ format.
68
+
69
+ The generated API reference also shows the implementation modules where these
70
+ public objects are defined.
71
+
72
+ ## Documentation and examples
73
+
74
+ - Example directory: [example/src/example/README.md](https://bitbucket.org/tom-bjorkholm/config_as_json/src/master/example/src/example/README.md)
75
+ - Public API notes: [doc/api.md](https://bitbucket.org/tom-bjorkholm/config_as_json/src/master/doc/api.md)
76
+ - Protected/internal API notes: [doc/protected_api.md](https://bitbucket.org/tom-bjorkholm/config_as_json/src/master/doc/protected_api.md)
77
+ - Source repository: [config_as_json](https://bitbucket.org/tom-bjorkholm/config_as_json/)
78
+
79
+ The example directory contains worked examples for new users. It is not
80
+ included in the package installed from PyPI.
81
+
82
+ ## Project status
83
+
84
+ This package originated as configuration code from a larger application and
85
+ has been refactored into a stand-alone reusable library. The public API
86
+ reference and worked examples are maintained in the source repository.
87
+
88
+ ## License
89
+
90
+ MIT
91
+
92
+ ## Test summary
93
+
94
+ - Test result: 3397 passed in 9s
95
+ - No flake8 warnings.
96
+ - No mypy errors found.
97
+ - Built version(s): 0.1
98
+ - Build and test using Python 3.14.3
@@ -0,0 +1,139 @@
1
+ # config-as-json
2
+
3
+ > Looking for installation and user-facing package information?
4
+ > See [README_pypi.md](README_pypi.md) or the
5
+ > [PyPI project page](https://pypi.org/project/config-as-json).
6
+
7
+ ## Repository purpose
8
+
9
+ `config-as-json` is a reusable library that grew out of configuration code
10
+ from an application.
11
+
12
+ The intended library model is:
13
+
14
+ - An application derives its own configuration class from
15
+ `config_as_json.Config`.
16
+ - The derived class creates one instance attribute per supported
17
+ configuration parameter. Supported configuration parameters can also
18
+ be dicts and lists.
19
+ - The values assigned in the derived class constructor are the default
20
+ configuration values.
21
+ - The library writes those values as JSON, reads JSON back into the object,
22
+ and helps users with clear diagnostics when configuration data is missing,
23
+ misspelled, outdated, or of the wrong type.
24
+
25
+ The source docstrings are used to generate the API reference in `doc/`, and
26
+ the examples in `example/src/example/` are the main worked documentation for
27
+ new users. User-facing documentation names public classes and functions as
28
+ exports from `config_as_json`; the generated API reference may also show the
29
+ implementation modules where those objects are defined.
30
+
31
+ ## Product vision and boundaries
32
+
33
+ This package is about representing an application's configuration as a Python
34
+ object with an explicit schema and a JSON representation.
35
+
36
+ The library is intended to:
37
+
38
+ - Keep the application's configuration schema visible in normal Python code.
39
+ - Use the derived class itself as the source of default values.
40
+ - Read and write human-editable JSON configuration files.
41
+ - Support evolution of the configuration format through optional keys,
42
+ converters, and backward-compatible key renames.
43
+ - Help end users with actionable error messages instead of silent fallback.
44
+
45
+ The library is not intended to invent configuration structure dynamically or
46
+ to hide the application's schema behind a large generic framework.
47
+
48
+
49
+ ## Main building blocks
50
+
51
+ - `config_as_json.Config`
52
+ The base class for derived configuration classes.
53
+ - `config_as_json.config_factory_from_json`
54
+ Select among several configuration classes by inspecting JSON input.
55
+ - `config_as_json.ConfigAutoChangeHook`
56
+ Hook interface for reporting automatic changes applied during parsing.
57
+ - `config_as_json.migrate_cfg`
58
+ Helper for reading an older configuration file and writing it back in the
59
+ newest supported format.
60
+ - `config_as_json.MigrateCfgWarnHook`
61
+ Standard hook that warns users when backward compatibility was needed.
62
+ - Utility modules such as `str_to_enum`, `file_extension`,
63
+ `file_must_exist`, `commontypes`, and `assert_dict_equal`
64
+ These are part of the current public surface and are documented as such.
65
+
66
+ ## Related documentation
67
+
68
+ - User-facing package overview: [README_pypi.md](README_pypi.md)
69
+ - Example directory: [example/src/example/README.md](example/src/example/README.md)
70
+ - Public API notes: [doc/api.md](doc/api.md)
71
+ - Protected/internal API notes: [doc/protected_api.md](doc/protected_api.md)
72
+ - Build system design: [common_build_tools/README.md](common_build_tools/README.md)
73
+
74
+ The example directory contains worked examples for new users and is also
75
+ useful for maintainers who want to see intended API usage in context.
76
+
77
+ ## Cloning
78
+
79
+ This repository uses submodules. Clone it with:
80
+
81
+ ```sh
82
+ git clone --recurse-submodules \
83
+ git@bitbucket.org:tom-bjorkholm/config_as_json.git
84
+ ```
85
+
86
+ If you already cloned without submodules, initialize them with:
87
+
88
+ ```sh
89
+ git submodule update --init --recursive
90
+ ```
91
+
92
+ To update the checked-out submodule revisions:
93
+
94
+ ```sh
95
+ git submodule update --remote --merge
96
+ ```
97
+
98
+ ## Supported Python versions
99
+
100
+ - Package runtime baseline: Python 3.12 or newer
101
+ - Maintainer validation target: Python 3.12, 3.13, and 3.14
102
+ - Main day-to-day development: usually the newest supported Python version
103
+
104
+ ## Development workflow
105
+
106
+ On macOS and Linux, the normal workflow is:
107
+
108
+ 1. Run `./run_setup_build_environment.py` once after cloning or when the
109
+ build environment needs to be recreated.
110
+ 2. Run `./run_build.py` for the normal build-and-test cycle.
111
+ 3. Run `./run_clean_build.py` before review or release work that needs a
112
+ completely fresh build.
113
+
114
+ The helper scripts are:
115
+
116
+ - `run_setup_build_environment.py`
117
+ Create or refresh the build environment.
118
+ - `run_build.py`
119
+ Build the package and run the configured checks in the project virtual
120
+ environment.
121
+ - `run_clean.py`
122
+ Remove files generated by the build system.
123
+ - `run_clean_build.py`
124
+ Perform a clean build from scratch. This is especially useful because some
125
+ duplicate-code diagnostics only appear on a clean build.
126
+ - `run_pypi_build.py`
127
+ Create the distribution artifacts intended for PyPI publishing.
128
+
129
+ The standard verification suite includes pytest, pylint, flake8, and mypy.
130
+ After a build, the generated reports can be browsed through
131
+ `reports/index.html`.
132
+
133
+ ## Test summary
134
+
135
+ - Test result: 3397 passed in 9s
136
+ - No flake8 warnings.
137
+ - No mypy errors found.
138
+ - Built version(s): 0.1
139
+ - Build and test using Python 3.14.3
@@ -0,0 +1,77 @@
1
+ # config-as-json
2
+
3
+ `config-as-json` helps an application keep its configuration schema in a
4
+ Python class while storing actual configuration data in JSON files.
5
+
6
+ The intended usage model is:
7
+
8
+ - Derive an application-specific class from `config_as_json.Config`.
9
+ - Add one instance attribute per supported configuration parameter. An
10
+ instance attribute can also be a dict or list, optionally with nested
11
+ dicts and lists.
12
+ - Let the values assigned in the derived constructor act as the default
13
+ configuration.
14
+ - Use the library to write those defaults as JSON and to read JSON back into
15
+ the derived configuration object.
16
+
17
+ The library is designed to support evolving configuration formats by letting
18
+ applications define:
19
+
20
+ - custom parsers for values that should become richer Python types
21
+ - optional keys that receive default values when omitted
22
+ - backward-compatible key renames for older configuration files
23
+ - hooks that can warn or report when automatic compatibility changes were
24
+ needed
25
+
26
+ ## Installation
27
+
28
+ `config-as-json` requires Python 3.12 or newer.
29
+
30
+ ```sh
31
+ pip install --upgrade config-as-json
32
+ ```
33
+
34
+ ## Main entry points
35
+
36
+ - `config_as_json.Config`
37
+ Base class for JSON-backed configuration objects.
38
+ - `config_as_json.config_factory_from_json`
39
+ Select the correct configuration class by inspecting JSON input.
40
+ - `config_as_json.ConfigAutoChangeHook`
41
+ Receive notifications about automatic changes during parsing.
42
+ - `config_as_json.MigrateCfgWarnHook`
43
+ Warn when backward compatibility was used.
44
+ - `config_as_json.migrate_cfg`
45
+ Read an older configuration file and write it back in the newest supported
46
+ format.
47
+
48
+ The generated API reference also shows the implementation modules where these
49
+ public objects are defined.
50
+
51
+ ## Documentation and examples
52
+
53
+ - Example directory: [example/src/example/README.md](https://bitbucket.org/tom-bjorkholm/config_as_json/src/master/example/src/example/README.md)
54
+ - Public API notes: [doc/api.md](https://bitbucket.org/tom-bjorkholm/config_as_json/src/master/doc/api.md)
55
+ - Protected/internal API notes: [doc/protected_api.md](https://bitbucket.org/tom-bjorkholm/config_as_json/src/master/doc/protected_api.md)
56
+ - Source repository: [config_as_json](https://bitbucket.org/tom-bjorkholm/config_as_json/)
57
+
58
+ The example directory contains worked examples for new users. It is not
59
+ included in the package installed from PyPI.
60
+
61
+ ## Project status
62
+
63
+ This package originated as configuration code from a larger application and
64
+ has been refactored into a stand-alone reusable library. The public API
65
+ reference and worked examples are maintained in the source repository.
66
+
67
+ ## License
68
+
69
+ MIT
70
+
71
+ ## Test summary
72
+
73
+ - Test result: 3397 passed in 9s
74
+ - No flake8 warnings.
75
+ - No mypy errors found.
76
+ - Built version(s): 0.1
77
+ - Build and test using Python 3.14.3
@@ -0,0 +1,98 @@
1
+ Metadata-Version: 2.4
2
+ Name: config-as-json
3
+ Version: 0.1
4
+ Summary: Read, write, validate, and migrate JSON-backed config classes.
5
+ Author: Tom Björkholm
6
+ Author-email: Tom Björkholm <klausuler_linnet0q@icloud.com>
7
+ License-Expression: MIT
8
+ Project-URL: Source code, https://bitbucket.org/tom-bjorkholm/config_as_json/
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.12
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE.txt
14
+ Requires-Dist: setuptools>=82.0.1
15
+ Requires-Dist: build>=1.4.2
16
+ Requires-Dist: wheel>=0.46.3
17
+ Dynamic: author
18
+ Dynamic: license-file
19
+ Dynamic: requires-dist
20
+ Dynamic: requires-python
21
+
22
+ # config-as-json
23
+
24
+ `config-as-json` helps an application keep its configuration schema in a
25
+ Python class while storing actual configuration data in JSON files.
26
+
27
+ The intended usage model is:
28
+
29
+ - Derive an application-specific class from `config_as_json.Config`.
30
+ - Add one instance attribute per supported configuration parameter. An
31
+ instance attribute can also be a dict or list, optionally with nested
32
+ dicts and lists.
33
+ - Let the values assigned in the derived constructor act as the default
34
+ configuration.
35
+ - Use the library to write those defaults as JSON and to read JSON back into
36
+ the derived configuration object.
37
+
38
+ The library is designed to support evolving configuration formats by letting
39
+ applications define:
40
+
41
+ - custom parsers for values that should become richer Python types
42
+ - optional keys that receive default values when omitted
43
+ - backward-compatible key renames for older configuration files
44
+ - hooks that can warn or report when automatic compatibility changes were
45
+ needed
46
+
47
+ ## Installation
48
+
49
+ `config-as-json` requires Python 3.12 or newer.
50
+
51
+ ```sh
52
+ pip install --upgrade config-as-json
53
+ ```
54
+
55
+ ## Main entry points
56
+
57
+ - `config_as_json.Config`
58
+ Base class for JSON-backed configuration objects.
59
+ - `config_as_json.config_factory_from_json`
60
+ Select the correct configuration class by inspecting JSON input.
61
+ - `config_as_json.ConfigAutoChangeHook`
62
+ Receive notifications about automatic changes during parsing.
63
+ - `config_as_json.MigrateCfgWarnHook`
64
+ Warn when backward compatibility was used.
65
+ - `config_as_json.migrate_cfg`
66
+ Read an older configuration file and write it back in the newest supported
67
+ format.
68
+
69
+ The generated API reference also shows the implementation modules where these
70
+ public objects are defined.
71
+
72
+ ## Documentation and examples
73
+
74
+ - Example directory: [example/src/example/README.md](https://bitbucket.org/tom-bjorkholm/config_as_json/src/master/example/src/example/README.md)
75
+ - Public API notes: [doc/api.md](https://bitbucket.org/tom-bjorkholm/config_as_json/src/master/doc/api.md)
76
+ - Protected/internal API notes: [doc/protected_api.md](https://bitbucket.org/tom-bjorkholm/config_as_json/src/master/doc/protected_api.md)
77
+ - Source repository: [config_as_json](https://bitbucket.org/tom-bjorkholm/config_as_json/)
78
+
79
+ The example directory contains worked examples for new users. It is not
80
+ included in the package installed from PyPI.
81
+
82
+ ## Project status
83
+
84
+ This package originated as configuration code from a larger application and
85
+ has been refactored into a stand-alone reusable library. The public API
86
+ reference and worked examples are maintained in the source repository.
87
+
88
+ ## License
89
+
90
+ MIT
91
+
92
+ ## Test summary
93
+
94
+ - Test result: 3397 passed in 9s
95
+ - No flake8 warnings.
96
+ - No mypy errors found.
97
+ - Built version(s): 0.1
98
+ - Build and test using Python 3.14.3
@@ -0,0 +1,27 @@
1
+ LICENSE.txt
2
+ README.md
3
+ README_pypi.md
4
+ pyproject.toml
5
+ setup.py
6
+ config_as_json.egg-info/PKG-INFO
7
+ config_as_json.egg-info/SOURCES.txt
8
+ config_as_json.egg-info/dependency_links.txt
9
+ config_as_json.egg-info/requires.txt
10
+ config_as_json.egg-info/top_level.txt
11
+ src/config_as_json/__init__.py
12
+ src/config_as_json/assert_dict_equal.py
13
+ src/config_as_json/commontypes.py
14
+ src/config_as_json/config.py
15
+ src/config_as_json/config_auto_change_hook.py
16
+ src/config_as_json/config_factory.py
17
+ src/config_as_json/dict_validators.py
18
+ src/config_as_json/discriminated_dict_validators.py
19
+ src/config_as_json/file_extension.py
20
+ src/config_as_json/file_must_exist.py
21
+ src/config_as_json/list_validators.py
22
+ src/config_as_json/migrate_cfg.py
23
+ src/config_as_json/migrate_cfg_warn_hook.py
24
+ src/config_as_json/projected_validators.py
25
+ src/config_as_json/py.typed
26
+ src/config_as_json/str_to_enum.py
27
+ src/config_as_json/validator.py
@@ -0,0 +1,3 @@
1
+ setuptools>=82.0.1
2
+ build>=1.4.2
3
+ wheel>=0.46.3
@@ -0,0 +1 @@
1
+ config_as_json
@@ -0,0 +1,24 @@
1
+ [build-system]
2
+ requires = ["setuptools"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "config-as-json"
7
+ version = "0.1"
8
+ authors = [
9
+ { name="Tom Björkholm", email="klausuler_linnet0q@icloud.com" },
10
+ ]
11
+ description = "Read, write, validate, and migrate JSON-backed config classes."
12
+ readme = "README_pypi.md"
13
+ requires-python = ">=3.12"
14
+ license = "MIT"
15
+ license-files = ["LICENSE.txt"]
16
+ classifiers = [
17
+ "Programming Language :: Python :: 3",
18
+ "Operating System :: OS Independent"
19
+ ]
20
+ dynamic = ["dependencies"]
21
+
22
+ [project.urls]
23
+ "Source code" = "https://bitbucket.org/tom-bjorkholm/config_as_json/"
24
+
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,21 @@
1
+ #! /usr/local/bin/python3
2
+ """Setup file specifying build of .whl."""
3
+
4
+ from setuptools import setup # type: ignore[import-untyped]
5
+
6
+ setup(
7
+ name='config-as-json',
8
+ version='0.1',
9
+ description='Read, write, validate, and migrate JSON-backed config classes.',
10
+ author='Tom Björkholm',
11
+ author_email='klausuler_linnet0q@icloud.com',
12
+ python_requires='>=3.12',
13
+ packages=['config_as_json'],
14
+ package_dir={'config_as_json': 'src/config_as_json'},
15
+ package_data={'config_as_json': ['py.typed']},
16
+ install_requires=[
17
+ 'setuptools >= 82.0.1',
18
+ 'build >= 1.4.2',
19
+ 'wheel >= 0.46.3',
20
+ ]
21
+ )
@@ -0,0 +1,74 @@
1
+ #! /usr/local/bin/python3
2
+ """Define application configuration classes that serialize to JSON.
3
+
4
+ The package centers on :class:`config_as_json.config.Config`. Applications
5
+ derive their own configuration classes, declare supported settings as
6
+ instance attributes, and use the library to read, validate, migrate, and
7
+ write JSON configuration files.
8
+ """
9
+
10
+ # Copyright (c) 2026 Tom Björkholm
11
+ # MIT License
12
+
13
+
14
+ from config_as_json.config import Config, BackwardCompatible, ConfigBadJson, \
15
+ ParseConverter
16
+ from config_as_json.commontypes import JsonType, PathOrStr
17
+ from config_as_json.config_factory import config_factory_from_json, \
18
+ MatchConfig, MatchConfigSeq, JsonValueMatcher
19
+ from config_as_json.config_auto_change_hook import ConfigAutoChangeHook
20
+ from config_as_json.migrate_cfg_warn_hook import MigrateCfgWarnHook
21
+ from config_as_json.migrate_cfg import migrate_cfg
22
+ from config_as_json.validator import ValidationPlan, ValidationStep, \
23
+ WholeConfigValidationStep, MemberValidationStep, WholeConfigValidator, \
24
+ MemberValidator, StrValidator, IntFloatValidator, string_best_match, \
25
+ InvalidConfiguration, InvalidConfigurationValue
26
+ from config_as_json.list_validators import ListValueValidator, \
27
+ ListSizeValidator, ListIsOrderedValidator, ListOrderingValidator, \
28
+ ListForEachValidator
29
+ from config_as_json.dict_validators import DictKeysValidator, DictRule, \
30
+ DictForEachValidator
31
+ from config_as_json.discriminated_dict_validators import DictVariant, \
32
+ DiscriminatedDictValidator
33
+ from config_as_json.projected_validators import ProjectedMemberValidator
34
+ from config_as_json.str_to_enum import string_to_enum_best_match
35
+ from config_as_json.assert_dict_equal import assert_dict_equal
36
+
37
+
38
+ __all__ = ['Config',
39
+ 'BackwardCompatible',
40
+ 'ConfigBadJson',
41
+ 'JsonType',
42
+ 'PathOrStr',
43
+ 'ParseConverter',
44
+ 'ConfigAutoChangeHook',
45
+ 'MigrateCfgWarnHook',
46
+ 'migrate_cfg',
47
+ 'config_factory_from_json',
48
+ 'MatchConfig',
49
+ 'MatchConfigSeq',
50
+ 'JsonValueMatcher',
51
+ 'ValidationPlan',
52
+ 'ValidationStep',
53
+ 'WholeConfigValidationStep',
54
+ 'MemberValidationStep',
55
+ 'WholeConfigValidator',
56
+ 'MemberValidator',
57
+ 'StrValidator',
58
+ 'IntFloatValidator',
59
+ 'string_best_match',
60
+ 'string_to_enum_best_match',
61
+ 'InvalidConfiguration',
62
+ 'InvalidConfigurationValue',
63
+ 'ListValueValidator',
64
+ 'ListSizeValidator',
65
+ 'ListIsOrderedValidator',
66
+ 'ListOrderingValidator',
67
+ 'ListForEachValidator',
68
+ 'DictKeysValidator',
69
+ 'DictRule',
70
+ 'DictForEachValidator',
71
+ 'DictVariant',
72
+ 'DiscriminatedDictValidator',
73
+ 'ProjectedMemberValidator',
74
+ 'assert_dict_equal']
@@ -0,0 +1,82 @@
1
+ #! /usr/local/bin/python3
2
+ """Compare mapping objects while ignoring selected keys.
3
+
4
+ This primarily exists as a tool for developers of applications that use
5
+ configuration classes derived from ``Config``.
6
+ It is also useful in test code that wants a readable failure message
7
+ before asserting equality of configuration objects in applications that
8
+ use the library.
9
+ """
10
+
11
+ # Copyright (c) 2024-2026 Tom Björkholm
12
+ # MIT License
13
+
14
+ from typing import Mapping, TextIO
15
+ import sys
16
+
17
+
18
+ def _print_dict_differs(msg: str, lhs: Mapping[str, object],
19
+ rhs: Mapping[str, object],
20
+ stderr_file: TextIO = sys.stderr) -> None:
21
+ """Print a detailed mismatch report to standard error.
22
+
23
+ Args:
24
+ msg: Summary of the mismatch that was detected.
25
+ lhs: Left-hand mapping after any ignored keys were removed.
26
+ rhs: Right-hand mapping after any ignored keys were removed.
27
+ stderr_file: Stream used for diagnostics. Defaults to ``sys.stderr``.
28
+ """
29
+ print(f'{msg}\n' +
30
+ f'Number of keys in left dict: {len(lhs)}\n' +
31
+ f'Number of keys in right dict: {len(rhs)}\n' +
32
+ f' left dict: {str(lhs)}\nright dict: {str(rhs)}',
33
+ file=stderr_file)
34
+
35
+
36
+ def assert_dict_equal(lhs: Mapping[str, object], rhs: Mapping[str, object],
37
+ ignorekeys: list[str],
38
+ stderr_file: TextIO = sys.stderr) -> None:
39
+ """Assert that two mappings are equal after ignoring selected keys.
40
+
41
+ The function makes defensive copies, removes any keys listed in
42
+ ``ignorekeys`` from both sides, prints a readable difference report when
43
+ a mismatch is detected, and finally raises ``AssertionError`` through the
44
+ normal ``assert`` statements.
45
+
46
+ Args:
47
+ lhs: Left-hand mapping to compare.
48
+ rhs: Right-hand mapping to compare.
49
+ ignorekeys: Keys to drop from both mappings before comparison.
50
+ stderr_file: Stream used for diagnostics. Defaults to ``sys.stderr``.
51
+
52
+ Raises:
53
+ AssertionError: The mappings do not match after ignored keys have been
54
+ removed.
55
+ """
56
+ lhs_val = dict(lhs)
57
+ rhs_val = dict(rhs)
58
+ assert isinstance(lhs_val, dict)
59
+ assert isinstance(rhs_val, dict)
60
+ for key in ignorekeys:
61
+ if key in lhs_val:
62
+ del lhs_val[key]
63
+ if key in rhs_val:
64
+ del rhs_val[key]
65
+ if len(lhs_val) != len(rhs_val):
66
+ _print_dict_differs('Different number of keys in dicts',
67
+ lhs_val, rhs_val, stderr_file)
68
+ assert len(lhs_val) == len(rhs_val)
69
+ for key, value in lhs_val.items():
70
+ if key not in rhs_val:
71
+ _print_dict_differs(f'Key "{key}" exist only in left dict.',
72
+ lhs_val, rhs_val, stderr_file)
73
+ assert key in rhs_val
74
+ if value != rhs_val[key]:
75
+ txt = f'Key "{key}" has different values in left and right\n'
76
+ txt += f' left[{key}] = {value}\n'
77
+ txt += f'right[{key}] = {rhs_val[key]}\n'
78
+ _print_dict_differs(txt, lhs_val, rhs_val, stderr_file)
79
+ assert value == rhs_val[key]
80
+ if lhs_val != rhs_val: # pragma: no cover
81
+ _print_dict_differs('Dicts differ', lhs_val, rhs_val, stderr_file)
82
+ assert lhs_val == rhs_val