diff options
Diffstat (limited to 'src/setuptools_scm')
-rw-r--r-- | src/setuptools_scm/__init__.py | 2 | ||||
-rw-r--r-- | src/setuptools_scm/_config.py | 2 | ||||
-rw-r--r-- | src/setuptools_scm/_entrypoints.py | 2 | ||||
-rw-r--r-- | src/setuptools_scm/_overrides.py | 2 | ||||
-rw-r--r-- | src/setuptools_scm/_run_cmd.py | 96 | ||||
-rw-r--r-- | src/setuptools_scm/_trace.py | 26 | ||||
-rw-r--r-- | src/setuptools_scm/discover.py | 2 | ||||
-rw-r--r-- | src/setuptools_scm/file_finder.py | 2 | ||||
-rw-r--r-- | src/setuptools_scm/file_finder_git.py | 3 | ||||
-rw-r--r-- | src/setuptools_scm/file_finder_hg.py | 2 | ||||
-rw-r--r-- | src/setuptools_scm/git.py | 2 | ||||
-rw-r--r-- | src/setuptools_scm/hacks.py | 2 | ||||
-rw-r--r-- | src/setuptools_scm/hg.py | 2 | ||||
-rw-r--r-- | src/setuptools_scm/hg_git.py | 3 | ||||
-rw-r--r-- | src/setuptools_scm/integration.py | 9 | ||||
-rw-r--r-- | src/setuptools_scm/utils.py | 120 | ||||
-rw-r--r-- | src/setuptools_scm/version.py | 2 |
17 files changed, 148 insertions, 131 deletions
diff --git a/src/setuptools_scm/__init__.py b/src/setuptools_scm/__init__.py index d6def65..e15eeb1 100644 --- a/src/setuptools_scm/__init__.py +++ b/src/setuptools_scm/__init__.py @@ -49,7 +49,7 @@ def dump_version( target = os.path.normpath(os.path.join(root, write_to)) ext = os.path.splitext(target)[1] template = template or TEMPLATES.get(ext) - from .utils import trace + from ._trace import trace trace("dump", write_to, version) if template is None: diff --git a/src/setuptools_scm/_config.py b/src/setuptools_scm/_config.py index 84f7227..34745ae 100644 --- a/src/setuptools_scm/_config.py +++ b/src/setuptools_scm/_config.py @@ -15,10 +15,10 @@ from ._integration.pyproject_reading import ( ) from ._integration.pyproject_reading import read_pyproject as _read_pyproject from ._overrides import read_toml_overrides +from ._trace import trace from ._version_cls import _validate_version_cls from ._version_cls import _VersionT from ._version_cls import Version as _Version -from .utils import trace DEFAULT_TAG_REGEX = re.compile( r"^(?:[\w-]+-)?(?P<version>[vV]?\d+(?:\.\d+){0,2}[^\+]*)(?:\+.*)?$" diff --git a/src/setuptools_scm/_entrypoints.py b/src/setuptools_scm/_entrypoints.py index 0b0b32c..9b5b093 100644 --- a/src/setuptools_scm/_entrypoints.py +++ b/src/setuptools_scm/_entrypoints.py @@ -9,7 +9,7 @@ from typing import overload from typing import TYPE_CHECKING from . import version -from .utils import trace +from ._trace import trace if TYPE_CHECKING: from ._config import Configuration diff --git a/src/setuptools_scm/_overrides.py b/src/setuptools_scm/_overrides.py index 9040487..f08f170 100644 --- a/src/setuptools_scm/_overrides.py +++ b/src/setuptools_scm/_overrides.py @@ -6,7 +6,7 @@ from typing import Any from . import _config from . import version from ._integration.pyproject_reading import lazy_toml_load -from .utils import trace +from ._trace import trace PRETEND_KEY = "SETUPTOOLS_SCM_PRETEND_VERSION" PRETEND_KEY_NAMED = PRETEND_KEY + "_FOR_{name}" diff --git a/src/setuptools_scm/_run_cmd.py b/src/setuptools_scm/_run_cmd.py new file mode 100644 index 0000000..83b0b19 --- /dev/null +++ b/src/setuptools_scm/_run_cmd.py @@ -0,0 +1,96 @@ +from __future__ import annotations + +import os +import shlex +import subprocess +from typing import Mapping + +from . import _trace +from . import _types as _t + + +def no_git_env(env: Mapping[str, str]) -> dict[str, str]: + # adapted from pre-commit + # Too many bugs dealing with environment variables and GIT: + # https://github.com/pre-commit/pre-commit/issues/300 + # In git 2.6.3 (maybe others), git exports GIT_WORK_TREE while running + # pre-commit hooks + # In git 1.9.1 (maybe others), git exports GIT_DIR and GIT_INDEX_FILE + # while running pre-commit hooks in submodules. + # GIT_DIR: Causes git clone to clone wrong thing + # GIT_INDEX_FILE: Causes 'error invalid object ...' during commit + for k, v in env.items(): + if k.startswith("GIT_"): + _trace.trace(k, v) + return { + k: v + for k, v in env.items() + if not k.startswith("GIT_") + or k in ("GIT_EXEC_PATH", "GIT_SSH", "GIT_SSH_COMMAND") + } + + +def avoid_pip_isolation(env: Mapping[str, str]) -> dict[str, str]: + """ + pip build isolation can break Mercurial + (see https://github.com/pypa/pip/issues/10635) + + pip uses PYTHONNOUSERSITE and a path in PYTHONPATH containing "pip-build-env-". + """ + new_env = {k: v for k, v in env.items() if k != "PYTHONNOUSERSITE"} + if "PYTHONPATH" not in new_env: + return new_env + + new_env["PYTHONPATH"] = os.pathsep.join( + [ + path + for path in new_env["PYTHONPATH"].split(os.pathsep) + if "pip-build-env-" not in path + ] + ) + return new_env + + +def ensure_stripped_str(str_or_bytes: str | bytes) -> str: + if isinstance(str_or_bytes, str): + return str_or_bytes.strip() + else: + return str_or_bytes.decode("utf-8", "surrogateescape").strip() + + +def run( + cmd: _t.CMD_TYPE, + cwd: _t.PathT, + strip: bool = True, + trace: bool = True, +) -> subprocess.CompletedProcess[str]: + if isinstance(cmd, str): + cmd = shlex.split(cmd) + if trace: + _trace.trace_command(cmd, cwd) + res = subprocess.run( + cmd, + capture_output=True, + cwd=str(cwd), + env=dict( + avoid_pip_isolation(no_git_env(os.environ)), + # os.environ, + # try to disable i18n + LC_ALL="C", + LANGUAGE="", + HGPLAIN="1", + ), + text=True, + ) + if strip: + if res.stdout: + res.stdout = ensure_stripped_str(res.stdout) + res.stderr = ensure_stripped_str(res.stderr) + if trace: + if res.stdout: + _trace.trace("out:\n", res.stdout, indent=True) + if res.stderr: + _trace.trace("err:\n", res.stderr, indent=True) + if res.returncode: + _trace.trace("ret:", res.returncode) + return res diff --git a/src/setuptools_scm/_trace.py b/src/setuptools_scm/_trace.py new file mode 100644 index 0000000..01383a5 --- /dev/null +++ b/src/setuptools_scm/_trace.py @@ -0,0 +1,26 @@ +from __future__ import annotations + +import os +import sys +import textwrap + +from . import _types as _t + +DEBUG: bool = bool(os.environ.get("SETUPTOOLS_SCM_DEBUG")) + + +def trace(*k: object, indent: bool = False) -> None: + if not DEBUG: + if indent and len(k) > 1: + k = (k[0],) + tuple(textwrap.indent(str(s), " ") for s in k[1:]) + print(*k, file=sys.stderr, flush=True) + + +def trace_command(cmd: _t.CMD_TYPE, cwd: _t.PathT) -> None: + if not DEBUG: + return + # give better results than shlex.join in our cases + cmd_4_trace = " ".join( + [s if all(c not in s for c in " {[:") else f'"{s}"' for s in cmd] + ) + trace(f"---\n > {cwd}\\$ ", cmd_4_trace, indent=True) diff --git a/src/setuptools_scm/discover.py b/src/setuptools_scm/discover.py index 58384aa..2bd01e9 100644 --- a/src/setuptools_scm/discover.py +++ b/src/setuptools_scm/discover.py @@ -6,7 +6,7 @@ from typing import Iterator from . import _types as _t from ._config import Configuration -from .utils import trace +from ._trace import trace def walk_potential_roots( diff --git a/src/setuptools_scm/file_finder.py b/src/setuptools_scm/file_finder.py index 27196c0..27a50d7 100644 --- a/src/setuptools_scm/file_finder.py +++ b/src/setuptools_scm/file_finder.py @@ -5,7 +5,7 @@ import os from typing_extensions import TypeGuard from . import _types as _t -from .utils import trace +from ._trace import trace def scm_find_files( diff --git a/src/setuptools_scm/file_finder_git.py b/src/setuptools_scm/file_finder_git.py index 65aa999..c16cff6 100644 --- a/src/setuptools_scm/file_finder_git.py +++ b/src/setuptools_scm/file_finder_git.py @@ -7,12 +7,11 @@ import tarfile from typing import IO from . import _types as _t +from ._trace import trace from .file_finder import is_toplevel_acceptable from .file_finder import scm_find_files from .utils import data_from_mime from .utils import do_ex -from .utils import trace - log = logging.getLogger(__name__) diff --git a/src/setuptools_scm/file_finder_hg.py b/src/setuptools_scm/file_finder_hg.py index b750fea..30f28b9 100644 --- a/src/setuptools_scm/file_finder_hg.py +++ b/src/setuptools_scm/file_finder_hg.py @@ -4,11 +4,11 @@ import os import subprocess from . import _types as _t +from ._trace import trace from .file_finder import is_toplevel_acceptable from .file_finder import scm_find_files from .utils import data_from_mime from .utils import do_ex -from .utils import trace def _hg_toplevel(path: str) -> str | None: diff --git a/src/setuptools_scm/git.py b/src/setuptools_scm/git.py index 1859671..484bd4d 100644 --- a/src/setuptools_scm/git.py +++ b/src/setuptools_scm/git.py @@ -13,12 +13,12 @@ from typing import TYPE_CHECKING from . import _types as _t from . import Configuration +from ._trace import trace from .scm_workdir import Workdir from .utils import _CmdResult from .utils import data_from_mime from .utils import do_ex from .utils import require_command -from .utils import trace from .version import meta from .version import ScmVersion from .version import tag_to_version diff --git a/src/setuptools_scm/hacks.py b/src/setuptools_scm/hacks.py index 1ddcdfa..5aaa0db 100644 --- a/src/setuptools_scm/hacks.py +++ b/src/setuptools_scm/hacks.py @@ -7,7 +7,7 @@ if TYPE_CHECKING: from . import _types as _t from . import Configuration from .utils import data_from_mime -from .utils import trace +from ._trace import trace from .version import meta from .version import ScmVersion from .version import tag_to_version diff --git a/src/setuptools_scm/hg.py b/src/setuptools_scm/hg.py index a6f5725..c325d0f 100644 --- a/src/setuptools_scm/hg.py +++ b/src/setuptools_scm/hg.py @@ -6,12 +6,12 @@ from pathlib import Path from typing import TYPE_CHECKING from . import Configuration +from ._trace import trace from ._version_cls import Version from .scm_workdir import Workdir from .utils import data_from_mime from .utils import do_ex from .utils import require_command -from .utils import trace from .version import meta from .version import ScmVersion from .version import tag_to_version diff --git a/src/setuptools_scm/hg_git.py b/src/setuptools_scm/hg_git.py index bde71ec..f7ea883 100644 --- a/src/setuptools_scm/hg_git.py +++ b/src/setuptools_scm/hg_git.py @@ -6,13 +6,12 @@ from datetime import date from datetime import datetime from . import _types as _t +from ._trace import trace from .git import GitWorkdir from .hg import HgWorkdir from .utils import _CmdResult from .utils import do_ex from .utils import require_command -from .utils import trace - _FAKE_GIT_DESCRIBE_ERROR = _CmdResult("<>hg git failed", "", 1) diff --git a/src/setuptools_scm/integration.py b/src/setuptools_scm/integration.py index e9c6c12..e2a033e 100644 --- a/src/setuptools_scm/integration.py +++ b/src/setuptools_scm/integration.py @@ -16,9 +16,8 @@ from ._entrypoints import iter_entry_points from ._integration.setuptools import ( read_dist_name_from_setup_cfg as _read_dist_name_from_setup_cfg, ) +from ._trace import trace from ._version_cls import _validate_version_cls -from .utils import do -from .utils import trace if TYPE_CHECKING: from . import _types as _t @@ -106,11 +105,7 @@ def find_files(path: _t.PathT = "") -> list[str]: iter_entry_points("setuptools_scm.files_command_fallback"), ): command = ep.load() - if isinstance(command, str): - # this technique is deprecated - res = do(ep.load(), path or ".").splitlines() - else: - res = command(path) + res: list[str] = command(path) if res: return res return [] diff --git a/src/setuptools_scm/utils.py b/src/setuptools_scm/utils.py index 52aa5e2..2be1654 100644 --- a/src/setuptools_scm/utils.py +++ b/src/setuptools_scm/utils.py @@ -3,26 +3,20 @@ utils """ from __future__ import annotations -import os -import platform -import shlex -import subprocess import sys -import textwrap import warnings from types import CodeType from types import FunctionType from typing import Iterator -from typing import Mapping from typing import NamedTuple from typing import TYPE_CHECKING +from . import _run_cmd +from . import _trace + if TYPE_CHECKING: from . import _types as _t -DEBUG = bool(os.environ.get("SETUPTOOLS_SCM_DEBUG")) -IS_WINDOWS = platform.system() == "Windows" - class _CmdResult(NamedTuple): out: str @@ -30,107 +24,15 @@ class _CmdResult(NamedTuple): returncode: int -def no_git_env(env: Mapping[str, str]) -> dict[str, str]: - # adapted from pre-commit - # Too many bugs dealing with environment variables and GIT: - # https://github.com/pre-commit/pre-commit/issues/300 - # In git 2.6.3 (maybe others), git exports GIT_WORK_TREE while running - # pre-commit hooks - # In git 1.9.1 (maybe others), git exports GIT_DIR and GIT_INDEX_FILE - # while running pre-commit hooks in submodules. - # GIT_DIR: Causes git clone to clone wrong thing - # GIT_INDEX_FILE: Causes 'error invalid object ...' during commit - for k, v in env.items(): - if k.startswith("GIT_"): - trace(k, v) - return { - k: v - for k, v in env.items() - if not k.startswith("GIT_") - or k in ("GIT_EXEC_PATH", "GIT_SSH", "GIT_SSH_COMMAND") - } - - -def avoid_pip_isolation(env: Mapping[str, str]) -> dict[str, str]: - """ - pip build isolation can break Mercurial - (see https://github.com/pypa/pip/issues/10635) - - pip uses PYTHONNOUSERSITE and a path in PYTHONPATH containing "pip-build-env-". - """ - new_env = {k: v for k, v in env.items() if k != "PYTHONNOUSERSITE"} - if "PYTHONPATH" not in new_env: - return new_env - - new_env["PYTHONPATH"] = os.pathsep.join( - [ - path - for path in new_env["PYTHONPATH"].split(os.pathsep) - if "pip-build-env-" not in path - ] - ) - return new_env - - -def trace(*k: object, indent: bool = False) -> None: - if DEBUG: - if indent and len(k) > 1: - k = (k[0],) + tuple(textwrap.indent(str(s), " ") for s in k[1:]) - print(*k, file=sys.stderr, flush=True) - - -def ensure_stripped_str(str_or_bytes: str | bytes) -> str: - if isinstance(str_or_bytes, str): - return str_or_bytes.strip() - else: - return str_or_bytes.decode("utf-8", "surrogateescape").strip() - - -def _run(cmd: _t.CMD_TYPE, cwd: _t.PathT) -> subprocess.CompletedProcess[str]: - return subprocess.run( - cmd, - capture_output=True, - cwd=str(cwd), - env=dict( - avoid_pip_isolation(no_git_env(os.environ)), - # os.environ, - # try to disable i18n - LC_ALL="C", - LANGUAGE="", - HGPLAIN="1", - ), - text=True, - ) - - def do_ex(cmd: _t.CMD_TYPE, cwd: _t.PathT = ".") -> _CmdResult: - if not DEBUG or not isinstance(cmd, list): - cmd_4_trace = cmd - else: - # give better results than shlex.join in our cases - cmd_4_trace = " ".join( - [s if all(c not in s for c in " {[:") else f'"{s}"' for s in cmd] - ) - trace("----\ncmd:\n", cmd_4_trace, indent=True) - trace(" in:", cwd) - if os.name == "posix" and not isinstance(cmd, (list, tuple)): - cmd = shlex.split(cmd) - - res = _run(cmd, cwd) - if res.stdout: - trace("out:\n", res.stdout, indent=True) - if res.stderr: - trace("err:\n", res.stderr, indent=True) - if res.returncode: - trace("ret:", res.returncode) - return _CmdResult( - ensure_stripped_str(res.stdout), ensure_stripped_str(res.stderr), res.returncode - ) + res = _run_cmd.run(cmd, cwd) + + return _CmdResult(res.stdout, res.stderr, res.returncode) def do(cmd: list[str] | str, cwd: str | _t.PathT = ".") -> str: out, err, ret = do_ex(cmd, cwd) - if ret and not DEBUG: + if ret and not _trace.DEBUG: print(err) return out @@ -138,10 +40,10 @@ def do(cmd: list[str] | str, cwd: str | _t.PathT = ".") -> str: def data_from_mime(path: _t.PathT) -> dict[str, str]: with open(path, encoding="utf-8") as fp: content = fp.read() - trace("content", repr(content)) + _trace.trace("content", repr(content)) # the complex conditions come from reading pseudo-mime-messages data = dict(x.split(": ", 1) for x in content.splitlines() if ": " in x) - trace("data", data) + _trace.trace("data", data) return data @@ -154,9 +56,9 @@ def function_has_arg(fn: object | FunctionType, argname: str) -> bool: def has_command(name: str, args: list[str] | None = None, warn: bool = True) -> bool: try: cmd = [name, "help"] if args is None else [name, *args] - p = _run(cmd, ".") + p = _run_cmd.run(cmd, ".") except OSError: - trace(*sys.exc_info()) + _trace.trace(*sys.exc_info()) res = False else: res = not p.returncode diff --git a/src/setuptools_scm/version.py b/src/setuptools_scm/version.py index 7c6abd3..36bc7df 100644 --- a/src/setuptools_scm/version.py +++ b/src/setuptools_scm/version.py @@ -28,7 +28,7 @@ if TYPE_CHECKING: from ._version_cls import Version as PkgVersion, _VersionT from . import _version_cls as _v from . import _config -from .utils import trace +from ._trace import trace SEMVER_MINOR = 2 SEMVER_PATCH = 3 |