Skip to content

Commit e8023e8

Browse files
dobyclaude
andcommitted
chore: centralise la version (0.0.1) sur __init__.py
Source unique dans `nikon_transfer.__version__`, lue par pyproject.toml (`dynamic = ["version"]`), par le .spec PyInstaller (parsing AST pour éviter d'importer le package), par la CLI (`--version`) et par le titre de la GUI. Reset 1.0.0 → 0.0.1. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 0110c56 commit e8023e8

6 files changed

Lines changed: 39 additions & 7 deletions

File tree

CLAUDE.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,8 @@ ne pas les défaire sans raison.
147147
- Tailles d'octets : `format_size` (base 1024, pour la taille des fichiers)
148148
vs `format_storage_size` (base 1000 SI, pour l'affichage carte mémoire — colle
149149
à l'étiquette « 32 Go » du fabricant). Ne pas mélanger.
150+
- Version : **source unique** dans `nikon_transfer/__init__.py:__version__`.
151+
Lue par `pyproject.toml` (`dynamic = ["version"]` + `[tool.setuptools.dynamic]`),
152+
par la CLI (`--version`), par la GUI (titre de fenêtre), et par le `.spec`
153+
PyInstaller (via parsing AST pour éviter d'importer le package — sinon ça
154+
trigger PySide6 au build). Ne jamais hardcoder la version ailleurs.

nikon_transfer.spec

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,20 @@ Build: pyinstaller nikon_transfer.spec --noconfirm
55
Output: dist/Nikon Transfer.app
66
"""
77

8+
# Lit la version depuis le package — source unique de vérité.
9+
# On évite `import nikon_transfer` (qui déclenche l'import de PySide6 via
10+
# le module .gui ré-exporté) en lisant directement l'AST de __init__.py.
11+
import ast
12+
from pathlib import Path
13+
14+
_init = Path(SPECPATH) / "nikon_transfer" / "__init__.py"
15+
_version = next(
16+
ast.literal_eval(node.value)
17+
for node in ast.walk(ast.parse(_init.read_text(encoding="utf-8")))
18+
if isinstance(node, ast.Assign)
19+
and any(getattr(t, "id", None) == "__version__" for t in node.targets)
20+
)
21+
822
a = Analysis(
923
['nikon_transfer_gui.py'],
1024
pathex=[],
@@ -60,8 +74,8 @@ app = BUNDLE(
6074
info_plist={
6175
'CFBundleName': 'Nikon Transfer',
6276
'CFBundleDisplayName': 'Nikon Transfer D5300',
63-
'CFBundleShortVersionString': '1.0.0',
64-
'CFBundleVersion': '1.0.0',
77+
'CFBundleShortVersionString': _version,
78+
'CFBundleVersion': _version,
6579
'NSHighResolutionCapable': True,
6680
# Let Qt follow the system appearance (dark/light).
6781
'NSRequiresAquaSystemAppearance': False,

nikon_transfer/__init__.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
"""nikon_transfer — Wi-Fi photo transfer from Nikon D5300 via PTP/IP."""
22

3-
__version__ = "1.0.0"
4-
__all__ = ["PtpIpClient", "transfer_photos", "discover_camera"]
3+
# Source unique de la version du package — lue par pyproject.toml (via
4+
# `tool.setuptools.dynamic`), par la CLI (`--version`), par la GUI (titre),
5+
# et par le .spec PyInstaller (Info.plist). Modifier ici, jamais ailleurs.
6+
__version__ = "0.0.1"
7+
__all__ = ["PtpIpClient", "transfer_photos", "discover_camera", "__version__"]
58

69
from .client import PtpIpClient
710
from .transfer import discover_camera, transfer_photos

nikon_transfer/cli.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import logging
55
from pathlib import Path
66

7+
from . import __version__
78
from .log import setup as setup_logging
89
from .protocol import PTPIP_HOST_DEFAULT, PTPIP_PORT, IMAGE_EXTENSIONS
910
from .transfer import discover_camera, transfer_photos
@@ -26,6 +27,11 @@ def build_parser() -> argparse.ArgumentParser:
2627
nikon-transfer --log-file ~/Desktop/cam.log # chemin personnalisé
2728
""",
2829
)
30+
parser.add_argument(
31+
"--version",
32+
action="version",
33+
version=f"nikon-transfer {__version__}",
34+
)
2935
parser.add_argument(
3036
"--host",
3137
default=PTPIP_HOST_DEFAULT,

nikon_transfer/gui.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
QCheckBox, QComboBox, QDialog, QMenu, QTabWidget,
2323
)
2424

25+
from . import __version__
2526
from .client import PtpIpClient
2627
from .log import setup as setup_logging
2728
from .protocol import PTPIP_HOST_DEFAULT, IMAGE_EXTENSIONS
@@ -544,7 +545,7 @@ class MainWindow(QMainWindow):
544545
request_download = Signal(list, str, bool)
545546
request_disconnect = Signal()
546547

547-
_BASE_TITLE = "Nikon D5300 — Transfert"
548+
_BASE_TITLE = f"Nikon D5300 — Transfert v{__version__}"
548549
_CLOCK_DRIFT_WARN_SECONDS = 5 * 60 # > 5 min → warn user once per session
549550
_MAX_RECONNECT_ATTEMPTS = 3
550551
_RECONNECT_BACKOFF_MS = [2000, 4000, 8000] # exponential between tries
@@ -1235,7 +1236,7 @@ def _on_device_info(self, info: dict) -> None:
12351236
fw = (info.get("device_version") or "").strip()
12361237
title = self._BASE_TITLE
12371238
if model:
1238-
title = f"{model} — Transfert"
1239+
title = f"{model} — Transfert v{__version__}"
12391240
if fw:
12401241
title += f" (firmware {fw})"
12411242
self.setWindowTitle(title)

pyproject.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "nikon-transfer"
7-
version = "1.0.0"
7+
dynamic = ["version"] # lue depuis nikon_transfer.__version__ — voir [tool.setuptools.dynamic]
88
description = "Wi-Fi photo transfer from Nikon D5300 via PTP/IP — stdlib only, optional Qt GUI"
99
readme = "README.md"
1010
license = "MIT"
@@ -48,6 +48,9 @@ Issues = "https://github.com/doby/nikon-transfer-wifi-dseries/issues"
4848
where = ["."]
4949
include = ["nikon_transfer*"]
5050

51+
[tool.setuptools.dynamic]
52+
version = {attr = "nikon_transfer.__version__"}
53+
5154
[tool.pytest.ini_options]
5255
testpaths = ["tests"]
5356
addopts = "-v --tb=short"

0 commit comments

Comments
 (0)