Skip to content

Commit 1295d41

Browse files
refactor: improve GlobalOverrides API and fix time parameter handling
- Make tool parameter required in GlobalOverrides.from_env() (no default) - Change env parameter to default directly to os.environ (simplifies code) - Add UserWarning when auto-creating GlobalOverrides for backwards compatibility - Update meta() to pass time parameter through to ScmVersion constructor - Add _ScmVersionKwargs TypedDict for proper typing of constructor arguments - Fix test_functions.py to pass explicit time to avoid import-time context creation This prevents unnecessary auto-creation of GlobalOverrides context during module import and provides better type safety for the meta() function.
1 parent 5e0606d commit 1295d41

File tree

3 files changed

+54
-18
lines changed

3 files changed

+54
-18
lines changed

setuptools-scm/testing_scm/test_functions.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import shutil
99
import subprocess
1010

11+
from datetime import datetime
12+
from datetime import timezone
1113
from pathlib import Path
1214

1315
import pytest
@@ -21,8 +23,15 @@
2123

2224
c = Configuration()
2325

26+
# Use explicit time to avoid triggering auto-creation of GlobalOverrides at import time
2427
VERSIONS = {
25-
"exact": meta("1.1", distance=0, dirty=False, config=c),
28+
"exact": meta(
29+
"1.1",
30+
distance=0,
31+
dirty=False,
32+
config=c,
33+
time=datetime(2009, 2, 13, 23, 31, 30, tzinfo=timezone.utc),
34+
),
2635
}
2736

2837

vcs-versioning/src/vcs_versioning/_version_schemes.py

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,18 @@ def _parse_tag(
333333
return tag
334334

335335

336+
class _ScmVersionKwargs(TypedDict, total=False):
337+
"""TypedDict for ScmVersion constructor keyword arguments."""
338+
339+
distance: int
340+
node: str | None
341+
dirty: bool
342+
preformatted: bool
343+
branch: str | None
344+
node_date: date | None
345+
time: datetime
346+
347+
336348
def meta(
337349
tag: str | _Version,
338350
*,
@@ -357,18 +369,20 @@ def meta(
357369

358370
log.info("version %s -> %s", tag, parsed_version)
359371
assert parsed_version is not None, f"Can't parse version {tag}"
360-
scm_version = ScmVersion(
361-
parsed_version,
362-
distance=distance,
363-
node=node,
364-
dirty=dirty,
365-
preformatted=preformatted,
366-
branch=branch,
367-
config=config,
368-
node_date=node_date,
369-
)
372+
373+
# Pass time explicitly to avoid triggering default_factory if provided
374+
kwargs: _ScmVersionKwargs = {
375+
"distance": distance,
376+
"node": node,
377+
"dirty": dirty,
378+
"preformatted": preformatted,
379+
"branch": branch,
380+
"node_date": node_date,
381+
}
370382
if time is not None:
371-
scm_version = dataclasses.replace(scm_version, time=time)
383+
kwargs["time"] = time
384+
385+
scm_version = ScmVersion(parsed_version, config=config, **kwargs)
372386
return scm_version
373387

374388

vcs-versioning/src/vcs_versioning/overrides.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import logging
2121
import os
22+
import warnings
2223
from collections.abc import Mapping, MutableMapping
2324
from contextvars import ContextVar
2425
from dataclasses import dataclass
@@ -66,8 +67,8 @@ class GlobalOverrides:
6667
@classmethod
6768
def from_env(
6869
cls,
69-
tool: str = "SETUPTOOLS_SCM",
70-
env: Mapping[str, str] | None = None,
70+
tool: str,
71+
env: Mapping[str, str] = os.environ,
7172
) -> GlobalOverrides:
7273
"""Read all global overrides from environment variables.
7374
@@ -80,8 +81,6 @@ def from_env(
8081
Returns:
8182
GlobalOverrides instance ready to use as context manager
8283
"""
83-
if env is None:
84-
env = os.environ
8584

8685
# Helper to read with fallback to VCS_VERSIONING prefix
8786
def read_with_fallback(name: str) -> str | None:
@@ -281,6 +280,9 @@ def set_var(key: str, value: str) -> None:
281280
"vcs_versioning_overrides", default=None
282281
)
283282

283+
# Flag to track if we've already warned about auto-creating context
284+
_auto_create_warning_issued = False
285+
284286

285287
# Accessor functions for getting current override values
286288

@@ -289,14 +291,25 @@ def get_active_overrides() -> GlobalOverrides:
289291
"""Get the currently active GlobalOverrides instance.
290292
291293
If no context is active, creates one from the current environment
292-
using SETUPTOOLS_SCM prefix.
294+
using SETUPTOOLS_SCM prefix for legacy compatibility.
293295
294296
Returns:
295297
GlobalOverrides instance
296298
"""
299+
global _auto_create_warning_issued
300+
297301
overrides = _active_overrides.get()
298302
if overrides is None:
299-
# Auto-create context from environment
303+
# Auto-create context from environment for backwards compatibility
304+
if not _auto_create_warning_issued:
305+
warnings.warn(
306+
"No GlobalOverrides context is active. "
307+
"Auto-creating one with SETUPTOOLS_SCM prefix for backwards compatibility. "
308+
"Consider using 'with GlobalOverrides.from_env(\"YOUR_TOOL\"):' explicitly.",
309+
UserWarning,
310+
stacklevel=2,
311+
)
312+
_auto_create_warning_issued = True
300313
overrides = GlobalOverrides.from_env("SETUPTOOLS_SCM")
301314
return overrides
302315

0 commit comments

Comments
 (0)