Skip to content

Commit 3b9eb20

Browse files
Centralize version string formatting in __init__.py.
PEP 440 canonical forms differ between pre-release tags (2.2.0rc1, no dot) and dev/post tags (2.2.0.dev1, with dot), so a single ".".join(__version_info__) cannot produce both correctly. Introduce a _format_version helper that applies the dev/post-vs-pre-release rule in one place, fix the existing ".dev1" tuple element (which produced the malformed "2.2.0..dev1"), and have docs/conf.py and the workflow verify step read the resulting __version__ via ast instead of re-deriving it.
1 parent c7cfe0a commit 3b9eb20

3 files changed

Lines changed: 33 additions & 8 deletions

File tree

.github/workflows/build-test-release.yml

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,21 @@ jobs:
318318
- name: Verify version matches tag
319319
run: |
320320
EXPECTED="${{ steps.meta.outputs.version }}"
321-
ACTUAL="$(python -c "import ast, re; src=open('src/wrapt/__init__.py').read(); m=re.search(r'__version_info__\s*=\s*(\([^)]+\))', src); parts=ast.literal_eval(m.group(1)); print(''.join(p if p.startswith('.') else '.'+p for p in parts).lstrip('.'))")"
321+
ACTUAL="$(python <<'PY'
322+
import ast
323+
src = open('src/wrapt/__init__.py').read()
324+
tree = ast.parse(src)
325+
ns = {}
326+
for node in tree.body:
327+
if isinstance(node, ast.FunctionDef) and node.name == "_format_version":
328+
exec(compile(ast.Module(body=[node], type_ignores=[]), '<v>', 'exec'), ns)
329+
elif isinstance(node, ast.Assign):
330+
names = [t.id for t in node.targets if isinstance(t, ast.Name)]
331+
if any(n.startswith('__version') for n in names):
332+
exec(compile(ast.Module(body=[node], type_ignores=[]), '<v>', 'exec'), ns)
333+
print(ns['__version__'])
334+
PY
335+
)"
322336
if [ "$ACTUAL" != "$EXPECTED" ]; then
323337
echo "Tag version ($EXPECTED) does not match __version__ ($ACTUAL) in src/wrapt/__init__.py" >&2
324338
exit 1

docs/conf.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,21 @@
4949
# built documents.
5050

5151
_init = pathlib.Path(__file__).resolve().parent.parent / "src" / "wrapt" / "__init__.py"
52+
_ns = {}
5253
for node in ast.parse(_init.read_text()).body:
53-
if isinstance(node, ast.Assign) and any(
54-
getattr(t, "id", None) == "__version_info__" for t in node.targets
54+
if isinstance(node, ast.FunctionDef) and node.name == "_format_version":
55+
exec(compile(ast.Module(body=[node], type_ignores=[]), "<v>", "exec"), _ns)
56+
elif isinstance(node, ast.Assign) and any(
57+
getattr(t, "id", None) in ("__version_info__", "__version__")
58+
for t in node.targets
5559
):
56-
version_info = ast.literal_eval(node.value)
57-
break
60+
exec(compile(ast.Module(body=[node], type_ignores=[]), "<v>", "exec"), _ns)
5861

62+
version_info = _ns["__version_info__"]
5963
# The short X.Y version.
6064
version = ".".join(version_info[:2])
6165
# The full version, including alpha/beta/rc tags.
62-
release = ".".join(version_info)
66+
release = _ns["__version__"]
6367

6468
# The language for content autogenerated by Sphinx. Refer to documentation
6569
# for a list of supported languages.

src/wrapt/__init__.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,15 @@
22
Wrapt is a library for decorators, wrappers and monkey patching.
33
"""
44

5-
__version_info__ = ("2", "2", "0", ".dev1")
6-
__version__ = ".".join(__version_info__)
5+
def _format_version(parts):
6+
base = ".".join(parts[:3])
7+
if len(parts) == 3:
8+
return base
9+
suffix = parts[3]
10+
return f"{base}.{suffix}" if suffix.startswith(("dev", "post")) else f"{base}{suffix}"
11+
12+
__version_info__ = ("2", "2", "0", "dev1")
13+
__version__ = _format_version(__version_info__)
714

815
from .__wrapt__ import (
916
BaseObjectProxy,

0 commit comments

Comments
 (0)