diff --git a/.cruft.json b/.cruft.json index f20aa9cc..ee539d41 100644 --- a/.cruft.json +++ b/.cruft.json @@ -1,6 +1,6 @@ { "template": "https://github.com/iterative/py-template", - "commit": "15ee26df315020399731c6291d61bef81a3fc5d3", + "commit": "454eb9a60092e980778877f1248072b45a92bb74", "context": { "cookiecutter": { "project_name": "scmrepo", diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 56f6f984..393a9449 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -7,12 +7,6 @@ updates: interval: "weekly" labels: - "maintenance" - # Update via cruft - ignore: - - dependency-name: "mkdocs*" - - dependency-name: "pytest*" - - dependency-name: "pylint" - - dependency-name: "mypy" - directory: "/" package-ecosystem: "github-actions" @@ -20,10 +14,3 @@ updates: interval: "weekly" labels: - "maintenance" - # Update via cruft - ignore: - - dependency-name: "actions/checkout" - - dependency-name: "actions/setup-python" - - dependency-name: "pypa/gh-action-pypi-publish" - - dependency-name: "codecov/codecov-action" - - dependency-name: "peter-evans/create-pull-request" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 85287510..6a4e0723 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ default_language_version: python: python3 repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: check-added-large-files - id: check-case-conflict @@ -14,25 +14,19 @@ repos: exclude: "README.rst" - id: check-toml - id: check-yaml - - id: debug-statements - id: end-of-file-fixer - id: mixed-line-ending args: ['--fix=lf'] - id: sort-simple-yaml - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit - rev: 'v0.1.5' + rev: 'v0.1.13' hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] - id: ruff-format - repo: https://github.com/codespell-project/codespell - rev: v2.2.5 + rev: v2.2.6 hooks: - id: codespell additional_dependencies: ["tomli"] - - repo: https://github.com/asottile/pyupgrade - rev: v3.10.1 - hooks: - - id: pyupgrade - args: [--py38-plus] diff --git a/pyproject.toml b/pyproject.toml index 27aee8a6..17b2c70e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -124,3 +124,81 @@ ignore_missing_imports = true [tool.codespell] ignore-words-list = "cachable, keypair" + +[tool.ruff] +ignore = [ + "S101", # assert + "PLR2004", # magic-value-comparison + "PLW2901", # redefined-loop-name + "ISC001", # single-line-implicit-string-concatenation + "SIM105", # suppressible-exception + "SIM108", # if-else-block-instead-of-if-exp + "D203", # one blank line before class + "D213", # multi-line-summary-second-line + "RET501", # unnecessary-return-none + "RET502", # implicit-return-value + "RET503", # implicit-return + "SIM117", # multiple-with-statements + "N818", # error-suffix-on-exception-name +] +select = [ + "A", # flake8-buitlins + "ASYNC", # flake8-async + "B", # flake8-bugbear + "BLE", # flake8-blind-except + "C4", # flake8-comprehensions + "C90", # mccabe + "DTZ", # flake8-datetimez + "E", # pycodestyle - Error + "EXE", # flake8-executable + "F", # pyflakes + "FLY", # flynt-rules + "G", # flake8-logging-format + "I", # isort + "ICN", # flake8-import-conventions + "INP", # flake8-no-pep420 + "ISC", # flake8-implicit-str-concat + "N", # pep8-naming + "PERF101", # perflint + "PGH", # pygrep-hooks + "PIE", # flake8-pie + "PL", # pylint + "PT", # flake8-pytest-style + "PYI", # flake8-pyi + "Q", # flae8-quotes + "RET", # flake8-return + "RSE", # flake8-raise + "RUF", # ruff + "S", # flake8-bandit + "SIM", # flake8-simplify + "SLOT", # flake8-slots + "T10", # flake8-debugger + "T20", # flake8-print + "TCH", # flake8-type-checking + "TCH", # flake8-type-checking + "TID", # flake8-tidy-imports + "UP", # pyupgrade + "W", # pycodestyle - Warning + "YTT", # flake8-2020 +] +show-source = true +show-fixes = true + +[tool.ruff.per-file-ignores] +"noxfile.py" = ["D", "PTH"] +"tests/**" = ["S", "ARG001", "ARG002", "ANN"] +"docs/**" = ["INP"] + +[tool.ruff.lint.flake8-pytest-style] +fixture-parentheses = false +mark-parentheses = false +parametrize-names-type = "csv" + +[tool.ruff.lint.flake8-type-checking] +strict = true + +[tool.ruff.lint.isort] +known-first-party = ["scmrepo"] + +[tool.ruff.pylint] +max-args = 10 diff --git a/src/scmrepo/base.py b/src/scmrepo/base.py index 8c6418d6..5c5df7ee 100644 --- a/src/scmrepo/base.py +++ b/src/scmrepo/base.py @@ -15,9 +15,7 @@ def root_dir(self) -> str: return self._root_dir def __repr__(self): - return "{class_name}: '{directory}'".format( - class_name=type(self).__name__, directory=self.dir - ) + return f"{type(self).__name__}: '{self.dir}'" def __exit__(self, exc_type, exc_value, traceback): self.close() diff --git a/src/scmrepo/fs.py b/src/scmrepo/fs.py index 6a24666a..3e76b586 100644 --- a/src/scmrepo/fs.py +++ b/src/scmrepo/fs.py @@ -31,11 +31,11 @@ class GitFileSystem(AbstractFileSystem): def __init__( self, - path: str = None, - rev: str = None, + path: Optional[str] = None, + rev: Optional[str] = None, scm: "Git" = None, trie: "GitTrie" = None, - rev_resolver: Callable[["Git", str], str] = None, + rev_resolver: Optional[Callable[["Git", str], str]] = None, **kwargs, ): from scmrepo.git import Git @@ -213,8 +213,10 @@ def info(self, path: str, **kwargs: Any) -> Dict[str, Any]: ret = self.trie.info(key) ret["name"] = path return ret - except KeyError: - raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), path) + except KeyError as exc: + raise FileNotFoundError( + errno.ENOENT, os.strerror(errno.ENOENT), path + ) from exc def exists(self, path: str, **kwargs: Any) -> bool: key = self._get_key(path) @@ -255,7 +257,7 @@ def get_file( with self.open(rpath, "rb", **kwargs) as f1: if outfile is None: - outfile = open(lpath, "wb") + outfile = open(lpath, "wb") # noqa: SIM115 try: callback.set_size(getattr(f1, "size", None)) diff --git a/src/scmrepo/git/__init__.py b/src/scmrepo/git/__init__.py index b828144a..b88b1948 100644 --- a/src/scmrepo/git/__init__.py +++ b/src/scmrepo/git/__init__.py @@ -8,7 +8,17 @@ from collections.abc import Mapping from contextlib import contextmanager from functools import partialmethod -from typing import TYPE_CHECKING, Callable, Dict, Iterable, Optional, Tuple, Type, Union +from typing import ( + TYPE_CHECKING, + Callable, + ClassVar, + Dict, + Iterable, + Optional, + Tuple, + Type, + Union, +) from funcy import cached_property, first from pathspec.patterns import GitWildMatchPattern @@ -41,7 +51,7 @@ class GitBackends(Mapping): - DEFAULT: Dict[str, BackendCls] = { + DEFAULT: ClassVar[Dict[str, BackendCls]] = { "dulwich": DulwichBackend, "pygit2": Pygit2Backend, "gitpython": GitPythonBackend, @@ -312,7 +322,9 @@ def get_fs(self, rev: str): return GitFileSystem(scm=self, rev=rev) @classmethod - def init(cls, path: str, bare: bool = False, _backend: str = None) -> "Git": + def init( + cls, path: str, bare: bool = False, _backend: Optional[str] = None + ) -> "Git": for name, backend in GitBackends.DEFAULT.items(): if _backend and name != _backend: continue diff --git a/src/scmrepo/git/backend/base.py b/src/scmrepo/git/backend/base.py index 124b6399..5942f011 100644 --- a/src/scmrepo/git/backend/base.py +++ b/src/scmrepo/git/backend/base.py @@ -4,15 +4,13 @@ from typing import TYPE_CHECKING, Callable, Iterable, Mapping, Optional, Tuple, Union from scmrepo.exceptions import SCMError - -from ..objects import GitObject +from scmrepo.git.objects import GitObject if TYPE_CHECKING: + from scmrepo.git.config import Config + from scmrepo.git.objects import GitCommit, GitTag from scmrepo.progress import GitProgressEvent - from ..config import Config - from ..objects import GitCommit, GitTag - class NoGitBackendError(SCMError): def __init__(self, func): @@ -50,7 +48,7 @@ def clone( url: str, to_path: str, shallow_branch: Optional[str] = None, - progress: Callable[["GitProgressEvent"], None] = None, + progress: Optional[Callable[["GitProgressEvent"], None]] = None, bare: bool = False, mirror: bool = False, ): @@ -229,7 +227,7 @@ def push_refspecs( refspecs: Union[str, Iterable[str]], force: bool = False, on_diverged: Optional[Callable[[str, str], bool]] = None, - progress: Callable[["GitProgressEvent"], None] = None, + progress: Optional[Callable[["GitProgressEvent"], None]] = None, **kwargs, ) -> Mapping[str, SyncStatus]: """Push refspec to a remote Git repo. @@ -253,7 +251,7 @@ def fetch_refspecs( refspecs: Union[str, Iterable[str]], force: bool = False, on_diverged: Optional[Callable[[str, str], bool]] = None, - progress: Callable[["GitProgressEvent"], None] = None, + progress: Optional[Callable[["GitProgressEvent"], None]] = None, **kwargs, ) -> Mapping[str, SyncStatus]: """Fetch refspecs from a remote Git repo. @@ -333,7 +331,7 @@ def diff(self, rev_a: str, rev_b: str, binary=False) -> str: """Return the git diff for two commits.""" @abstractmethod - def reset(self, hard: bool = False, paths: Iterable[str] = None): + def reset(self, hard: bool = False, paths: Optional[Iterable[str]] = None): """Reset current git HEAD.""" @abstractmethod diff --git a/src/scmrepo/git/backend/dulwich/__init__.py b/src/scmrepo/git/backend/dulwich/__init__.py index 68f1e375..df52b893 100644 --- a/src/scmrepo/git/backend/dulwich/__init__.py +++ b/src/scmrepo/git/backend/dulwich/__init__.py @@ -24,22 +24,20 @@ from funcy import cached_property, reraise from scmrepo.exceptions import AuthError, CloneError, InvalidRemote, RevError, SCMError +from scmrepo.git.backend.base import BaseGitBackend, SyncStatus +from scmrepo.git.config import Config +from scmrepo.git.objects import GitObject, GitTag from scmrepo.progress import GitProgressReporter from scmrepo.utils import relpath -from ...config import Config -from ...objects import GitObject, GitTag -from ..base import BaseGitBackend, SyncStatus - if TYPE_CHECKING: from dulwich.client import SSHVendor from dulwich.config import ConfigFile, StackedConfig from dulwich.repo import Repo + from scmrepo.git.objects import GitCommit from scmrepo.progress import GitProgressEvent - from ...objects import GitCommit - logger = logging.getLogger(__name__) @@ -82,7 +80,7 @@ def mode(self) -> int: def scandir(self) -> Iterable["DulwichObject"]: tree = self.repo[self._sha] - for entry in tree.iteritems(): # noqa: B301 + for entry in tree.iteritems(): yield DulwichObject(self.repo, entry.path.decode(), entry.mode, entry.sha) @cached_property @@ -232,7 +230,7 @@ def clone( url: str, to_path: str, shallow_branch: Optional[str] = None, - progress: Callable[["GitProgressEvent"], None] = None, + progress: Optional[Callable[["GitProgressEvent"], None]] = None, bare: bool = False, mirror: bool = False, ): @@ -271,7 +269,7 @@ def clone( cls._set_mirror(repo, progress=progress) else: cls._set_default_tracking_branch(repo) - except Exception as exc: + except Exception as exc: # noqa: BLE001 raise CloneError(url, to_path) from exc @staticmethod @@ -291,7 +289,7 @@ def _set_default_tracking_branch(repo: "Repo"): @staticmethod def _set_mirror( - repo: "Repo", progress: Callable[["GitProgressEvent"], None] = None + repo: "Repo", progress: Optional[Callable[["GitProgressEvent"], None]] = None ): from dulwich.porcelain import NoneStream, fetch @@ -458,10 +456,7 @@ def untracked_files(self) -> Iterable[str]: def is_tracked(self, path: str) -> bool: rel = relpath(path, self.root_dir).replace(os.path.sep, "/").encode() rel_dir = rel + b"/" - for p in self.repo.open_index(): - if p == rel or p.startswith(rel_dir): - return True - return False + return any(p == rel or p.startswith(rel_dir) for p in self.repo.open_index()) def is_dirty(self, untracked_files: bool = False) -> bool: kwargs: Dict[str, Any] = {} if untracked_files else {"untracked_files": "no"} @@ -590,7 +585,7 @@ def iter_remote_refs(self, url: str, base: Optional[str] = None, **kwargs): try: _remote, location = get_remote_repo(self.repo, url) client, path = get_transport_and_path(location, **kwargs) - except Exception as exc: + except Exception as exc: # noqa: BLE001 raise InvalidRemote(url) from exc try: @@ -616,7 +611,7 @@ def push_refspecs( # noqa: C901 refspecs: Union[str, Iterable[str]], force: bool = False, on_diverged: Optional[Callable[[str, str], bool]] = None, - progress: Callable[["GitProgressEvent"], None] = None, + progress: Optional[Callable[["GitProgressEvent"], None]] = None, **kwargs, ) -> Mapping[str, SyncStatus]: from dulwich.client import HTTPUnauthorized, get_transport_and_path @@ -627,7 +622,7 @@ def push_refspecs( # noqa: C901 try: _remote, location = get_remote_repo(self.repo, url) client, path = get_transport_and_path(location, **kwargs) - except Exception as exc: + except Exception as exc: # noqa: BLE001 raise SCMError(f"'{url}' is not a valid Git remote or URL") from exc change_result = {} @@ -700,7 +695,7 @@ def fetch_refspecs( refspecs: Union[str, Iterable[str]], force: bool = False, on_diverged: Optional[Callable[[str, str], bool]] = None, - progress: Callable[["GitProgressEvent"], None] = None, + progress: Optional[Callable[["GitProgressEvent"], None]] = None, **kwargs, ) -> Mapping[str, SyncStatus]: from dulwich.client import get_transport_and_path @@ -868,7 +863,7 @@ def diff(self, rev_a: str, rev_b: str, binary=False) -> str: write_tree_diff(buf, self.repo.object_store, commit_a.tree, commit_b.tree) return buf.getvalue().decode("utf-8") - def reset(self, hard: bool = False, paths: Iterable[str] = None): + def reset(self, hard: bool = False, paths: Optional[Iterable[str]] = None): raise NotImplementedError def checkout_index( @@ -920,7 +915,7 @@ def validate_git_remote(self, url: str, **kwargs): try: _, location = get_remote_repo(self.repo, url) client, path = get_transport_and_path(location, **kwargs) - except Exception as exc: + except Exception as exc: # noqa: BLE001 raise InvalidRemote(url) from exc if isinstance(client, LocalGitClient) and not os.path.exists( os.path.join("", path) diff --git a/src/scmrepo/git/backend/dulwich/asyncssh_vendor.py b/src/scmrepo/git/backend/dulwich/asyncssh_vendor.py index 978905ce..c176abc1 100644 --- a/src/scmrepo/git/backend/dulwich/asyncssh_vendor.py +++ b/src/scmrepo/git/backend/dulwich/asyncssh_vendor.py @@ -129,7 +129,7 @@ def _process_public_key_ok_gh(self, _pkttype, _pktid, packet): b"rsa-sha2-512", ) ) - or (algorithm != b"ssh-rsa" and algorithm != self._keypair.algorithm) + or (algorithm not in (b"ssh-rsa", self._keypair.algorithm)) or key_data != self._keypair.public_data ): raise ProtocolError("Key mismatch") @@ -141,7 +141,11 @@ def _process_public_key_ok_gh(self, _pkttype, _pktid, packet): class InteractiveSSHClient(SSHClient): _conn: Optional["SSHClientConnection"] = None _keys_to_try: Optional[List["FilePath"]] = None - _passphrases: Dict[str, str] = {} + _passphrases: Dict[str, str] + + def __init__(self, *args, **kwargs): + super(*args, **kwargs) + _passphrases: Dict[str, str] = {} def connection_made(self, conn: "SSHClientConnection") -> None: self._conn = conn @@ -150,7 +154,7 @@ def connection_made(self, conn: "SSHClientConnection") -> None: def connection_lost(self, exc: Optional[Exception]) -> None: self._conn = None - async def public_key_auth_requested( # pylint: disable=invalid-overridden-method + async def public_key_auth_requested( # noqa: C901, PLR0912 self, ) -> Optional["KeyPairListArg"]: from asyncssh.public_key import ( @@ -246,7 +250,7 @@ def _getpass(prompt: str) -> str: return getpass(prompt=prompt).rstrip() if instructions: - print(instructions) + pass loop = asyncio.get_running_loop() return [ await loop.run_in_executor( diff --git a/src/scmrepo/git/backend/gitpython.py b/src/scmrepo/git/backend/gitpython.py index 25855860..1626459b 100644 --- a/src/scmrepo/git/backend/gitpython.py +++ b/src/scmrepo/git/backend/gitpython.py @@ -28,16 +28,15 @@ SCMError, UnsupportedIndexFormat, ) +from scmrepo.git.objects import GitCommit, GitObject, GitTag from scmrepo.utils import relpath -from ..objects import GitCommit, GitObject, GitTag from .base import BaseGitBackend, SyncStatus if TYPE_CHECKING: + from scmrepo.git.config import Config from scmrepo.progress import GitProgressEvent - from ..config import Config - logger = logging.getLogger(__name__) @@ -64,7 +63,7 @@ def is_binary() -> bool: return getattr(sys, "frozen", False) -def fix_env(env: Dict[str, str] = None) -> Dict[str, str]: +def fix_env(env: Optional[Dict[str, str]] = None) -> Dict[str, str]: if env is None: environ = os.environ.copy() else: @@ -87,7 +86,7 @@ def __init__(self, obj): def open( self, mode: str = "r", - encoding: str = None, + encoding: Optional[str] = None, raw: bool = True, **kwargs, ): @@ -142,9 +141,9 @@ def __init__( # pylint:disable=W0231 self.repo = git.Repo( root_dir, search_parent_directories=search_parent_directories ) - except InvalidGitRepositoryError: + except InvalidGitRepositoryError as exc: msg = "{} is not a git repository" - raise SCMError(msg.format(root_dir)) + raise SCMError(msg.format(root_dir)) from exc # NOTE: fixing LD_LIBRARY_PATH for binary built by PyInstaller. # http://pyinstaller.readthedocs.io/en/stable/runtime-information.html @@ -175,7 +174,7 @@ def clone( url: str, to_path: str, shallow_branch: Optional[str] = None, - progress: Callable[["GitProgressEvent"], None] = None, + progress: Optional[Callable[["GitProgressEvent"], None]] = None, bare: bool = False, mirror: bool = False, ): @@ -185,7 +184,7 @@ def clone( ld_key = "LD_LIBRARY_PATH" env = fix_env() - if is_binary() and ld_key not in env.keys(): + if is_binary() and ld_key not in env: # In fix_env, we delete LD_LIBRARY_PATH key if it was empty before # PyInstaller modified it. GitPython, in git.Repo.clone_from, uses # env to update its own internal state. When there is no key in @@ -398,8 +397,8 @@ def _resolve_rev(name): except NotImplementedError: # Fall back to `git rev-parse` for advanced features return self.repo.git.rev_parse(name) - except ValueError: - raise RevError(f"unknown Git revision '{name}'") + except ValueError as exc: + raise RevError(f"unknown Git revision '{name}'") from exc # Resolve across local names sha = _resolve_rev(rev) @@ -473,7 +472,7 @@ def set_ref( except GitCommandError as exc: raise SCMError(f"Failed to set ref '{name}'") from exc - def get_ref(self, name: str, follow: bool = True) -> Optional[str]: + def get_ref(self, name: str, follow: bool = True) -> Optional[str]: # noqa: C901, PLR0911, PLR0912 from git.exc import GitCommandError if name == "HEAD": @@ -550,7 +549,7 @@ def push_refspecs( refspecs: Union[str, Iterable[str]], force: bool = False, on_diverged: Optional[Callable[[str, str], bool]] = None, - progress: Callable[["GitProgressEvent"], None] = None, + progress: Optional[Callable[["GitProgressEvent"], None]] = None, **kwargs, ) -> Mapping[str, SyncStatus]: raise NotImplementedError @@ -561,7 +560,7 @@ def fetch_refspecs( refspecs: Union[str, Iterable[str]], force: bool = False, on_diverged: Optional[Callable[[str, str], bool]] = None, - progress: Callable[["GitProgressEvent"], None] = None, + progress: Optional[Callable[["GitProgressEvent"], None]] = None, **kwargs, ) -> Mapping[str, SyncStatus]: raise NotImplementedError @@ -650,7 +649,7 @@ def _describe( def diff(self, rev_a: str, rev_b: str, binary=False) -> str: raise NotImplementedError - def reset(self, hard: bool = False, paths: Iterable[str] = None): + def reset(self, hard: bool = False, paths: Optional[Iterable[str]] = None): if paths: paths_list: Optional[List[str]] = [ relpath(path, self.root_dir) for path in paths diff --git a/src/scmrepo/git/backend/pygit2/__init__.py b/src/scmrepo/git/backend/pygit2/__init__.py index 8a3b0e2c..f7ef7ae3 100644 --- a/src/scmrepo/git/backend/pygit2/__init__.py +++ b/src/scmrepo/git/backend/pygit2/__init__.py @@ -39,7 +39,7 @@ if TYPE_CHECKING: from pygit2 import Commit, Oid, Signature from pygit2.config import Config as _Pygit2Config - from pygit2.remote import Remote # type: ignore + from pygit2.remote import Remote from pygit2.repository import Repository from scmrepo.progress import GitProgressEvent @@ -53,7 +53,7 @@ def __init__(self, obj, backend: Optional["Pygit2Backend"] = None): def open( self, mode: str = "r", - encoding: str = None, + encoding: Optional[str] = None, key: Optional[Tuple[str, ...]] = None, raw: bool = True, rev: Optional[str] = None, @@ -116,7 +116,7 @@ def sha(self) -> str: return self.obj.hex def scandir(self) -> Iterable["Pygit2Object"]: - for entry in self.obj: # noqa: B301 + for entry in self.obj: yield Pygit2Object(entry, backend=self.backend) @@ -125,7 +125,7 @@ def __init__(self, config: "_Pygit2Config"): self._config = config def _key(self, section: Tuple[str, ...], name: str) -> str: - return ".".join(section + (name,)) + return ".".join((*section, name)) def get(self, section: Tuple[str, ...], name: str) -> str: return self._config[self._key(section, name)] @@ -280,7 +280,7 @@ def clone( url: str, to_path: str, shallow_branch: Optional[str] = None, - progress: Callable[["GitProgressEvent"], None] = None, + progress: Optional[Callable[["GitProgressEvent"], None]] = None, bare: bool = False, mirror: bool = False, ): @@ -303,7 +303,7 @@ def clone( @staticmethod def _set_mirror( repo: "Repository", - progress: Callable[["GitProgressEvent"], None] = None, + progress: Optional[Callable[["GitProgressEvent"], None]] = None, ): from .callbacks import RemoteCallbacks @@ -361,8 +361,8 @@ def checkout( branch = "@{-1}" try: commit, ref = self._resolve_refish(branch) - except (KeyError, GitError): - raise RevError(f"unknown Git revision '{branch}'") + except (KeyError, GitError) as exc: + raise RevError(f"unknown Git revision '{branch}'") from exc self.repo.checkout_tree(commit, strategy=strategy) detach = kwargs.get("detach", False) if ref and not detach: @@ -477,7 +477,9 @@ def resolve_rev(self, rev: str) -> str: if len(shas) > 1: raise RevError(f"ambiguous Git revision '{rev}'") if len(shas) == 1: - return shas.pop() # type: ignore + sha = shas.pop() + if sha is not None: + return sha raise RevError(f"unknown Git revision '{rev}'") def resolve_commit(self, rev: str) -> "GitCommit": @@ -485,8 +487,8 @@ def resolve_commit(self, rev: str) -> "GitCommit": try: commit, _ref = self._resolve_refish(rev) - except (KeyError, GitError): - raise SCMError(f"Invalid commit '{rev}'") + except (KeyError, GitError) as exc: + raise SCMError(f"Invalid commit '{rev}'") from exc return GitCommit( str(commit.id), commit.commit_time, @@ -576,8 +578,8 @@ def _contains(repo, ref, search_commit): try: search_commit, _ref = self._resolve_refish(rev) - except (KeyError, GitError): - raise SCMError(f"Invalid rev '{rev}'") + except (KeyError, GitError) as exc: + raise SCMError(f"Invalid rev '{rev}'") from exc if not pattern: yield from ( @@ -601,7 +603,7 @@ def push_refspecs( refspecs: Union[str, Iterable[str]], force: bool = False, on_diverged: Optional[Callable[[str, str], bool]] = None, - progress: Callable[["GitProgressEvent"], None] = None, + progress: Optional[Callable[["GitProgressEvent"], None]] = None, **kwargs, ) -> Mapping[str, SyncStatus]: raise NotImplementedError @@ -647,8 +649,8 @@ def _get_remote(self, url: str) -> Generator["Remote", None, None]: url = remote.url except ValueError: pass - except KeyError: - raise SCMError(f"'{url}' is not a valid Git remote or URL") + except KeyError as exc: + raise SCMError(f"'{url}' is not a valid Git remote or URL") from exc parsed = urlparse(url) if parsed.scheme in ("git", "git+ssh", "ssh") or url.startswith("git@"): @@ -664,7 +666,7 @@ def fetch_refspecs( refspecs: Union[str, Iterable[str]], force: bool = False, on_diverged: Optional[Callable[[str, str], bool]] = None, - progress: Callable[["GitProgressEvent"], None] = None, + progress: Optional[Callable[["GitProgressEvent"], None]] = None, **kwargs, ) -> Mapping[str, SyncStatus]: import fnmatch @@ -833,7 +835,7 @@ def _describe( def diff(self, rev_a: str, rev_b: str, binary=False) -> str: raise NotImplementedError - def reset(self, hard: bool = False, paths: Iterable[str] = None): + def reset(self, hard: bool = False, paths: Optional[Iterable[str]] = None): from pygit2 import GIT_RESET_HARD, GIT_RESET_MIXED, IndexEntry self.repo.index.read(False) @@ -963,7 +965,7 @@ def status( def iter_remote_refs(self, url: str, base: Optional[str] = None, **kwargs): raise NotImplementedError - def merge( + def merge( # noqa: C901 self, rev: str, commit: bool = True, diff --git a/src/scmrepo/git/backend/pygit2/filter.py b/src/scmrepo/git/backend/pygit2/filter.py index 306a4355..3c884fe9 100644 --- a/src/scmrepo/git/backend/pygit2/filter.py +++ b/src/scmrepo/git/backend/pygit2/filter.py @@ -18,11 +18,10 @@ def __init__(self, *args, **kwargs): self._smudge_root: Optional[str] = None def check(self, src: "FilterSource", attr_values: List[str]): - if attr_values[0] == "lfs": - if src.mode != GIT_FILTER_CLEAN: - self._smudge_buf = io.BytesIO() - self._smudge_root = src.repo.workdir or src.repo.path - return + if attr_values[0] == "lfs" and src.mode != GIT_FILTER_CLEAN: + self._smudge_buf = io.BytesIO() + self._smudge_root = src.repo.workdir or src.repo.path + return raise Passthrough def write( diff --git a/src/scmrepo/git/credentials.py b/src/scmrepo/git/credentials.py index 1ac86964..32e33dcf 100644 --- a/src/scmrepo/git/credentials.py +++ b/src/scmrepo/git/credentials.py @@ -89,7 +89,7 @@ def store(self, credential: "Credential", **kwargs): @abstractmethod def erase(self, credential: "Credential", **kwargs): - """Remove a matching credential, if any, from the helper’s storage""" + """Remove a matching credential, if any, from the helper's storage""" class GitCredentialHelper(CredentialHelper): @@ -138,8 +138,8 @@ def _prepare_command(self, action: Optional[str] = None) -> Union[str, List[str] if not shutil.which(executable) and shutil.which("git"): # If the helper cannot be found in PATH, it might be # a C git helper in GIT_EXEC_PATH - git_exec_path = subprocess.check_output( # nosec B603 - ("git", "--exec-path"), + git_exec_path = subprocess.check_output( + ("git", "--exec-path"), # noqa: S603 text=True, ).strip() if shutil.which(executable, path=git_exec_path): @@ -160,8 +160,8 @@ def get(self, credential: "Credential", **kwargs) -> "Credential": helper_input.append("") try: - res = subprocess.run( # type: ignore # nosec B603 # breaks on 3.6 - cmd, + res = subprocess.run( + cmd, # noqa: S603 check=True, capture_output=True, input="\n".join(helper_input), @@ -201,12 +201,13 @@ def store(self, credential: "Credential", **kwargs): helper_input.append("") try: - res = subprocess.run( # type: ignore # nosec B603 # pylint: disable=W1510 - cmd, + res = subprocess.run( + cmd, # noqa: S603 capture_output=True, input="\n".join(helper_input), encoding=self._encoding, **self._run_kwargs, + check=False, ) if res.stderr: logger.debug(res.stderr) @@ -214,7 +215,7 @@ def store(self, credential: "Credential", **kwargs): logger.debug("Helper not found", exc_info=True) def erase(self, credential: "Credential", **kwargs): - """Remove a matching credential, if any, from the helper’s storage""" + """Remove a matching credential, if any, from the helper's storage""" cmd = self._prepare_command("erase") use_path = credential.protocol in ("http", "https") and self.use_http_path helper_input = [ @@ -225,12 +226,13 @@ def erase(self, credential: "Credential", **kwargs): helper_input.append("") try: - res = subprocess.run( # type: ignore # nosec B603 # pylint: disable=W1510 - cmd, + res = subprocess.run( + cmd, # noqa: S603 capture_output=True, input="\n".join(helper_input), encoding=self._encoding, **self._run_kwargs, + check=False, ) if res.stderr: logger.debug(res.stderr) @@ -375,7 +377,7 @@ def get( try: if self.askpass: return self._get_interactive(credential, self.askpass.input) - if not os.environ.get("GIT_TERMINAL_PROMPT") == "0": + if os.environ.get("GIT_TERMINAL_PROMPT") != "0": return self._get_interactive(credential, _input_tty, getpass) except (EOFError, OSError): pass @@ -403,8 +405,8 @@ def _get_interactive( if not new.password: prompt = f"Password for '{new.describe()}': " new.password = input_noecho(prompt) - except KeyboardInterrupt: - raise CredentialNotFoundError("User cancelled prompt") + except KeyboardInterrupt as exc: + raise CredentialNotFoundError("User cancelled prompt") from exc return new def store(self, credential: "Credential", **kwargs): @@ -413,7 +415,7 @@ def store(self, credential: "Credential", **kwargs): self[credential] = credential def erase(self, credential: "Credential", **kwargs): - """Remove a matching credential, if any, from the helper’s storage""" + """Remove a matching credential, if any, from the helper's storage""" try: del self[credential] except KeyError: @@ -425,7 +427,7 @@ def askpass(self) -> Optional["_AskpassCommand"]: @staticmethod def get_askpass( - config: Optional[Union["ConfigDict", "StackedConfig"]] = None + config: Optional[Union["ConfigDict", "StackedConfig"]] = None, ) -> Optional["_AskpassCommand"]: askpass = os.environ.get("GIT_ASKPASS") if not askpass: @@ -448,8 +450,8 @@ def __init__(self, command: str): def input(self, prompt: str) -> str: argv = [self.command, prompt] try: - res = subprocess.run( # type: ignore # nosec B603 # breaks on 3.6 - argv, + res = subprocess.run( + argv, # noqa: S603 check=True, capture_output=True, encoding=locale.getpreferredencoding(), @@ -476,7 +478,7 @@ class Credential(Mapping[str, str]): >>> credential = generated.fill() 3. Use the credential from (2) in Git operation - 4. If the operation in (3) was successful, approve it for re-use in subsequent + 4. If the operation in (3) was successful, approve it for reuse in subsequent operations >>> credential.approve() diff --git a/src/scmrepo/git/lfs/client.py b/src/scmrepo/git/lfs/client.py index a89bb610..f0224cf6 100644 --- a/src/scmrepo/git/lfs/client.py +++ b/src/scmrepo/git/lfs/client.py @@ -12,7 +12,8 @@ from fsspec.asyn import sync_wrapper from funcy import cached_property -from ..credentials import Credential, CredentialNotFoundError +from scmrepo.git.credentials import Credential, CredentialNotFoundError + from .exceptions import LFSError from .pointer import Pointer @@ -137,8 +138,7 @@ async def _batch_request( headers=headers, json=body, ) as resp: - data = await resp.json() - return data + return await resp.json() @_authed async def _download( diff --git a/src/scmrepo/git/lfs/fetch.py b/src/scmrepo/git/lfs/fetch.py index 5c6f9347..19306752 100644 --- a/src/scmrepo/git/lfs/fetch.py +++ b/src/scmrepo/git/lfs/fetch.py @@ -44,7 +44,7 @@ def fetch( scm.lfs_storage.fetch(url, objects, progress=progress) -def get_fetch_url(scm: "Git", remote: Optional[str] = None): # noqa: C901 +def get_fetch_url(scm: "Git", remote: Optional[str] = None): # noqa: C901,PLR0912 """Return LFS fetch URL for the specified repository.""" git_config = scm.get_config() @@ -135,9 +135,8 @@ def _filter_paths( if __name__ == "__main__": # Minimal `git lfs fetch` CLI implementation import argparse - import sys - from scmrepo.git import Git # noqa: F811 + from scmrepo.git import Git parser = argparse.ArgumentParser( description=( @@ -158,5 +157,4 @@ def _filter_paths( ) args = parser.parse_args() with Git(".") as scm_: # pylint: disable=E0601 - print("fetch: fetching reference", ", ".join(args.refs), file=sys.stderr) fetch(scm_, revs=args.refs, remote=args.remote) diff --git a/src/scmrepo/git/lfs/pointer.py b/src/scmrepo/git/lfs/pointer.py index ec0445b4..9be769f4 100644 --- a/src/scmrepo/git/lfs/pointer.py +++ b/src/scmrepo/git/lfs/pointer.py @@ -103,7 +103,5 @@ def to_bytes(self) -> bytes: if not args.file: sys.exit("Nothing to do") - print(f"Git LFS pointer for {args.file}\n", file=sys.stderr) with open(args.file, mode="rb") as fobj_: p = Pointer.build(fobj_) - print(p.dump(), end="") diff --git a/src/scmrepo/git/lfs/storage.py b/src/scmrepo/git/lfs/storage.py index 0570a3bc..65a51fc0 100644 --- a/src/scmrepo/git/lfs/storage.py +++ b/src/scmrepo/git/lfs/storage.py @@ -44,17 +44,17 @@ def open( oid = obj if isinstance(obj, str) else obj.oid path = self.oid_to_path(oid) try: - return open(path, **kwargs) # pylint: disable=unspecified-encoding + return open(path, **kwargs) # noqa: SIM115 except FileNotFoundError: if not fetch_url or not isinstance(obj, Pointer): raise try: self.fetch(fetch_url, [obj]) - except BaseException as exc: + except BaseException as exc: # noqa: BLE001 raise FileNotFoundError( errno.ENOENT, os.strerror(errno.ENOENT), path ) from exc - return open(path, **kwargs) # pylint: disable=unspecified-encoding + return open(path, **kwargs) # noqa: SIM115 def close(self): pass diff --git a/src/scmrepo/git/objects.py b/src/scmrepo/git/objects.py index edb5047b..3947f2a5 100644 --- a/src/scmrepo/git/objects.py +++ b/src/scmrepo/git/objects.py @@ -9,7 +9,7 @@ S_IFGITLINK = 0o160000 -def S_ISGITLINK(m: int) -> bool: +def S_ISGITLINK(m: int) -> bool: # noqa: N802 return stat.S_IFMT(m) == S_IFGITLINK @@ -20,7 +20,7 @@ def _to_datetime(time: int, offset: int) -> datetime.datetime: class GitObject(ABC): @abstractmethod - def open(self, mode: str = "r", encoding: str = None, **kwargs): + def open(self, mode: str = "r", encoding: Optional[str] = None, **kwargs): pass @property @@ -71,7 +71,7 @@ def __init__(self, tree: GitObject, rev: str): def _build(self, tree: GitObject, path: tuple): for obj in tree.scandir(): - obj_path = path + (obj.name,) + obj_path = (*path, obj.name) self.trie[obj_path] = obj if obj.isdir: @@ -127,7 +127,7 @@ def walk(self, top: tuple, topdown: Optional[bool] = True): nondirs = [] for name in self.ls(top): - info = self.info(top + (name,)) + info = self.info((*top, name)) if info["type"] == "directory": dirs.append(name) else: @@ -137,7 +137,7 @@ def walk(self, top: tuple, topdown: Optional[bool] = True): yield top, dirs, nondirs for dname in dirs: - yield from self.walk(top + (dname,), topdown=topdown) + yield from self.walk((*top, dname), topdown=topdown) if not topdown: yield top, dirs, nondirs diff --git a/src/scmrepo/git/stash.py b/src/scmrepo/git/stash.py index f352ae2a..d5878a58 100644 --- a/src/scmrepo/git/stash.py +++ b/src/scmrepo/git/stash.py @@ -53,7 +53,7 @@ def pop(self, **kwargs): rev = self.scm.resolve_rev(ref) try: self.apply(rev, **kwargs) - except Exception as exc: + except Exception as exc: # noqa: BLE001 raise SCMError("Could not apply stash commit") from exc self.drop() return rev diff --git a/src/scmrepo/noscm.py b/src/scmrepo/noscm.py index c022e7fa..d321c9b5 100644 --- a/src/scmrepo/noscm.py +++ b/src/scmrepo/noscm.py @@ -1,3 +1,5 @@ +from typing import Optional + from .base import Base @@ -6,7 +8,7 @@ class NoSCM(Base): def __init__( self, - root_dir: str = None, + root_dir: Optional[str] = None, _raise_not_implemented_as=NotImplementedError, ): super().__init__(root_dir=root_dir) diff --git a/src/scmrepo/progress.py b/src/scmrepo/progress.py index c5524db4..65bfcd48 100644 --- a/src/scmrepo/progress.py +++ b/src/scmrepo/progress.py @@ -4,7 +4,7 @@ def code2desc(op_code): - from git import RootUpdateProgress as OP + from git import RootUpdateProgress as OP # noqa: N814 ops = { OP.COUNTING: "Counting", diff --git a/tests/conftest.py b/tests/conftest.py index e857682d..2a5a99fc 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -35,7 +35,7 @@ def pytest_collection_modifyitems(config, items): @pytest.fixture(autouse=True) -def isolate(tmp_dir_factory: TempDirFactory, monkeypatch: pytest.MonkeyPatch) -> None: +def _isolate(tmp_dir_factory: TempDirFactory, monkeypatch: pytest.MonkeyPatch) -> None: path = tmp_dir_factory.mktemp("mock") home_dir = path / "home" home_dir.mkdir() @@ -117,7 +117,7 @@ def docker(request: pytest.FixtureRequest): pytest.skip("disabled for Windows on Github Actions") pytest.importorskip("pytest_docker") - yield request.getfixturevalue("docker_services") + return request.getfixturevalue("docker_services") @pytest.fixture @@ -139,7 +139,7 @@ async def _check() -> bool: assert result.returncode == 0 async with conn.start_sftp_client() as sftp: assert await sftp.exists("/") - except Exception: # pylint: disable=broad-except + except Exception: # noqa: BLE001 # pylint: disable=broad-except return False return True diff --git a/tests/test_credentials.py b/tests/test_credentials.py index 9acc1f72..c53a2aec 100644 --- a/tests/test_credentials.py +++ b/tests/test_credentials.py @@ -21,16 +21,12 @@ def test_subprocess_get(git_helper, mocker): run = mocker.patch( "subprocess.run", return_value=mocker.Mock( - stdout="\n".join( - ["protocol=https", "host=foo.com", "username=foo", "password=bar", ""] - ) + stdout="protocol=https\nhost=foo.com\nusername=foo\npassword=bar\n" ), ) creds = git_helper.get(Credential(protocol="https", host="foo.com", path="foo.git")) assert run.call_args.args[0] == ["git-credential-foo", "get"] - assert run.call_args.kwargs.get("input") == "\n".join( - ["protocol=https", "host=foo.com", ""] - ) + assert run.call_args.kwargs.get("input") == "protocol=https\nhost=foo.com\n" assert creds == Credential(url="https://foo:bar@foo.com") @@ -38,14 +34,13 @@ def test_subprocess_get_use_http_path(git_helper, mocker): git_helper.use_http_path = True run = mocker.patch( "subprocess.run", - return_value=mocker.Mock( - stdout="\n".join(["username=foo", "password=bar", ""]) - ), + return_value=mocker.Mock(stdout="username=foo\npassword=bar\n"), ) creds = git_helper.get(Credential(protocol="https", host="foo.com", path="foo.git")) assert run.call_args.args[0] == ["git-credential-foo", "get"] - assert run.call_args.kwargs.get("input") == "\n".join( - ["protocol=https", "host=foo.com", "path=foo.git", ""] + assert ( + run.call_args.kwargs.get("input") + == "protocol=https\nhost=foo.com\npath=foo.git\n" ) assert creds == Credential(username="foo", password="bar") @@ -70,8 +65,9 @@ def test_subprocess_store(git_helper, mocker): Credential(protocol="https", host="foo.com", username="foo", password="bar") ) assert run.call_args.args[0] == ["git-credential-foo", "store"] - assert run.call_args.kwargs.get("input") == "\n".join( - ["protocol=https", "host=foo.com", "username=foo", "password=bar", ""] + assert ( + run.call_args.kwargs.get("input") + == "protocol=https\nhost=foo.com\nusername=foo\npassword=bar\n" ) @@ -79,9 +75,7 @@ def test_subprocess_erase(git_helper, mocker): run = mocker.patch("subprocess.run") git_helper.erase(Credential(protocol="https", host="foo.com")) assert run.call_args.args[0] == ["git-credential-foo", "erase"] - assert run.call_args.kwargs.get("input") == "\n".join( - ["protocol=https", "host=foo.com", ""] - ) + assert run.call_args.kwargs.get("input") == "protocol=https\nhost=foo.com\n" def test_memory_helper_get(mocker): @@ -149,7 +143,7 @@ def test_memory_helper_prompt_askpass(mocker): "subprocess.run", side_effect=[ mocker.Mock(stdout="foo"), - mocker.Mock(stdout="\n".join(["bar", ""])), + mocker.Mock(stdout="bar\n"), ], ) expected = Credential( diff --git a/tests/test_dulwich.py b/tests/test_dulwich.py index ed100b74..9a6d211b 100644 --- a/tests/test_dulwich.py +++ b/tests/test_dulwich.py @@ -2,7 +2,7 @@ import socket import threading from io import StringIO -from typing import Any, Dict, Iterator +from typing import Any, Dict from unittest.mock import AsyncMock import asyncssh @@ -18,7 +18,7 @@ @pytest.fixture -def ssh_conn(request: pytest.FixtureRequest) -> Iterator[Dict[str, Any]]: +def ssh_conn(request: pytest.FixtureRequest) -> Dict[str, Any]: server = Server([]) socket.setdefaulttimeout(10) @@ -27,10 +27,10 @@ def ssh_conn(request: pytest.FixtureRequest) -> Iterator[Dict[str, Any]]: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(("127.0.0.1", 0)) sock.listen(5) - request.addfinalizer(sock.close) + request.addfinalizer(sock.close) # noqa: PT021 port = sock.getsockname()[1] - conn_info = {"port": port, "server": server} + conn_info: Dict[str, Any] = {"port": port, "server": server} def _run_server(): try: @@ -45,7 +45,7 @@ def _run_server(): thread = threading.Thread(target=_run_server) thread.start() - yield conn_info + return conn_info @pytest.fixture @@ -150,9 +150,9 @@ def test_dulwich_github_compat(mocker: MockerFixture, algorithm: bytes): ) packet = mocker.Mock() + strings = iter((b"ed21556", key_data)) + packet.get_string = lambda: next(strings) with pytest.raises(ProtocolError): - strings = iter((b"ed21556", key_data)) - packet.get_string = lambda: next(strings) _process_public_key_ok_gh(auth, None, None, packet) strings = iter((b"ssh-rsa", key_data)) diff --git a/tests/test_fs.py b/tests/test_fs.py index 451022bf..e81bcc2c 100644 --- a/tests/test_fs.py +++ b/tests/test_fs.py @@ -35,9 +35,9 @@ def test_open(tmp_dir: TmpDir, scm: Git, make_fs, raw: bool, git_backend: str): assert fobj.read() == "foo" with fs.open("тест", mode="r", encoding="utf-8", raw=raw) as fobj: assert fobj.read() == "проверка" - with pytest.raises(IOError): + with pytest.raises(IOError): # noqa: PT011 fs.open("not-existing-file", raw=raw) - with pytest.raises(IOError): + with pytest.raises(IOError): # noqa: PT011 fs.open("data", raw=raw) diff --git a/tests/test_git.py b/tests/test_git.py index 050feb63..07fefef2 100644 --- a/tests/test_git.py +++ b/tests/test_git.py @@ -8,7 +8,7 @@ from dulwich.client import LocalGitClient from git import Repo as GitPythonRepo from pygit2 import GitError -from pygit2.remote import Remote # type: ignore +from pygit2.remote import Remote from pytest_mock import MockerFixture from pytest_test_utils import TempDirFactory, TmpDir from pytest_test_utils.matchers import Matcher @@ -31,7 +31,7 @@ def submodule_dir(tmp_dir: TmpDir, scm: Git): subrepo.create_submodule(subrepo_path, subrepo_path, subrepo.git_dir) subrepo.close() - yield tmp_dir / subrepo_path + return tmp_dir / subrepo_path def test_git_init(tmp_dir: TmpDir, git_backend: str): @@ -362,9 +362,9 @@ def test_fetch_refspecs( } assert baz_rev == scm.get_ref("refs/foo/baz") + mocker.patch.object(LocalGitClient, "fetch", side_effect=KeyError) + mocker.patch.object(Remote, "fetch", side_effect=GitError) with pytest.raises(SCMError): - mocker.patch.object(LocalGitClient, "fetch", side_effect=KeyError) - mocker.patch.object(Remote, "fetch", side_effect=GitError) git.fetch_refspecs(remote, "refs/foo/bar:refs/foo/bar") assert len(scm.pygit2.repo.remotes) == 1 @@ -394,9 +394,9 @@ def test_iter_remote_refs( with pytest.raises(InvalidRemote): set(git.iter_remote_refs("bad-remote")) + tmp_directory = tmp_dir_factory.mktemp("not_a_git_repo") + remote = f"file://{tmp_directory.as_posix()}" with pytest.raises(InvalidRemote): - tmp_directory = tmp_dir_factory.mktemp("not_a_git_repo") - remote = f"file://{tmp_directory.as_posix()}" set(git.iter_remote_refs(remote)) remote = url if use_url else "origin" @@ -478,9 +478,9 @@ def test_merge(tmp_dir: TmpDir, scm: Git, git: Git, squash: bool): scm.checkout("master") + tmp_dir.gen("foo", "baz") + scm.add_commit("foo", message="baz") with pytest.raises(MergeConflictError): - tmp_dir.gen("foo", "baz") - scm.add_commit("foo", message="baz") git.merge(branch, commit=not squash, squash=squash, msg="merge") scm.gitpython.git.reset(init_rev, hard=True) diff --git a/tests/test_lfs.py b/tests/test_lfs.py index 6918f05f..b20d745b 100644 --- a/tests/test_lfs.py +++ b/tests/test_lfs.py @@ -21,7 +21,7 @@ def storage(tmp_dir_factory: TempDirFactory) -> LFSStorage: @pytest.fixture -def lfs(tmp_dir: TmpDir, scm: Git) -> None: +def lfs(tmp_dir: TmpDir, scm: Git) -> None: # noqa: PT004 tmp_dir.gen(".gitattributes", "*.lfs filter=lfs diff=lfs merge=lfs -text") scm.add([".gitattributes"]) scm.commit("init lfs attributes") diff --git a/tests/test_pygit2.py b/tests/test_pygit2.py index b9f096a6..0258ca34 100644 --- a/tests/test_pygit2.py +++ b/tests/test_pygit2.py @@ -90,7 +90,7 @@ def test_pygit_use_env_vars_for_signature( ) git = Git.init(tmp_dir) with pytest.raises(SCMError): - git.pygit2.default_signature # pylint: disable=W0104 + _ = git.pygit2.default_signature # pylint: disable=W0104 # Make sure that the environment variables are not set to not interfere with # with the check below