/
niceSOFT
/
python3-setuptools_scm
Обзор
Документация
Войти
/
niceSOFT
/
python3-setuptools_scm
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
vcs-versioning/src/vcs_versioning/_test_utils.py
295 строк
9 KB
Ronny Pfannschmidt
feat: add native Jujutsu (jj) VCS backend (#1070)
22 июн 2026, 08:36
22 июн 2026, 08:36
c291474
Код
Авторство
О чём код?
from __future__ import annotations import itertools from collections.abc import Callable from pathlib import Path from typing import TYPE_CHECKING, Any import pytest from vcs_versioning._run_cmd import has_command if TYPE_CHECKING: import sys from vcs_versioning._config import Configuration from vcs_versioning._scm_version import ScmVersion, VersionExpectations if sys.version_info >= (3, 11): from typing import Unpack else: from typing_extensions import Unpack class WorkDir: """a simple model for a""" commit_command: str signed_commit_command: str add_command: str tag_command: str parse: Callable[[Path, Configuration], ScmVersion | None] | None = None _env: Any = None """Optional VcsEnvironment for get_version(). Set by test fixtures.""" def __repr__(self) -> str: return f"<WD {self.cwd}>" def __init__(self, cwd: Path) -> None: self.cwd = cwd self.__counter = itertools.count() def __call__(self, cmd: list[str] | str, *, timeout: int = 10, **kw: object) -> str: if kw: assert isinstance(cmd, str), "formatting the command requires text input" cmd = cmd.format(**kw) from vcs_versioning._run_cmd import run return run(cmd, cwd=self.cwd, timeout=timeout).stdout def write(self, name: str, content: str | bytes) -> Path: path = self.cwd / name if isinstance(content, bytes): path.write_bytes(content) else: path.write_text(content, encoding="utf-8") return path def _reason(self, given_reason: str | None) -> str: if given_reason is None: return f"number-{next(self.__counter)}" else: return given_reason def add_and_commit( self, reason: str | None = None, signed: bool = False, **kwargs: object ) -> None: self(self.add_command) self.commit(reason=reason, signed=signed, **kwargs) def commit(self, reason: str | None = None, signed: bool = False) -> None: reason = self._reason(reason) self( self.commit_command if not signed else self.signed_commit_command, reason=reason, ) def commit_testfile(self, reason: str | None = None, signed: bool = False) -> None: reason = self._reason(reason) self.write("test.txt", f"test {reason}") self(self.add_command) self.commit(reason=reason, signed=signed) def get_version(self, **kw: Any) -> str: __tracebackhide__ = True from vcs_versioning._get_version_impl import get_version version = get_version( root=self.cwd, fallback_root=self.cwd, _env=self._env, **kw ) print(self.cwd.name, version, sep=": ") return version def create_basic_setup_py( self, name: str = "test-package", use_scm_version: str = "True" ) -> None: """Create a basic setup.py file with version configuration. Note: This is for setuptools_scm compatibility testing. """ self.write( "setup.py", f"""__import__('setuptools').setup( name="{name}", use_scm_version={use_scm_version}, )""", ) def create_basic_pyproject_toml( self, name: str = "test-package", dynamic_version: bool = True, tool_name: str = "vcs-versioning", ) -> None: """Create a basic pyproject.toml file with version configuration. Args: name: Project name dynamic_version: Whether to add dynamic=['version'] tool_name: Tool section name (e.g., 'vcs-versioning' or 'setuptools_scm') """ dynamic_section = 'dynamic = ["version"]' if dynamic_version else "" self.write( "pyproject.toml", f"""[build-system] requires = ["setuptools>=64", "setuptools_scm>=8"] build-backend = "setuptools.build_meta" [project] name = "{name}" {dynamic_section} [tool.{tool_name}] """, ) def create_basic_setup_cfg(self, name: str = "test-package") -> None: """Create a basic setup.cfg file with metadata.""" self.write( "setup.cfg", f"""[metadata] name = {name} """, ) def create_test_file( self, filename: str = "test.txt", content: str = "test content" ) -> None: """Create a test file and commit it to the repository.""" # Create parent directories if they don't exist path = self.cwd / filename path.parent.mkdir(parents=True, exist_ok=True) self.write(filename, content) self.add_and_commit() def create_tag(self, tag: str = "1.0.0") -> None: """Create a tag using the configured tag_command.""" if hasattr(self, "tag_command"): self(self.tag_command, tag=tag) else: raise RuntimeError("No tag_command configured") def configure_git_commands(self) -> None: """Configure git commands without initializing the repository.""" from vcs_versioning._backends._git import parse as git_parse self.add_command = "git add ." self.commit_command = "git commit -m test-{reason}" self.tag_command = "git tag {tag}" self.parse = git_parse def configure_hg_commands(self) -> None: """Configure mercurial commands without initializing the repository.""" from vcs_versioning._backends._hg import parse as hg_parse self.add_command = "hg add ." self.commit_command = 'hg commit -m test-{reason} -u test -d "0 0"' self.tag_command = "hg tag {tag}" self.parse = hg_parse def setup_git( self, monkeypatch: pytest.MonkeyPatch | None = None, *, init: bool = True ) -> WorkDir: """Set up git SCM for this WorkDir. Args: monkeypatch: Optional pytest MonkeyPatch to clear HOME environment init: Whether to initialize the git repository (default: True) Returns: Self for method chaining Raises: pytest.skip: If git executable is not found """ if not has_command("git", warn=False): pytest.skip("git executable not found") self.configure_git_commands() if init: if monkeypatch: monkeypatch.delenv("HOME", raising=False) self("git init") self("git config user.email test@example.com") self('git config user.name "a test"') return self def configure_jj_commands(self) -> None: """Configure jj commands without initializing the repository.""" from vcs_versioning._backends._jj import parse as jj_parse self.add_command = "jj file track ." self.commit_command = "jj commit -m test-{reason}" self.tag_command = "jj tag set {tag} -r @-" self.parse = jj_parse def setup_jj(self, *, init: bool = True) -> WorkDir: """Set up Jujutsu (jj) SCM for this WorkDir. Creates a colocated jj/git repo (``jj git init``). Args: init: Whether to initialize the jj repository (default: True) Returns: Self for method chaining Raises: pytest.skip: If jj executable is not found """ if not has_command("jj", args=["version"], warn=False): pytest.skip("jj executable not found") self.configure_jj_commands() if init: self("jj git init") self("jj config set --repo user.name 'a test'") self("jj config set --repo user.email test@example.com") return self def setup_hg(self, *, init: bool = True) -> WorkDir: """Set up mercurial SCM for this WorkDir. Args: init: Whether to initialize the mercurial repository (default: True) Returns: Self for method chaining Raises: pytest.skip: If hg executable is not found """ if not has_command("hg", warn=False): pytest.skip("hg executable not found") self.configure_hg_commands() if init: self("hg init") return self def expect_parse( self, **expectations: Unpack[VersionExpectations], ) -> None: """Parse version from this working directory and assert it matches expected properties. Uses the same signature as ScmVersion.matches() via TypedDict Unpack. """ __tracebackhide__ = True from vcs_versioning._config import Configuration if self.parse is None: raise RuntimeError( "No SCM configured - call setup_git() or setup_hg() first" ) config = Configuration(root=self.cwd) scm_version = self.parse(self.cwd, config) if scm_version is None: raise AssertionError("Failed to parse version") # Call matches with all expectations result = scm_version.matches(**expectations) # If result is mismatches (falsy), raise assertion with details if not result: raise AssertionError( f"Version mismatch:\n{result}\nActual version: {scm_version!r}" )