ycleptic 2.2.1__tar.gz → 2.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ycleptic-2.2.1 → ycleptic-2.2.2}/CHANGELOG.md +8 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/PKG-INFO +8 -2
- {ycleptic-2.2.1 → ycleptic-2.2.2}/README.md +6 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/docs/source/index.rst +18 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/pyproject.toml +1 -1
- {ycleptic-2.2.1 → ycleptic-2.2.2}/tests/test_set.py +36 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/ycleptic/yclept.py +15 -6
- {ycleptic-2.2.1 → ycleptic-2.2.2}/.envrc +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/.github/workflows/ci.yaml +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/.github/workflows/release.yaml +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/.gitignore +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/.readthedocs.yaml +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/LICENSE +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/docs/Makefile +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/docs/make.bat +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/docs/requirements.txt +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/docs/source/_static/css/custom.css +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/docs/source/about.rst +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/docs/source/api/API.rst +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/docs/source/api/ycleptic.cli.rst +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/docs/source/api/ycleptic.dictthings.rst +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/docs/source/api/ycleptic.errors.rst +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/docs/source/api/ycleptic.makedoc.rst +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/docs/source/api/ycleptic.resources.rst +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/docs/source/api/ycleptic.rst +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/docs/source/api/ycleptic.stringthings.rst +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/docs/source/api/ycleptic.walkers.rst +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/docs/source/api/ycleptic.yclept.rst +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/docs/source/changelog.rst +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/docs/source/conf.py +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/docs/source/installation.rst +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/docs/source/roadmap.md +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/docs/source/usage/class.rst +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/docs/source/usage/interactive_help.rst +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/docs/source/usage/quickstart.rst +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/docs/source/usage/resource_file.rst +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/docs/source/usage/the_base_config.rst +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/docs/source/usage/the_user_config.rst +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/docs/source/usage/yclept_config-help.rst +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/docs/source/usage/yclept_makedoc.rst +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/docs/source/usage.rst +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/pytest.ini +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/scripts/release.sh +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/tests/__init__.py +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/tests/conftest.py +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/tests/test_set/test_package/rootdir/README.md +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/tests/test_set/test_package/rootdir/mypackage/__init__.py +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/tests/test_set/test_package/rootdir/mypackage/config.py +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/tests/test_set/test_package/rootdir/mypackage/data/__init__.py +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/tests/test_set/test_package/rootdir/mypackage/data/base.yaml +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/tests/test_set/test_package/rootdir/mypackage/docs/source/intro.rst +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/tests/test_set/test_package/rootdir/mypackage/otherstuff/stuff.py +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/tests/test_set/test_package/rootdir/setup.py +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/ycleptic/__init__.py +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/ycleptic/cli.py +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/ycleptic/dictthings.py +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/ycleptic/errors.py +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/ycleptic/makedoc.py +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/ycleptic/resources/__init__.py +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/ycleptic/resources/example_base.yaml +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/ycleptic/src/__init__.py +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/ycleptic/stringthings.py +0 -0
- {ycleptic-2.2.1 → ycleptic-2.2.2}/ycleptic/walkers.py +0 -0
|
@@ -5,6 +5,14 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [2.2.2] - 2026-08-13
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Status badges (PyPI version, supported Python versions, CI, documentation, license) on the README and the documentation landing page
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
- Interactive `config-help` no longer ends with an uncaught traceback when stdin reaches EOF (piped or redirected input) or when the user presses Ctrl-C; either case now quits the session cleanly, exactly as if the user had typed `!`
|
|
15
|
+
|
|
8
16
|
## [2.2.1] - 2026-07-21
|
|
9
17
|
|
|
10
18
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: ycleptic
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.2
|
|
4
4
|
Summary: A YAML-format configuration generator
|
|
5
5
|
Project-URL: Source, https://github.com/cameronabrams/ycleptic
|
|
6
6
|
Project-URL: Documentation, https://ycleptic.readthedocs.io/en/latest/
|
|
@@ -48,6 +48,12 @@ Description-Content-Type: text/markdown
|
|
|
48
48
|
# Ycleptic
|
|
49
49
|
> Self-documenting YAML configuration for Python applications
|
|
50
50
|
|
|
51
|
+
[](https://pypi.org/project/ycleptic/)
|
|
52
|
+
[](https://pypi.org/project/ycleptic/)
|
|
53
|
+
[](https://github.com/cameronabrams/ycleptic/actions/workflows/ci.yaml)
|
|
54
|
+
[](https://ycleptic.readthedocs.io/en/latest/?badge=latest)
|
|
55
|
+
[](https://github.com/cameronabrams/ycleptic/blob/main/LICENSE)
|
|
56
|
+
|
|
51
57
|
One structured YAML file describes your application's configuration schema. From
|
|
52
58
|
that single source of truth, Ycleptic gives you three things that normally live
|
|
53
59
|
in three separate places:
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
# Ycleptic
|
|
2
2
|
> Self-documenting YAML configuration for Python applications
|
|
3
3
|
|
|
4
|
+
[](https://pypi.org/project/ycleptic/)
|
|
5
|
+
[](https://pypi.org/project/ycleptic/)
|
|
6
|
+
[](https://github.com/cameronabrams/ycleptic/actions/workflows/ci.yaml)
|
|
7
|
+
[](https://ycleptic.readthedocs.io/en/latest/?badge=latest)
|
|
8
|
+
[](https://github.com/cameronabrams/ycleptic/blob/main/LICENSE)
|
|
9
|
+
|
|
4
10
|
One structured YAML file describes your application's configuration schema. From
|
|
5
11
|
that single source of truth, Ycleptic gives you three things that normally live
|
|
6
12
|
in three separate places:
|
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
ycleptic
|
|
2
2
|
========
|
|
3
3
|
|
|
4
|
+
|pypi| |pyversions| |ci| |docs| |license|
|
|
5
|
+
|
|
6
|
+
.. |pypi| image:: https://img.shields.io/pypi/v/ycleptic
|
|
7
|
+
:target: https://pypi.org/project/ycleptic/
|
|
8
|
+
:alt: PyPI version
|
|
9
|
+
.. |pyversions| image:: https://img.shields.io/pypi/pyversions/ycleptic
|
|
10
|
+
:target: https://pypi.org/project/ycleptic/
|
|
11
|
+
:alt: Supported Python versions
|
|
12
|
+
.. |ci| image:: https://github.com/cameronabrams/ycleptic/actions/workflows/ci.yaml/badge.svg
|
|
13
|
+
:target: https://github.com/cameronabrams/ycleptic/actions/workflows/ci.yaml
|
|
14
|
+
:alt: CI status
|
|
15
|
+
.. |docs| image:: https://readthedocs.org/projects/ycleptic/badge/?version=latest
|
|
16
|
+
:target: https://ycleptic.readthedocs.io/en/latest/?badge=latest
|
|
17
|
+
:alt: Documentation status
|
|
18
|
+
.. |license| image:: https://img.shields.io/pypi/l/ycleptic
|
|
19
|
+
:target: https://github.com/cameronabrams/ycleptic/blob/main/LICENSE
|
|
20
|
+
:alt: License: MIT
|
|
21
|
+
|
|
4
22
|
**Ycleptic** (ee-KLEP-tic) turns a single, structured YAML file into a self-documenting configuration system for your Python application. From one schema file you get three things at once: validation of a user's config (with defaults, allowed value ``choices``, and required parameters), an automatically generated RST/Sphinx documentation tree, and an interactive help explorer for your users. Because the schema is data rather than Python code, editing that one file keeps validation, documentation, and help in lockstep.
|
|
5
23
|
|
|
6
24
|
Ycleptic is intentionally lightweight on validation and focuses on the *spec-as-documentation* workflow — a good fit for scientific and command-line applications whose users are domain experts. If you need heavyweight validation and coercion, `pydantic <https://docs.pydantic.dev/>`_ or `jsonschema <https://python-jsonschema.readthedocs.io/>`_ are better suited; for composable, hierarchical config, see `Hydra <https://hydra.cc/>`_.
|
|
@@ -270,6 +270,42 @@ base|attribute_2->attribute_2a
|
|
|
270
270
|
with open('console-out.txt', 'r') as f:
|
|
271
271
|
self.assertEqual(f.read(), ref_str)
|
|
272
272
|
|
|
273
|
+
def test_console_help_eof_quits_cleanly(self):
|
|
274
|
+
"""EOF on stdin ends an interactive help session as if the user typed '!'."""
|
|
275
|
+
from unittest.mock import patch
|
|
276
|
+
|
|
277
|
+
Y = Yclept(BFILE)
|
|
278
|
+
with open('console-out.txt', 'w') as f:
|
|
279
|
+
with redirect_stdout(f):
|
|
280
|
+
with patch('builtins.input', side_effect=EOFError):
|
|
281
|
+
Y.console_help([], interactive_prompt='help: ')
|
|
282
|
+
with open('console-out.txt', 'r') as f:
|
|
283
|
+
self.assertIn('! quit', f.read())
|
|
284
|
+
|
|
285
|
+
def test_console_help_eof_after_bad_choice_quits_cleanly(self):
|
|
286
|
+
"""EOF also ends the session from the re-prompt after an unrecognized choice."""
|
|
287
|
+
from unittest.mock import patch
|
|
288
|
+
|
|
289
|
+
Y = Yclept(BFILE)
|
|
290
|
+
with open('console-out.txt', 'w') as f:
|
|
291
|
+
with redirect_stdout(f):
|
|
292
|
+
with patch('builtins.input', side_effect=['bogus', EOFError]):
|
|
293
|
+
Y.console_help([], interactive_prompt='help: ')
|
|
294
|
+
with open('console-out.txt', 'r') as f:
|
|
295
|
+
self.assertIn('bogus not recognized.', f.read())
|
|
296
|
+
|
|
297
|
+
def test_console_help_keyboard_interrupt_quits_cleanly(self):
|
|
298
|
+
"""Ctrl-C at the prompt ends the session cleanly rather than raising."""
|
|
299
|
+
from unittest.mock import patch
|
|
300
|
+
|
|
301
|
+
Y = Yclept(BFILE)
|
|
302
|
+
with open('console-out.txt', 'w') as f:
|
|
303
|
+
with redirect_stdout(f):
|
|
304
|
+
with patch('builtins.input', side_effect=KeyboardInterrupt):
|
|
305
|
+
Y.console_help([], interactive_prompt='help: ')
|
|
306
|
+
with open('console-out.txt', 'r') as f:
|
|
307
|
+
self.assertIn('! quit', f.read())
|
|
308
|
+
|
|
273
309
|
def test_makedoc(self):
|
|
274
310
|
Y = Yclept(BFILE)
|
|
275
311
|
Y.make_doctree('ydoc')
|
|
@@ -201,23 +201,32 @@ class Yclept(UserDict):
|
|
|
201
201
|
c = ''
|
|
202
202
|
H.write_func(f' {m}{c}{a}')
|
|
203
203
|
|
|
204
|
+
def _prompt_choice(self):
|
|
205
|
+
H: Namespace = self.H
|
|
206
|
+
choice = '!'
|
|
207
|
+
if H.interactive_prompt != '':
|
|
208
|
+
try:
|
|
209
|
+
choice = input(H.interactive_prompt)
|
|
210
|
+
except (EOFError, KeyboardInterrupt):
|
|
211
|
+
# no more input, or the user interrupted; quit as though
|
|
212
|
+
# the user had typed '!'
|
|
213
|
+
H.write_func('')
|
|
214
|
+
choice = '!'
|
|
215
|
+
return choice
|
|
216
|
+
|
|
204
217
|
def _get_help_choice(self, init_list: list[str]):
|
|
205
218
|
H: Namespace = self.H
|
|
206
219
|
if len(init_list) > 0:
|
|
207
220
|
choice = init_list.pop()
|
|
208
221
|
else:
|
|
209
|
-
choice =
|
|
210
|
-
if H.interactive_prompt != '':
|
|
211
|
-
choice = input(H.interactive_prompt)
|
|
222
|
+
choice = self._prompt_choice()
|
|
212
223
|
while choice == '' or choice not in [x['name'] for x in H.base] + ['..', '!']:
|
|
213
224
|
if choice != '':
|
|
214
225
|
H.write_func(f'{choice} not recognized.')
|
|
215
226
|
if len(init_list) > 0:
|
|
216
227
|
choice = init_list.pop()
|
|
217
228
|
else:
|
|
218
|
-
choice =
|
|
219
|
-
if H.interactive_prompt != '':
|
|
220
|
-
choice = input(H.interactive_prompt)
|
|
229
|
+
choice = self._prompt_choice()
|
|
221
230
|
return choice
|
|
222
231
|
|
|
223
232
|
def _help(self):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ycleptic-2.2.1 → ycleptic-2.2.2}/tests/test_set/test_package/rootdir/mypackage/data/__init__.py
RENAMED
|
File without changes
|
{ycleptic-2.2.1 → ycleptic-2.2.2}/tests/test_set/test_package/rootdir/mypackage/data/base.yaml
RENAMED
|
File without changes
|
|
File without changes
|
{ycleptic-2.2.1 → ycleptic-2.2.2}/tests/test_set/test_package/rootdir/mypackage/otherstuff/stuff.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|