ConfigUpdater 3.2b1__tar.gz → 4.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.
- configupdater-4.0/.gitignore +59 -0
- {ConfigUpdater-3.2b1 → configupdater-4.0}/AUTHORS.rst +6 -1
- configupdater-4.0/CHANGELOG.rst +183 -0
- {ConfigUpdater-3.2b1 → configupdater-4.0}/CONTRIBUTING.rst +20 -20
- configupdater-4.0/PKG-INFO +349 -0
- configupdater-4.0/README.md +320 -0
- configupdater-4.0/docs/api.rst +8 -0
- configupdater-4.0/docs/conf.py +59 -0
- {ConfigUpdater-3.2b1 → configupdater-4.0}/docs/index.rst +6 -2
- configupdater-4.0/docs/usage.rst +388 -0
- configupdater-4.0/pyproject.toml +216 -0
- configupdater-4.0/src/configupdater/__init__.py +18 -0
- {ConfigUpdater-3.2b1 → configupdater-4.0}/src/configupdater/block.py +57 -48
- {ConfigUpdater-3.2b1 → configupdater-4.0}/src/configupdater/builder.py +33 -14
- {ConfigUpdater-3.2b1 → configupdater-4.0}/src/configupdater/configupdater.py +54 -39
- {ConfigUpdater-3.2b1 → configupdater-4.0}/src/configupdater/container.py +18 -21
- {ConfigUpdater-3.2b1 → configupdater-4.0}/src/configupdater/document.py +86 -64
- {ConfigUpdater-3.2b1 → configupdater-4.0}/src/configupdater/option.py +57 -62
- {ConfigUpdater-3.2b1 → configupdater-4.0}/src/configupdater/parser.py +97 -86
- {ConfigUpdater-3.2b1 → configupdater-4.0}/src/configupdater/section.py +111 -77
- {ConfigUpdater-3.2b1 → configupdater-4.0}/tests/conftest.py +4 -7
- {ConfigUpdater-3.2b1 → configupdater-4.0}/tests/test_configupdater.py +272 -35
- configupdater-4.0/tests/test_option.py +171 -0
- configupdater-4.0/tests/test_optionxform.py +270 -0
- configupdater-4.0/tests/test_parser.py +54 -0
- configupdater-4.0/tests/test_readme.py +26 -0
- {ConfigUpdater-3.2b1 → configupdater-4.0}/tests/test_section.py +67 -0
- configupdater-4.0/tools/release_notes.py +61 -0
- {ConfigUpdater-3.2b1 → configupdater-4.0}/tools/stdlib_diff.py +14 -9
- ConfigUpdater-3.2b1/.cirrus.yml +0 -185
- ConfigUpdater-3.2b1/.coveragerc +0 -32
- ConfigUpdater-3.2b1/.github/FUNDING.yml +0 -12
- ConfigUpdater-3.2b1/.github/ISSUE_TEMPLATE/bug_report.md +0 -27
- ConfigUpdater-3.2b1/.github/ISSUE_TEMPLATE/feature_request.md +0 -11
- ConfigUpdater-3.2b1/.github/workflows/publish-package.yml +0 -32
- ConfigUpdater-3.2b1/.gitignore +0 -53
- ConfigUpdater-3.2b1/.isort.cfg +0 -3
- ConfigUpdater-3.2b1/.pre-commit-config.yaml +0 -51
- ConfigUpdater-3.2b1/.readthedocs.yml +0 -22
- ConfigUpdater-3.2b1/CHANGELOG.rst +0 -117
- ConfigUpdater-3.2b1/PKG-INFO +0 -281
- ConfigUpdater-3.2b1/README.rst +0 -247
- ConfigUpdater-3.2b1/docs/Makefile +0 -29
- ConfigUpdater-3.2b1/docs/conf.py +0 -295
- ConfigUpdater-3.2b1/docs/requirements.txt +0 -3
- ConfigUpdater-3.2b1/docs/usage.rst +0 -179
- ConfigUpdater-3.2b1/hatch.toml +0 -0
- ConfigUpdater-3.2b1/pyproject.toml +0 -8
- ConfigUpdater-3.2b1/setup.cfg +0 -110
- ConfigUpdater-3.2b1/setup.py +0 -21
- ConfigUpdater-3.2b1/src/ConfigUpdater.egg-info/PKG-INFO +0 -281
- ConfigUpdater-3.2b1/src/ConfigUpdater.egg-info/SOURCES.txt +0 -64
- ConfigUpdater-3.2b1/src/ConfigUpdater.egg-info/dependency_links.txt +0 -1
- ConfigUpdater-3.2b1/src/ConfigUpdater.egg-info/not-zip-safe +0 -1
- ConfigUpdater-3.2b1/src/ConfigUpdater.egg-info/requires.txt +0 -12
- ConfigUpdater-3.2b1/src/ConfigUpdater.egg-info/top_level.txt +0 -1
- ConfigUpdater-3.2b1/src/configupdater/__init__.py +0 -20
- ConfigUpdater-3.2b1/tests/test_option.py +0 -53
- ConfigUpdater-3.2b1/tests/test_optionxform.py +0 -100
- ConfigUpdater-3.2b1/tests/test_parser.py +0 -16
- ConfigUpdater-3.2b1/tox.ini +0 -101
- {ConfigUpdater-3.2b1 → configupdater-4.0}/LICENSE.txt +0 -0
- {ConfigUpdater-3.2b1 → configupdater-4.0}/docs/_static/.gitignore +0 -0
- {ConfigUpdater-3.2b1 → configupdater-4.0}/docs/authors.rst +0 -0
- {ConfigUpdater-3.2b1 → configupdater-4.0}/docs/changelog.rst +0 -0
- {ConfigUpdater-3.2b1 → configupdater-4.0}/docs/contributing.rst +0 -0
- {ConfigUpdater-3.2b1 → configupdater-4.0}/docs/gfx/README.md +0 -0
- {ConfigUpdater-3.2b1 → configupdater-4.0}/docs/gfx/banner-1280x645.png +0 -0
- {ConfigUpdater-3.2b1 → configupdater-4.0}/docs/gfx/banner-640x323.png +0 -0
- {ConfigUpdater-3.2b1 → configupdater-4.0}/docs/gfx/configupdater.svg +0 -0
- {ConfigUpdater-3.2b1 → configupdater-4.0}/docs/gfx/configupdater_plain.svg +0 -0
- {ConfigUpdater-3.2b1 → configupdater-4.0}/docs/gfx/favicon.ico +0 -0
- {ConfigUpdater-3.2b1 → configupdater-4.0}/docs/gfx/icon-92x128.png +0 -0
- {ConfigUpdater-3.2b1 → configupdater-4.0}/docs/gfx/icon.svg +0 -0
- {ConfigUpdater-3.2b1 → configupdater-4.0}/docs/gfx/sidebar-254x128.png +0 -0
- {ConfigUpdater-3.2b1 → configupdater-4.0}/docs/license.rst +0 -0
- {ConfigUpdater-3.2b1 → configupdater-4.0}/src/configupdater/py.typed +0 -0
- {ConfigUpdater-3.2b1 → configupdater-4.0}/tests/test_setup.cfg +0 -0
- {ConfigUpdater-3.2b1 → configupdater-4.0}/tools/__init__.py +0 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Byte-compiled files and caches
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.so
|
|
5
|
+
.cache/
|
|
6
|
+
.mypy_cache/
|
|
7
|
+
.pytest_cache/
|
|
8
|
+
.ruff_cache/
|
|
9
|
+
|
|
10
|
+
# Virtual environments
|
|
11
|
+
.venv*/
|
|
12
|
+
.conda*/
|
|
13
|
+
.tox/
|
|
14
|
+
.python-version
|
|
15
|
+
|
|
16
|
+
# Build artifacts
|
|
17
|
+
build/
|
|
18
|
+
dist/
|
|
19
|
+
*.egg-info/
|
|
20
|
+
*.egg
|
|
21
|
+
.eggs/
|
|
22
|
+
|
|
23
|
+
# Coverage and test reports
|
|
24
|
+
.coverage
|
|
25
|
+
.coverage.*
|
|
26
|
+
htmlcov/
|
|
27
|
+
coverage.xml
|
|
28
|
+
coverage.lcov
|
|
29
|
+
junit*.xml
|
|
30
|
+
|
|
31
|
+
# Docs
|
|
32
|
+
docs/_build/
|
|
33
|
+
docs/api/
|
|
34
|
+
docs/_rst/
|
|
35
|
+
docs/html/
|
|
36
|
+
site/
|
|
37
|
+
|
|
38
|
+
# Editors and OS files
|
|
39
|
+
.idea/
|
|
40
|
+
.vscode/
|
|
41
|
+
.project
|
|
42
|
+
.pydevproject
|
|
43
|
+
.settings/
|
|
44
|
+
.ropeproject
|
|
45
|
+
tags
|
|
46
|
+
*~
|
|
47
|
+
.*.swp
|
|
48
|
+
.DS_Store
|
|
49
|
+
*/.ipynb_checkpoints/
|
|
50
|
+
|
|
51
|
+
# Claude Code (local only)
|
|
52
|
+
.claude/worktrees/
|
|
53
|
+
.claude/settings.local.json
|
|
54
|
+
|
|
55
|
+
# Temporary files
|
|
56
|
+
*.orig
|
|
57
|
+
*.bak
|
|
58
|
+
*.log
|
|
59
|
+
*.pot
|
|
@@ -3,5 +3,10 @@ Contributors
|
|
|
3
3
|
============
|
|
4
4
|
|
|
5
5
|
* Florian Wilhelm <florian.wilhelm@gmail.com>
|
|
6
|
-
* Christian Theune <ct@flyingcircus.io>
|
|
7
6
|
* Anderson Bravalheri <andersonbravalheri@gmail.com>
|
|
7
|
+
* Christian Theune <ct@flyingcircus.io>
|
|
8
|
+
* codeofwxz (GitHub)
|
|
9
|
+
* mika <211269698+mikamikasuki@users.noreply.github.com>
|
|
10
|
+
* Sinkleberg
|
|
11
|
+
* blouirts123 (GitHub)
|
|
12
|
+
* bysiber (GitHub)
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
=========
|
|
2
|
+
Changelog
|
|
3
|
+
=========
|
|
4
|
+
|
|
5
|
+
Version 4.0
|
|
6
|
+
===========
|
|
7
|
+
|
|
8
|
+
- **Breaking:** Drop support for Python < 3.11, the minimum supported version is now
|
|
9
|
+
Python 3.11, PR #150
|
|
10
|
+
- Add support for Python 3.15, PR #160
|
|
11
|
+
- Simplify type hints using Python 3.11 features (``typing.Self``, ``X | Y`` unions)
|
|
12
|
+
and remove compatibility code for older Python versions, PR #150
|
|
13
|
+
- Add ``add_before_skip_comments`` to ``Section`` for inserting blocks before the
|
|
14
|
+
comments that directly precede a section, issue #117
|
|
15
|
+
- Fix ``add_section`` gluing the new section header to the last line when the file
|
|
16
|
+
does not end with a newline, issue #126
|
|
17
|
+
- Fix ``None`` being written as delimiter when assigning a value to an option that was
|
|
18
|
+
parsed without a value (``allow_no_value``), issue #141
|
|
19
|
+
- Port the option regexes of Python 3.15's ``configparser`` (CPython gh-146333). The
|
|
20
|
+
old ones backtracked quadratically, so a long line with many spaces could stall
|
|
21
|
+
the parser for minutes, PR #161
|
|
22
|
+
- Fix navigating, detaching or inserting at the wrong position when a container holds
|
|
23
|
+
several equal comment or blank-line blocks, issue #140
|
|
24
|
+
- ``del section[key]`` (and thus ``section.pop(key)``) now applies ``optionxform``
|
|
25
|
+
like ``section[key]`` does, PR #148
|
|
26
|
+
- Document that a custom ``optionxform`` must be idempotent, PR #152
|
|
27
|
+
- Build with hatchling and hatch-vcs, with all metadata in ``pyproject.toml``. The
|
|
28
|
+
wheel is now tagged ``py3-none-any`` (instead of ``py2.py3-none-any``), and the
|
|
29
|
+
license is declared as a PEP 639 expression. The ``testing`` extra is removed;
|
|
30
|
+
development dependencies are dependency groups, e.g. ``uv sync``, PR #156
|
|
31
|
+
- Complete the type annotations of the public API, now checked with mypy in strict
|
|
32
|
+
mode, and fix the ``NoneValueDisallowed`` warning under ``python -OO``, PR #158
|
|
33
|
+
- Fix ``NotAttachedError``, ``AlreadyAttachedError`` and ``AssignMultilineValueError``
|
|
34
|
+
failing with an ``AttributeError`` under ``python -OO``, PR #163
|
|
35
|
+
- Correct the ``NoneValueDisallowed`` message. It claimed that an option without a
|
|
36
|
+
value is written with an empty value, but the option is omitted since version 3.0.1,
|
|
37
|
+
PR #163
|
|
38
|
+
- ``add_before.option`` and ``add_after.option`` pass ``delimiter`` and
|
|
39
|
+
``space_around_delimiters`` on to the new option instead of silently ignoring all
|
|
40
|
+
keyword arguments. Other keyword arguments now raise a ``TypeError``, PR #163
|
|
41
|
+
- **Breaking:** An explicit ``space_around_delimiters`` of an ``Option`` (also given
|
|
42
|
+
to ``add_before.option``, ``add_after.option`` or ``Section.create_option``) now
|
|
43
|
+
takes precedence over the syntax options of the ``ConfigUpdater``, so ``False``
|
|
44
|
+
writes ``key=value`` even with the default ``space_around_delimiters=True``.
|
|
45
|
+
``Option`` defaults to ``space_around_delimiters=None``, i.e. it follows the
|
|
46
|
+
``ConfigUpdater`` it belongs to: ``Option("key", "value")`` now writes
|
|
47
|
+
``key=value`` into a ``ConfigUpdater(space_around_delimiters=False)`` instead of
|
|
48
|
+
``key = value``. Options created with ``section[key] = value`` or ``set`` keep
|
|
49
|
+
following the ``ConfigUpdater`` they are copied to, PR #166
|
|
50
|
+
- **Breaking:** ``key in section``, ``section.has_option(key)`` and
|
|
51
|
+
``section.get(key)`` now apply ``optionxform`` to ``key``, like ``section[key]``
|
|
52
|
+
and ConfigParser do. With the default ``str.lower``, ``"KEY" in section`` is now
|
|
53
|
+
``True`` and ``section.get("KEY")`` returns the option instead of ``None``, PR #153
|
|
54
|
+
- **Breaking:** ``Section.set`` and ``ConfigUpdater.set`` keep the given spelling
|
|
55
|
+
when they create a new option, like ``section[key] = value`` does. For example,
|
|
56
|
+
``updater.set("metadata", "Name", "x")`` now writes ``Name = x`` instead of
|
|
57
|
+
``name = x``, PR #153
|
|
58
|
+
- ``ConfigUpdater.set``, ``get``, ``has_option`` and ``remove_option`` apply
|
|
59
|
+
``optionxform`` only once instead of transforming already transformed keys again,
|
|
60
|
+
PR #153
|
|
61
|
+
- Fix wrong code examples in the usage guide and the README, which are now tested,
|
|
62
|
+
PR #164
|
|
63
|
+
- Fix ``update_file`` writing the file with the platform's default encoding instead
|
|
64
|
+
of the encoding it was read with. ``read`` and ``read_file`` remember the encoding
|
|
65
|
+
(``read_file`` takes it from ``f.encoding``), PR #165
|
|
66
|
+
- ``ConfigUpdater.read`` and ``Parser.read`` call ``io.text_encoding`` like
|
|
67
|
+
ConfigParser, so with ``-X warn_default_encoding`` the ``EncodingWarning`` points
|
|
68
|
+
to the caller (PEP 597), PR #165
|
|
69
|
+
|
|
70
|
+
Version 3.2
|
|
71
|
+
===========
|
|
72
|
+
|
|
73
|
+
- Option ``prepend_newline`` in ``set_values`` to optionally avoid new lines, issue #104
|
|
74
|
+
- Fix square brackets not parsed in section names, issue #122
|
|
75
|
+
|
|
76
|
+
Version 3.1.1
|
|
77
|
+
=============
|
|
78
|
+
|
|
79
|
+
- Preserve indentation of section when there are comments, issue #92
|
|
80
|
+
|
|
81
|
+
Version 3.1
|
|
82
|
+
===========
|
|
83
|
+
|
|
84
|
+
- Prevent modifying multi-line values directly with ``value``, issue #87
|
|
85
|
+
- Added ``append`` method to ``Option`` for editing multi-line values
|
|
86
|
+
- Added ``as_list`` method to ``Option`` to handle multi-line values more easily
|
|
87
|
+
|
|
88
|
+
Version 3.0.1
|
|
89
|
+
=============
|
|
90
|
+
|
|
91
|
+
- Fix error when parsing unindented comments in multi-line values, issue #73
|
|
92
|
+
- Fix invalid string produced when ``allow_no_value = False``, issue #68
|
|
93
|
+
|
|
94
|
+
Version 3.0
|
|
95
|
+
===========
|
|
96
|
+
|
|
97
|
+
- Added type hinting, issue #16
|
|
98
|
+
- Fix parsing error of indented comment lines, issue #25
|
|
99
|
+
- Allow handling of raw section comment, issue #25
|
|
100
|
+
- More unit testing of optionxform, issue #55
|
|
101
|
+
- Allowing sections/options to be copied from one document to the other, issue #47
|
|
102
|
+
- New logo, issue #29
|
|
103
|
+
- Whole API was rechecked by @abravalheri and changed for consistency, issue #19
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
Version 2.0
|
|
107
|
+
===========
|
|
108
|
+
|
|
109
|
+
- Changes in parser, i.e. comments in multi-line option values are kept
|
|
110
|
+
- Issue #14 is fixed
|
|
111
|
+
- Parameter ``empty_lines_in_values`` is now activated by default and can be changed
|
|
112
|
+
- Renamed ``sections_blocks`` to ``section_blocks`` for consistency
|
|
113
|
+
- Renamed ``last_item`` to ``last_block`` for consistency
|
|
114
|
+
- Added ``first_block``
|
|
115
|
+
- Reworked some internal parts of the inheritance hierarchy
|
|
116
|
+
- Added ``remove`` to remove the current block
|
|
117
|
+
- Added ``next_block`` and ``previous_block`` for easier navigation in section
|
|
118
|
+
|
|
119
|
+
Version 1.1.3
|
|
120
|
+
=============
|
|
121
|
+
|
|
122
|
+
- Added fallback option to ConfigUpdater.get reflecting ConfigParser
|
|
123
|
+
|
|
124
|
+
Version 1.1.2
|
|
125
|
+
=============
|
|
126
|
+
|
|
127
|
+
- Fix wrongly modifying input in Option.set_value #11
|
|
128
|
+
|
|
129
|
+
Version 1.1.1
|
|
130
|
+
=============
|
|
131
|
+
|
|
132
|
+
- Fix iterating over the items() view of a section breaks #8
|
|
133
|
+
|
|
134
|
+
Version 1.1
|
|
135
|
+
===========
|
|
136
|
+
|
|
137
|
+
- Validate format on write by default (can be deactivated)
|
|
138
|
+
- Fixed issue #7 with mixed-case options
|
|
139
|
+
- Fixed issue #7 with add_before/add_after problem
|
|
140
|
+
- Fixed issue #7 with wrong duplicate mixed-case entries
|
|
141
|
+
- Fixed issue #7 with duplicate options after add_after/before
|
|
142
|
+
|
|
143
|
+
Version 1.0.1
|
|
144
|
+
=============
|
|
145
|
+
|
|
146
|
+
- More sane error message if ``read_file`` is misused
|
|
147
|
+
- Also run unittests with Windows
|
|
148
|
+
|
|
149
|
+
Version 1.0
|
|
150
|
+
===========
|
|
151
|
+
|
|
152
|
+
- Fix: Use \n instead of os.linesep where appropriate
|
|
153
|
+
|
|
154
|
+
Version 0.3.2
|
|
155
|
+
=============
|
|
156
|
+
|
|
157
|
+
- Added Github and documentation link into the project's metadata
|
|
158
|
+
|
|
159
|
+
Version 0.3.1
|
|
160
|
+
=============
|
|
161
|
+
|
|
162
|
+
- Require Python >= 3.4 with ``python_requires``
|
|
163
|
+
|
|
164
|
+
Version 0.3
|
|
165
|
+
===========
|
|
166
|
+
|
|
167
|
+
- Added a ``insert_at`` function at section level
|
|
168
|
+
- Some internal code simplifications
|
|
169
|
+
|
|
170
|
+
Version 0.2
|
|
171
|
+
===========
|
|
172
|
+
|
|
173
|
+
- Added a ``to_dict()`` function
|
|
174
|
+
|
|
175
|
+
Version 0.1.1
|
|
176
|
+
=============
|
|
177
|
+
|
|
178
|
+
- Allow for flexible comment character in ``comment(...)``
|
|
179
|
+
|
|
180
|
+
Version 0.1
|
|
181
|
+
===========
|
|
182
|
+
|
|
183
|
+
- First release
|
|
@@ -22,34 +22,30 @@ additional considerations and avoids unnecessary work.
|
|
|
22
22
|
Create an environment
|
|
23
23
|
---------------------
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
conda create -n configupdater python=3 virtualenv pytest pytest-cov
|
|
29
|
-
|
|
30
|
-
Then activate the environment ``configupdater`` with::
|
|
31
|
-
|
|
32
|
-
source activate configupdater
|
|
25
|
+
ConfigUpdater requires Python 3.11 or newer. The development environment is
|
|
26
|
+
managed with uv_, so install it first. uv also downloads a suitable Python
|
|
27
|
+
version if you don't have one.
|
|
33
28
|
|
|
34
29
|
Clone the repository
|
|
35
30
|
--------------------
|
|
36
31
|
|
|
37
|
-
#. `Create a
|
|
32
|
+
#. `Create a Github account`_ if you do not already have one.
|
|
38
33
|
#. Fork the `project repository`_: click on the *Fork* button near the top of the
|
|
39
34
|
page. This creates a copy of the code under your account on the GitHub server.
|
|
40
35
|
#. Clone this copy to your local disk::
|
|
41
36
|
|
|
42
37
|
git clone git@github.com:YourLogin/configupdater.git
|
|
43
38
|
|
|
44
|
-
#.
|
|
39
|
+
#. Create a virtual environment in ``.venv`` with ``configupdater`` and all
|
|
40
|
+
development tools (pytest, mypy, tox, pre-commit)::
|
|
45
41
|
|
|
46
|
-
|
|
42
|
+
uv sync
|
|
47
43
|
|
|
48
|
-
|
|
49
|
-
pre-commit install
|
|
44
|
+
#. Install the ``pre-commit`` hooks::
|
|
50
45
|
|
|
51
|
-
|
|
52
|
-
|
|
46
|
+
uv run pre-commit install
|
|
47
|
+
|
|
48
|
+
The hooks automatically check the code you are writing on every commit.
|
|
53
49
|
|
|
54
50
|
#. Create a branch to hold your changes::
|
|
55
51
|
|
|
@@ -68,12 +64,16 @@ Clone the repository
|
|
|
68
64
|
|
|
69
65
|
#. Please check that your changes don't break any unit tests with::
|
|
70
66
|
|
|
71
|
-
|
|
67
|
+
uv run tox
|
|
68
|
+
|
|
69
|
+
or, for a quick run without coverage, ``uv run pytest``.
|
|
72
70
|
|
|
73
71
|
Don't forget to also add unit tests in case your contribution
|
|
74
72
|
adds an additional feature and is not just a bugfix.
|
|
75
73
|
|
|
76
|
-
#.
|
|
74
|
+
#. The ``pre-commit`` hooks check your code style with `ruff`_ on every commit.
|
|
75
|
+
You can also run them on all files with ``uv run tox -e lint``, and check the
|
|
76
|
+
type hints with ``uv run tox -e typecheck``.
|
|
77
77
|
#. Add yourself to the list of contributors in ``AUTHORS.rst``.
|
|
78
78
|
#. Go to the web page of your ConfigUpdater fork, and click
|
|
79
79
|
"Create pull request" to send your changes to the maintainers for review.
|
|
@@ -83,8 +83,8 @@ Clone the repository
|
|
|
83
83
|
.. _PyPI: https://pypi.python.org/
|
|
84
84
|
.. _project repository: https://github.com/pyscaffold/configupdater/
|
|
85
85
|
.. _Git: https://git-scm.com/
|
|
86
|
-
..
|
|
86
|
+
.. _uv: https://docs.astral.sh/uv/getting-started/installation/
|
|
87
87
|
.. _issue tracker: https://github.com/pyscaffold/configupdater/issues
|
|
88
|
-
.. _Create a
|
|
88
|
+
.. _Create a Github account: https://github.com/join
|
|
89
89
|
.. _creating a PR: https://help.github.com/articles/creating-a-pull-request/
|
|
90
|
-
..
|
|
90
|
+
.. _ruff: https://docs.astral.sh/ruff/
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: ConfigUpdater
|
|
3
|
+
Version: 4.0
|
|
4
|
+
Summary: Parser like ConfigParser but for updating configuration files
|
|
5
|
+
Project-URL: Homepage, https://github.com/pyscaffold/configupdater
|
|
6
|
+
Project-URL: Documentation, https://configupdater.readthedocs.io/
|
|
7
|
+
Project-URL: Source, https://github.com/pyscaffold/configupdater/
|
|
8
|
+
Project-URL: Issues, https://github.com/pyscaffold/configupdater/issues
|
|
9
|
+
Project-URL: Changelog, https://configupdater.readthedocs.io/en/latest/changelog.html
|
|
10
|
+
Project-URL: Download, https://pypi.org/project/configupdater/#files
|
|
11
|
+
Project-URL: Conda-Forge, https://anaconda.org/conda-forge/configupdater
|
|
12
|
+
Author-email: Florian Wilhelm <florian.wilhelm@gmail.com>, Anderson Bravalheri <andersonbravalheri@gmail.com>
|
|
13
|
+
License-Expression: MIT
|
|
14
|
+
License-File: LICENSE.txt
|
|
15
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
25
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
26
|
+
Classifier: Typing :: Typed
|
|
27
|
+
Requires-Python: >=3.11
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
<div align="center">
|
|
31
|
+
<img src="https://raw.githubusercontent.com/pyscaffold/configupdater/master/docs/gfx/banner-640x323.png" alt="Configupdater logo" width="500" role="img">
|
|
32
|
+
</div>
|
|
33
|
+
<br/>
|
|
34
|
+
|
|
35
|
+
| | |
|
|
36
|
+
|---------|------------------------------------|
|
|
37
|
+
| CI/CD | [![Tests][Tests-image]][Tests-link] [![Coverage][Coverage-image]][Coverage-link] [![Publish Package][Publish-image]][Publish-link] [![GitHub Sponsors][sponsor-image]][sponsor-link] |
|
|
38
|
+
| Package | [![PyPI - Version][PyPI_ver-image]][PyPI_ver-link] [![Conda - Version][Conda-image]][Conda-link] [![PyPI - Downloads][PyPI_down-image]][PyPI_down-link] [![PyPI - Python Version][PyPI_py-image]][PyPI_py-link] |
|
|
39
|
+
| Details | [![uv][uv-image]][uv-link] [![Linting - Ruff][ruff-image]][ruff-link] [![test - pytest][pytest-image]][pytest-link] [![Pre-Commit][precommit-image]][precommit-link] [![Types - Mypy][mypy-image]][mypy-link] [![License - MIT][MIT-image]][MIT-link] [![Docs - RTD][rtd-image]][rtd-link] |
|
|
40
|
+
|
|
41
|
+
The sole purpose of [ConfigUpdater] is to easily
|
|
42
|
+
update an INI config file with no changes to the original file except
|
|
43
|
+
the intended ones. This means comments, the ordering of sections and
|
|
44
|
+
key/value-pairs as wells as their cases are kept as in the original
|
|
45
|
+
file. Thus ConfigUpdater provides complementary functionality to
|
|
46
|
+
Python\'s [ConfigParser], which is primarily meant for reading config
|
|
47
|
+
files and writing *new* ones.
|
|
48
|
+
|
|
49
|
+
# Features
|
|
50
|
+
|
|
51
|
+
The key differences to [ConfigParser] are:
|
|
52
|
+
|
|
53
|
+
- minimal invasive changes in the update configuration file,
|
|
54
|
+
- proper handling of comments,
|
|
55
|
+
- only a single config file can be updated at a time,
|
|
56
|
+
- the original case of sections and keys are kept,
|
|
57
|
+
- control over the position of a new section/key
|
|
58
|
+
|
|
59
|
+
Following features are **deliberately not** implemented:
|
|
60
|
+
|
|
61
|
+
- interpolation of values,
|
|
62
|
+
- propagation of parameters from the default section,
|
|
63
|
+
- conversions of values,
|
|
64
|
+
- passing key/value-pairs with `default` argument,
|
|
65
|
+
- non-strict mode allowing duplicate sections and keys.
|
|
66
|
+
|
|
67
|
+
# Usage
|
|
68
|
+
|
|
69
|
+
First install the package with either:
|
|
70
|
+
|
|
71
|
+
```console
|
|
72
|
+
pip install configupdater
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
or add it to a project managed with [uv]:
|
|
76
|
+
|
|
77
|
+
```console
|
|
78
|
+
uv add configupdater
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
or:
|
|
82
|
+
|
|
83
|
+
```console
|
|
84
|
+
conda install -c conda-forge configupdater
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Now we can simply do:
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from configupdater import ConfigUpdater
|
|
91
|
+
|
|
92
|
+
updater = ConfigUpdater()
|
|
93
|
+
updater.read("setup.cfg")
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
which would read the file `setup.cfg` that is found in many projects.
|
|
97
|
+
|
|
98
|
+
To change the value of an existing key we can simply do:
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
updater["metadata"]["author"].value = "Alan Turing"
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
At any point we can print the current state of the configuration file
|
|
105
|
+
with:
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
print(updater)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
To update the read-in file, with the encoding it was read with, just call
|
|
112
|
+
`updater.update_file()` or `updater.write(open('filename','w'))` to write
|
|
113
|
+
the changed configuration file to another destination. Before actually
|
|
114
|
+
writing, ConfigUpdater will automatically check that the updated
|
|
115
|
+
configuration file is still valid by parsing it with the help of ConfigParser.
|
|
116
|
+
|
|
117
|
+
Many of ConfigParser\'s methods still exists and it\'s best to look them
|
|
118
|
+
up in the [module reference]. Let\'s look at some examples.
|
|
119
|
+
|
|
120
|
+
## Adding and removing options
|
|
121
|
+
|
|
122
|
+
Let\'s say we have the following configuration in a string:
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
cfg = """
|
|
126
|
+
[metadata]
|
|
127
|
+
author = Ada Lovelace
|
|
128
|
+
summary = The Analytical Engine
|
|
129
|
+
"""
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
We can add an *license* option, i.e. a key/value pair, in the same way
|
|
133
|
+
we would do with ConfigParser:
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
updater = ConfigUpdater()
|
|
137
|
+
updater.read_string(cfg)
|
|
138
|
+
updater["metadata"]["license"] = "MIT"
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
A simple `print(updater)` will give show you that the new option was
|
|
142
|
+
appended to the end:
|
|
143
|
+
|
|
144
|
+
```ini
|
|
145
|
+
[metadata]
|
|
146
|
+
author = Ada Lovelace
|
|
147
|
+
summary = The Analytical Engine
|
|
148
|
+
license = MIT
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Since the license is really important to us let\'s say we want to add it
|
|
152
|
+
before the `summary` and even add a short comment before it:
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
updater = ConfigUpdater()
|
|
156
|
+
updater.read_string(cfg)
|
|
157
|
+
(
|
|
158
|
+
updater["metadata"]["summary"]
|
|
159
|
+
.add_before.comment("Ada would have loved MIT")
|
|
160
|
+
.option("license", "MIT")
|
|
161
|
+
)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
which would result in:
|
|
165
|
+
|
|
166
|
+
```ini
|
|
167
|
+
[metadata]
|
|
168
|
+
author = Ada Lovelace
|
|
169
|
+
# Ada would have loved MIT
|
|
170
|
+
license = MIT
|
|
171
|
+
summary = The Analytical Engine
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Using `add_after` would give the same result and looks like:
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
updater = ConfigUpdater()
|
|
178
|
+
updater.read_string(cfg)
|
|
179
|
+
(
|
|
180
|
+
updater["metadata"]["author"]
|
|
181
|
+
.add_after.comment("Ada would have loved MIT")
|
|
182
|
+
.option("license", "MIT")
|
|
183
|
+
)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Let\'s say we want to rename `summary` to the more common
|
|
187
|
+
`description`:
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
updater = ConfigUpdater()
|
|
191
|
+
updater.read_string(cfg)
|
|
192
|
+
updater["metadata"]["summary"].key = "description"
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
If we wanted no summary at all, we could just do
|
|
196
|
+
`del updater["metadata"]["summary"]`.
|
|
197
|
+
|
|
198
|
+
## Adding and removing sections
|
|
199
|
+
|
|
200
|
+
Adding and remove sections just works like adding and removing options
|
|
201
|
+
but on a higher level. Sticking to our *Ada Lovelace* example, let\'s
|
|
202
|
+
say we want to add a section `options` just before `metadata` with a
|
|
203
|
+
comment and two new lines to separate it from `metadata`:
|
|
204
|
+
|
|
205
|
+
```python
|
|
206
|
+
updater = ConfigUpdater()
|
|
207
|
+
updater.read_string(cfg)
|
|
208
|
+
(
|
|
209
|
+
updater["metadata"]
|
|
210
|
+
.add_before.comment("Some specific project options")
|
|
211
|
+
.section("options")
|
|
212
|
+
.space(2)
|
|
213
|
+
)
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
As expected, this results in:
|
|
217
|
+
|
|
218
|
+
```ini
|
|
219
|
+
# Some specific project options
|
|
220
|
+
[options]
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
[metadata]
|
|
224
|
+
author = Ada Lovelace
|
|
225
|
+
summary = The Analytical Engine
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
We could now fill the new section with options like we learnt before. If
|
|
229
|
+
we wanted to rename an existing section we could do this with the help
|
|
230
|
+
of the `name` attribute:
|
|
231
|
+
|
|
232
|
+
```python
|
|
233
|
+
updater["metadata"].name = "MetaData"
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Sometimes it might be useful to inject a new section not in a
|
|
237
|
+
programmatic way but more declarative. Let\'s assume we have thus
|
|
238
|
+
defined our new section in a multi-line string:
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
sphinx_sect_str = """
|
|
242
|
+
[build_sphinx]
|
|
243
|
+
source_dir = docs
|
|
244
|
+
build_dir = docs/_build
|
|
245
|
+
"""
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
With the help of two ConfigUpdater objects we can easily inject this
|
|
249
|
+
section into our example:
|
|
250
|
+
|
|
251
|
+
```python
|
|
252
|
+
sphinx = ConfigUpdater()
|
|
253
|
+
sphinx.read_string(sphinx_sect_str)
|
|
254
|
+
sphinx_sect = sphinx["build_sphinx"]
|
|
255
|
+
|
|
256
|
+
updater = ConfigUpdater()
|
|
257
|
+
updater.read_string(cfg)
|
|
258
|
+
|
|
259
|
+
(updater["metadata"].add_after.space().section(sphinx_sect.detach()))
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
The `detach` method will remove the `build_sphinx` section from the
|
|
263
|
+
first object and add it to the second object. This results in:
|
|
264
|
+
|
|
265
|
+
```ini
|
|
266
|
+
[metadata]
|
|
267
|
+
author = Ada Lovelace
|
|
268
|
+
summary = The Analytical Engine
|
|
269
|
+
|
|
270
|
+
[build_sphinx]
|
|
271
|
+
source_dir = docs
|
|
272
|
+
build_dir = docs/_build
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
Alternatively, if you want to preserve `build_sphinx` in both
|
|
276
|
+
`ConfigUpdater` objects (i.e., prevent it from being removed from the
|
|
277
|
+
first while still adding a copy to the second), you call also rely on
|
|
278
|
+
stdlib\'s `copy.deepcopy` function instead of `detach`:
|
|
279
|
+
|
|
280
|
+
```python
|
|
281
|
+
from copy import deepcopy
|
|
282
|
+
|
|
283
|
+
sphinx = ConfigUpdater()
|
|
284
|
+
sphinx.read_string(sphinx_sect_str)
|
|
285
|
+
sphinx_sect = sphinx["build_sphinx"]
|
|
286
|
+
|
|
287
|
+
updater = ConfigUpdater()
|
|
288
|
+
updater.read_string(cfg)
|
|
289
|
+
|
|
290
|
+
(updater["metadata"].add_after.space().section(deepcopy(sphinx_sect)))
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
This technique can be used for all objects inside ConfigUpdater:
|
|
294
|
+
sections, options, comments and blank spaces.
|
|
295
|
+
|
|
296
|
+
Shallow copies are discouraged in the context of ConfigUpdater because
|
|
297
|
+
each configuration block keeps a reference to its container to allow
|
|
298
|
+
easy document editing. When doing editions (such as adding or changing
|
|
299
|
+
options and comments) based on a shallow copy, the results can be
|
|
300
|
+
unreliable and unexpected.
|
|
301
|
+
|
|
302
|
+
For more examples on how the API of ConfigUpdater works it\'s best to
|
|
303
|
+
take a look into the [unit tests] and read the references.
|
|
304
|
+
|
|
305
|
+
# Notes
|
|
306
|
+
|
|
307
|
+
ConfigUpdater started out as a helper for [PyScaffold], which still uses
|
|
308
|
+
it to update the `setup.cfg` of existing projects. Since then it has
|
|
309
|
+
become a useful tool in its own right for everyone who needs to change
|
|
310
|
+
configuration files programmatically without losing the comments and
|
|
311
|
+
formatting that humans put into them.
|
|
312
|
+
|
|
313
|
+
[ConfigUpdater]: https://configupdater.readthedocs.io/
|
|
314
|
+
[ConfigParser]: https://docs.python.org/3/library/configparser.html
|
|
315
|
+
[PyScaffold]: https://pyscaffold.org/
|
|
316
|
+
[uv]: https://docs.astral.sh/uv/
|
|
317
|
+
[module reference]: https://configupdater.readthedocs.io/en/latest/api.html#configupdater.configupdater.ConfigUpdater
|
|
318
|
+
[unit tests]: https://github.com/pyscaffold/configupdater/blob/main/tests/test_configupdater.py
|
|
319
|
+
|
|
320
|
+
[Tests-image]: https://github.com/pyscaffold/configupdater/actions/workflows/ci.yml/badge.svg
|
|
321
|
+
[Tests-link]: https://github.com/pyscaffold/configupdater/actions/workflows/ci.yml
|
|
322
|
+
[Coverage-image]: https://img.shields.io/coveralls/github/pyscaffold/configupdater/main.svg
|
|
323
|
+
[Coverage-link]: https://coveralls.io/r/pyscaffold/configupdater
|
|
324
|
+
[Publish-image]: https://github.com/pyscaffold/configupdater/actions/workflows/publish-package.yml/badge.svg
|
|
325
|
+
[Publish-link]: https://github.com/pyscaffold/configupdater/actions/workflows/publish-package.yml
|
|
326
|
+
[PyPI_ver-image]:https://img.shields.io/pypi/v/configupdater.svg?logo=pypi&label=PyPI&logoColor=gold
|
|
327
|
+
[PyPI_ver-link]: https://pypi.org/project/configupdater/
|
|
328
|
+
[Conda-image]: https://img.shields.io/conda/vn/conda-forge/configupdater.svg?logo=Anaconda&label=Conda-Forge&logoColor=44A833
|
|
329
|
+
[Conda-link]: https://anaconda.org/conda-forge/configupdater/
|
|
330
|
+
[PyPI_down-image]: https://img.shields.io/pypi/dm/configupdater.svg?color=blue&label=Downloads&logo=pypi&logoColor=gold
|
|
331
|
+
[PyPI_down-link]: https://clickpy.clickhouse.com/dashboard/configupdater
|
|
332
|
+
[PyPI_py-image]: https://img.shields.io/pypi/pyversions/configupdater.svg?logo=python&label=Python&logoColor=gold
|
|
333
|
+
[PyPI_py-link]: https://pypi.org/project/configupdater/
|
|
334
|
+
[uv-image]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json
|
|
335
|
+
[uv-link]: https://github.com/astral-sh/uv
|
|
336
|
+
[ruff-image]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
|
|
337
|
+
[ruff-link]: https://github.com/charliermarsh/ruff
|
|
338
|
+
[mypy-image]: https://img.shields.io/badge/Types-mypy-blue.svg
|
|
339
|
+
[mypy-link]: https://mypy-lang.org/
|
|
340
|
+
[pytest-image]: https://img.shields.io/static/v1?label=&message=Pytest&logo=Pytest&color=0A9EDC&logoColor=white
|
|
341
|
+
[pytest-link]: https://docs.pytest.org/
|
|
342
|
+
[rtd-image]: https://readthedocs.org/projects/configupdater/badge/?version=latest
|
|
343
|
+
[rtd-link]: https://configupdater.readthedocs.io/
|
|
344
|
+
[precommit-image]: https://img.shields.io/static/v1?label=&message=pre-commit&logo=pre-commit&color=76877c
|
|
345
|
+
[precommit-link]: https://pre-commit.com/
|
|
346
|
+
[MIT-image]: https://img.shields.io/badge/License-MIT-9400d3.svg
|
|
347
|
+
[MIT-link]: LICENSE.txt
|
|
348
|
+
[sponsor-image]: https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&color=ff69b4
|
|
349
|
+
[sponsor-link]: https://github.com/sponsors/FlorianWilhelm
|