Marco Ricci commited on 2025-02-08 17:24:28
Zeige 16 geänderte Dateien mit 37 Einfügungen und 50 Löschungen.
Establish pyproject.toml functionally as the single source of truth for the version number, so users can rely on `importlib.metadata.version` to query the version programmatically [1, 2]. Storing the version number in the packaging metadata makes it directly accessible to most build tools statically, instead of dynamically, which in theory would make certain kinds of operations (reinstall, querying the metadata from a git checkout) much more tractable [3]. And frankly, the only reason the version attribute previously was dynamic was incomplete tooling support. [4, 5] We still retain the `__version__` attribute -- we need the version number for the command-line interface and machinery, and querying it via the installed packages registry is unnecessarily heavyweight -- but only on the top-level package. Same goes for the `__author__` attribute. In future commits, the `__version__` attribute (and version markers in other files such as the manpages) will be auto-generated by our build system, commit hooks, or the like. In general, what we still need is a system to update all mentions of a version number across the software package [6]. Further reading: 1. Python Packaging Authority: [Single-sourcing the Project Version](https://packaging.python.org/en/latest/discussions/single-source-version/). 2. Hynek Schlawek: [Python Packaging Metadata](https://hynek.me/articles/packaging-metadata/). 3. Armin Ronacher: [Constraints are Good: Python's Metadata Dilemma](https://lucumr.pocoo.org/2024/11/26/python-packaging-metadata/). 4. Barry Warsaw: [`hatch version` should be able to handle static version numbers](https://github.com/pypa/hatch/issues/1419). 5. Henry Schreiner: [Support PEP 621 version?](https://github.com/callowayproject/bump-my-version/issues/66) 6. Alex Gaynor: [Scaling Software Development](https://alexgaynor.net/2020/feb/18/scaling-software-development/). "As the scale of a codebase increases, any properties of it which are not programmatically enforced will tend to regress."
... | ... |
@@ -6,6 +6,7 @@ build-backend = "hatchling.build" |
6 | 6 |
name = "derivepassphrase" |
7 | 7 |
description = "An almost faithful Python reimplementation of James Coglan's vault." |
8 | 8 |
readme = "README.md" |
9 |
+version = "0.5a1.dev1" |
|
9 | 10 |
requires-python = ">= 3.9" |
10 | 11 |
license = { text = "zlib/libpng" } |
11 | 12 |
keywords = [] |
... | ... |
@@ -38,7 +39,6 @@ dependencies = [ |
38 | 39 |
# is unavailable in the Python standard library until Python 3.11. |
39 | 40 |
'tomli; python_version < "3.11"' |
40 | 41 |
] |
41 |
-dynamic = ['version'] |
|
42 | 42 |
|
43 | 43 |
[project.optional-dependencies] |
44 | 44 |
dev = [ |
... | ... |
@@ -363,7 +363,7 @@ max-doc-length = 72 # for W505 |
363 | 363 |
convention = 'google' |
364 | 364 |
|
365 | 365 |
[tool.scriv] |
366 |
-version = "command: hatch version" |
|
366 |
+version = "literal: pyproject.toml: project.version" |
|
367 | 367 |
format = "md" |
368 | 368 |
fragment_directory = "docs/changelog.d" |
369 | 369 |
output_file = "docs/changelog.md" |
... | ... |
@@ -5,4 +5,9 @@ |
5 | 5 |
"""Work-alike of vault(1) – a deterministic, stateless password manager""" # noqa: D415,RUF002 |
6 | 6 |
|
7 | 7 |
__author__ = 'Marco Ricci <software@the13thletter.info>' |
8 |
-__version__ = '0.4.0' |
|
8 |
+__distribution_name__ = 'derivepassphrase' |
|
9 |
+ |
|
10 |
+# Automatically generated. DO NOT EDIT! Use importlib.metadata instead |
|
11 |
+# to query the correct values. |
|
12 |
+__version__ = '0.5a1.dev1' |
|
13 |
+# END automatically generated. |
... | ... |
@@ -13,7 +13,8 @@ Warning: |
13 | 13 |
|
14 | 14 |
import derivepassphrase |
15 | 15 |
|
16 |
-__author__ = derivepassphrase.__author__ |
|
17 |
-__version__ = derivepassphrase.__version__ |
|
18 |
- |
|
19 | 16 |
__all__ = () |
17 |
+ |
|
18 |
+PROG_NAME = derivepassphrase.__distribution_name__ |
|
19 |
+VERSION = derivepassphrase.__version__ |
|
20 |
+AUTHOR = derivepassphrase.__author__ |
... | ... |
@@ -31,7 +31,6 @@ import click |
31 | 31 |
import click.shell_completion |
32 | 32 |
from typing_extensions import Any |
33 | 33 |
|
34 |
-import derivepassphrase as dpp |
|
35 | 34 |
from derivepassphrase import _types, ssh_agent, vault |
36 | 35 |
from derivepassphrase._internals import cli_messages as _msg |
37 | 36 |
|
... | ... |
@@ -49,9 +48,6 @@ if TYPE_CHECKING: |
49 | 48 |
|
50 | 49 |
from typing_extensions import Buffer |
51 | 50 |
|
52 |
-__author__ = dpp.__author__ |
|
53 |
-__version__ = dpp.__version__ |
|
54 |
- |
|
55 | 51 |
PROG_NAME = _msg.PROG_NAME |
56 | 52 |
KEY_DISPLAY_LENGTH = 50 |
57 | 53 |
|
... | ... |
@@ -26,7 +26,7 @@ import click |
26 | 26 |
import click.shell_completion |
27 | 27 |
from typing_extensions import Any, ParamSpec, override |
28 | 28 |
|
29 |
-import derivepassphrase as dpp |
|
29 |
+from derivepassphrase import _internals |
|
30 | 30 |
from derivepassphrase._internals import cli_messages as _msg |
31 | 31 |
|
32 | 32 |
if TYPE_CHECKING: |
... | ... |
@@ -37,10 +37,8 @@ if TYPE_CHECKING: |
37 | 37 |
|
38 | 38 |
from typing_extensions import Self |
39 | 39 |
|
40 |
-__author__ = dpp.__author__ |
|
41 |
-__version__ = dpp.__version__ |
|
42 |
- |
|
43 |
-PROG_NAME = _msg.PROG_NAME |
|
40 |
+PROG_NAME = _internals.PROG_NAME |
|
41 |
+VERSION = _internals.VERSION |
|
44 | 42 |
|
45 | 43 |
# Error messages |
46 | 44 |
NOT_AN_INTEGER = 'not an integer' |
... | ... |
@@ -964,7 +962,7 @@ def version_option_callback( |
964 | 962 |
_msg.TranslatedString( |
965 | 963 |
_msg.Label.VERSION_INFO_TEXT, |
966 | 964 |
PROG_NAME=PROG_NAME, |
967 |
- __version__=__version__, |
|
965 |
+ VERSION=VERSION, |
|
968 | 966 |
) |
969 | 967 |
), |
970 | 968 |
) |
... | ... |
@@ -33,19 +33,18 @@ from typing import TYPE_CHECKING, NamedTuple, Protocol, TextIO, Union, cast |
33 | 33 |
|
34 | 34 |
from typing_extensions import TypeAlias, override |
35 | 35 |
|
36 |
-import derivepassphrase as dpp |
|
36 |
+from derivepassphrase import _internals |
|
37 | 37 |
|
38 | 38 |
if TYPE_CHECKING: |
39 | 39 |
from collections.abc import Iterable, Iterator, Mapping, Sequence |
40 | 40 |
|
41 | 41 |
from typing_extensions import Any, Self |
42 | 42 |
|
43 |
-__author__ = dpp.__author__ |
|
44 |
-__version__ = dpp.__version__ |
|
45 |
- |
|
46 | 43 |
__all__ = ('PROG_NAME',) |
47 | 44 |
|
48 |
-PROG_NAME = 'derivepassphrase' |
|
45 |
+PROG_NAME = _internals.PROG_NAME |
|
46 |
+VERSION = _internals.VERSION |
|
47 |
+AUTHOR = _internals.AUTHOR |
|
49 | 48 |
|
50 | 49 |
|
51 | 50 |
def load_translations( |
... | ... |
@@ -1250,7 +1249,7 @@ class Label(enum.Enum): |
1250 | 1249 |
'', |
1251 | 1250 |
)( |
1252 | 1251 |
'Label :: Info Message', |
1253 |
- '{PROG_NAME!s} {__version__}', # noqa: RUF027 |
|
1252 |
+ '{PROG_NAME!s} {VERSION}', # noqa: RUF027 |
|
1254 | 1253 |
flags='python-brace-format', |
1255 | 1254 |
) |
1256 | 1255 |
"""""" |
... | ... |
@@ -2226,7 +2225,7 @@ def _write_po_file( # noqa: C901,PLR0912 |
2226 | 2225 |
/, |
2227 | 2226 |
*, |
2228 | 2227 |
is_template: bool = True, |
2229 |
- version: str = __version__, |
|
2228 |
+ version: str = VERSION, |
|
2230 | 2229 |
) -> None: # pragma: no cover |
2231 | 2230 |
r"""Write a .po file to the given file object. |
2232 | 2231 |
|
... | ... |
@@ -2284,7 +2283,7 @@ def _write_po_file( # noqa: C901,PLR0912 |
2284 | 2283 |
header = ( |
2285 | 2284 |
inspect.cleandoc(rf""" |
2286 | 2285 |
# English debug translation for {PROG_NAME!s}. |
2287 |
- # Copyright (C) {build_time.strftime('%Y')} {__author__} |
|
2286 |
+ # Copyright (C) {build_time.strftime('%Y')} {AUTHOR!s} |
|
2288 | 2287 |
# This file is distributed under the same license as {PROG_NAME!s}. |
2289 | 2288 |
# |
2290 | 2289 |
msgid "" |
... | ... |
@@ -2311,7 +2310,7 @@ def _write_po_file( # noqa: C901,PLR0912 |
2311 | 2310 |
}) |
2312 | 2311 |
else: |
2313 | 2312 |
po_info.update({ |
2314 |
- 'Last-Translator': __author__, |
|
2313 |
+ 'Last-Translator': AUTHOR, |
|
2315 | 2314 |
'Language': 'en_DEBUG', |
2316 | 2315 |
'Language-Team': 'English', |
2317 | 2316 |
}) |
... | ... |
@@ -2453,7 +2452,7 @@ if __name__ == '__main__': |
2453 | 2452 |
'--set-version', |
2454 | 2453 |
action='store', |
2455 | 2454 |
dest='version', |
2456 |
- default=__version__, |
|
2455 |
+ default=VERSION, |
|
2457 | 2456 |
help='Override declared software version', |
2458 | 2457 |
) |
2459 | 2458 |
args = ap.parse_args() |
... | ... |
@@ -28,8 +28,7 @@ from typing_extensions import ( |
28 | 28 |
Any, |
29 | 29 |
) |
30 | 30 |
|
31 |
-import derivepassphrase as dpp |
|
32 |
-from derivepassphrase import _types, exporter, ssh_agent, vault |
|
31 |
+from derivepassphrase import _internals, _types, exporter, ssh_agent, vault |
|
33 | 32 |
from derivepassphrase._internals import cli_helpers, cli_machinery |
34 | 33 |
from derivepassphrase._internals import cli_messages as _msg |
35 | 34 |
|
... | ... |
@@ -38,12 +37,10 @@ if TYPE_CHECKING: |
38 | 37 |
Sequence, |
39 | 38 |
) |
40 | 39 |
|
41 |
-__author__ = dpp.__author__ |
|
42 |
-__version__ = dpp.__version__ |
|
43 |
- |
|
44 | 40 |
__all__ = ('derivepassphrase',) |
45 | 41 |
|
46 |
-PROG_NAME = _msg.PROG_NAME |
|
42 |
+PROG_NAME = _internals.PROG_NAME |
|
43 |
+VERSION = _internals.VERSION |
|
47 | 44 |
|
48 | 45 |
|
49 | 46 |
@click.group( |
... | ... |
@@ -11,17 +11,12 @@ import os |
11 | 11 |
import pathlib |
12 | 12 |
from typing import TYPE_CHECKING, Protocol |
13 | 13 |
|
14 |
-import derivepassphrase as dpp |
|
15 |
- |
|
16 | 14 |
if TYPE_CHECKING: |
17 | 15 |
from collections.abc import Callable |
18 | 16 |
from typing import Any |
19 | 17 |
|
20 | 18 |
from typing_extensions import Buffer |
21 | 19 |
|
22 |
-__author__ = dpp.__author__ |
|
23 |
-__version__ = dpp.__version__ |
|
24 |
- |
|
25 | 20 |
__all__ = () |
26 | 21 |
|
27 | 22 |
|
... | ... |
@@ -24,7 +24,6 @@ if TYPE_CHECKING: |
24 | 24 |
from typing_extensions import Buffer |
25 | 25 |
|
26 | 26 |
__all__ = ('SSHAgentClient',) |
27 |
-__author__ = 'Marco Ricci <software@the13thletter.info>' |
|
28 | 27 |
|
29 | 28 |
# In SSH bytestrings, the "length" of the byte string is stored as |
30 | 29 |
# a 4-byte/32-bit unsigned integer at the beginning. |
... | ... |
@@ -1616,7 +1616,7 @@ class TestCLI: |
1616 | 1616 |
assert result.clean_exit(empty_stderr=True, output=cli.PROG_NAME), ( |
1617 | 1617 |
'expected clean exit, and program name in version text' |
1618 | 1618 |
) |
1619 |
- assert result.clean_exit(empty_stderr=True, output=cli.__version__), ( |
|
1619 |
+ assert result.clean_exit(empty_stderr=True, output=cli.VERSION), ( |
|
1620 | 1620 |
'expected clean exit, and version in help text' |
1621 | 1621 |
) |
1622 | 1622 |
|
1623 | 1623 |