scmrepo 3.3.8__tar.gz → 3.3.10__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {scmrepo-3.3.8 → scmrepo-3.3.10}/.github/workflows/tests.yaml +2 -2
- {scmrepo-3.3.8 → scmrepo-3.3.10}/.pre-commit-config.yaml +3 -3
- {scmrepo-3.3.8/src/scmrepo.egg-info → scmrepo-3.3.10}/PKG-INFO +4 -3
- {scmrepo-3.3.8 → scmrepo-3.3.10}/noxfile.py +1 -1
- {scmrepo-3.3.8 → scmrepo-3.3.10}/pyproject.toml +2 -1
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo/fs.py +0 -31
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo/git/__init__.py +2 -2
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo/git/backend/gitpython.py +1 -1
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo/git/backend/pygit2/__init__.py +1 -1
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo/git/backend/pygit2/filter.py +3 -3
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo/git/credentials.py +34 -3
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo/git/lfs/client.py +1 -1
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo/git/lfs/fetch.py +1 -1
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo/git/lfs/storage.py +2 -2
- {scmrepo-3.3.8 → scmrepo-3.3.10/src/scmrepo.egg-info}/PKG-INFO +4 -3
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo.egg-info/requires.txt +1 -1
- {scmrepo-3.3.8 → scmrepo-3.3.10}/tests/conftest.py +1 -1
- {scmrepo-3.3.8 → scmrepo-3.3.10}/tests/test_credentials.py +40 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/tests/test_lfs.py +1 -1
- {scmrepo-3.3.8 → scmrepo-3.3.10}/tests/test_pygit2.py +1 -1
- {scmrepo-3.3.8 → scmrepo-3.3.10}/.coveragerc +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/.cruft.json +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/.gitattributes +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/.github/dependabot.yml +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/.github/workflows/release.yaml +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/.github/workflows/update-template.yaml +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/.gitignore +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/CODE_OF_CONDUCT.rst +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/CONTRIBUTING.rst +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/LICENSE +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/README.rst +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/setup.cfg +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo/__init__.py +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo/asyn.py +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo/base.py +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo/exceptions.py +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo/git/backend/__init__.py +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo/git/backend/base.py +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo/git/backend/dulwich/__init__.py +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo/git/backend/dulwich/asyncssh_vendor.py +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo/git/backend/dulwich/client.py +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo/git/backend/pygit2/callbacks.py +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo/git/config.py +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo/git/lfs/__init__.py +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo/git/lfs/exceptions.py +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo/git/lfs/object.py +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo/git/lfs/pointer.py +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo/git/lfs/progress.py +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo/git/lfs/smudge.py +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo/git/objects.py +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo/git/stash.py +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo/noscm.py +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo/progress.py +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo/py.typed +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo/urls.py +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo/utils.py +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo.egg-info/SOURCES.txt +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo.egg-info/dependency_links.txt +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/src/scmrepo.egg-info/top_level.txt +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/tests/__init__.py +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/tests/docker-compose.yml +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/tests/git-init/git.sh +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/tests/test_dulwich.py +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/tests/test_fs.py +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/tests/test_git.py +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/tests/test_noscm.py +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/tests/test_scmrepo.py +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/tests/test_stash.py +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/tests/test_urls.py +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/tests/user.key +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/tests/user.key.pub +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/tests/vendor/__init__.py +0 -0
- {scmrepo-3.3.8 → scmrepo-3.3.10}/tests/vendor/test_paramiko_vendor.py +0 -0
|
@@ -21,7 +21,7 @@ jobs:
|
|
|
21
21
|
fail-fast: false
|
|
22
22
|
matrix:
|
|
23
23
|
os: [ubuntu-20.04, windows-latest, macos-latest]
|
|
24
|
-
pyv: ['3.9', '3.10', '3.11', '3.12']
|
|
24
|
+
pyv: ['3.9', '3.10', '3.11', '3.12', '3.13']
|
|
25
25
|
|
|
26
26
|
steps:
|
|
27
27
|
- name: Check out the repository
|
|
@@ -47,7 +47,7 @@ jobs:
|
|
|
47
47
|
run: nox -s tests-${{ matrix.pyv }} -- --slow --cov-report=xml
|
|
48
48
|
|
|
49
49
|
- name: Upload coverage report
|
|
50
|
-
uses: codecov/codecov-action@
|
|
50
|
+
uses: codecov/codecov-action@v5.3.1
|
|
51
51
|
|
|
52
52
|
- name: Build package
|
|
53
53
|
run: nox -s build
|
|
@@ -2,7 +2,7 @@ default_language_version:
|
|
|
2
2
|
python: python3
|
|
3
3
|
repos:
|
|
4
4
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
5
|
-
rev:
|
|
5
|
+
rev: v5.0.0
|
|
6
6
|
hooks:
|
|
7
7
|
- id: check-added-large-files
|
|
8
8
|
- id: check-case-conflict
|
|
@@ -20,13 +20,13 @@ repos:
|
|
|
20
20
|
- id: sort-simple-yaml
|
|
21
21
|
- id: trailing-whitespace
|
|
22
22
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
23
|
-
rev: 'v0.
|
|
23
|
+
rev: 'v0.9.3'
|
|
24
24
|
hooks:
|
|
25
25
|
- id: ruff
|
|
26
26
|
args: [--fix, --exit-non-zero-on-fix]
|
|
27
27
|
- id: ruff-format
|
|
28
28
|
- repo: https://github.com/codespell-project/codespell
|
|
29
|
-
rev: v2.
|
|
29
|
+
rev: v2.4.0
|
|
30
30
|
hooks:
|
|
31
31
|
- id: codespell
|
|
32
32
|
additional_dependencies: ["tomli"]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: scmrepo
|
|
3
|
-
Version: 3.3.
|
|
3
|
+
Version: 3.3.10
|
|
4
4
|
Summary: scmrepo
|
|
5
5
|
Author-email: Iterative <support@dvc.org>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -11,6 +11,7 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.10
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
15
|
Classifier: Development Status :: 4 - Beta
|
|
15
16
|
Requires-Python: >=3.9
|
|
16
17
|
Description-Content-Type: text/x-rst
|
|
@@ -37,7 +38,7 @@ Requires-Dist: pytest-sugar; extra == "tests"
|
|
|
37
38
|
Requires-Dist: pytest-test-utils<0.2,>=0.1.0; extra == "tests"
|
|
38
39
|
Requires-Dist: proxy.py; extra == "tests"
|
|
39
40
|
Provides-Extra: dev
|
|
40
|
-
Requires-Dist: mypy==1.
|
|
41
|
+
Requires-Dist: mypy==1.14.1; extra == "dev"
|
|
41
42
|
Requires-Dist: scmrepo[tests]; extra == "dev"
|
|
42
43
|
Requires-Dist: types-certifi; extra == "dev"
|
|
43
44
|
Requires-Dist: types-mock; extra == "dev"
|
|
@@ -10,7 +10,7 @@ nox.options.sessions = "lint", "tests"
|
|
|
10
10
|
locations = "src", "tests"
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
@nox.session(python=["3.9", "3.10", "3.11", "3.12"])
|
|
13
|
+
@nox.session(python=["3.9", "3.10", "3.11", "3.12", "3.13"])
|
|
14
14
|
def tests(session: nox.Session) -> None:
|
|
15
15
|
session.install(".[tests]")
|
|
16
16
|
session.run(
|
|
@@ -16,6 +16,7 @@ classifiers = [
|
|
|
16
16
|
"Programming Language :: Python :: 3.10",
|
|
17
17
|
"Programming Language :: Python :: 3.11",
|
|
18
18
|
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Programming Language :: Python :: 3.13",
|
|
19
20
|
"Development Status :: 4 - Beta",
|
|
20
21
|
]
|
|
21
22
|
requires-python = ">=3.9"
|
|
@@ -51,7 +52,7 @@ tests = [
|
|
|
51
52
|
"proxy.py",
|
|
52
53
|
]
|
|
53
54
|
dev = [
|
|
54
|
-
"mypy==1.
|
|
55
|
+
"mypy==1.14.1",
|
|
55
56
|
"scmrepo[tests]",
|
|
56
57
|
"types-certifi",
|
|
57
58
|
"types-mock",
|
|
@@ -3,9 +3,7 @@ import os
|
|
|
3
3
|
import posixpath
|
|
4
4
|
from typing import TYPE_CHECKING, Any, BinaryIO, Callable, Optional
|
|
5
5
|
|
|
6
|
-
from fsspec.callbacks import _DEFAULT_CALLBACK
|
|
7
6
|
from fsspec.spec import AbstractFileSystem
|
|
8
|
-
from fsspec.utils import isfilelike
|
|
9
7
|
|
|
10
8
|
if TYPE_CHECKING:
|
|
11
9
|
from io import BytesIO
|
|
@@ -242,32 +240,3 @@ class GitFileSystem(AbstractFileSystem):
|
|
|
242
240
|
return paths
|
|
243
241
|
|
|
244
242
|
return [self.info(_path) for _path in paths]
|
|
245
|
-
|
|
246
|
-
def get_file(
|
|
247
|
-
self, rpath, lpath, callback=_DEFAULT_CALLBACK, outfile=None, **kwargs
|
|
248
|
-
):
|
|
249
|
-
# NOTE: temporary workaround while waiting for
|
|
250
|
-
# https://github.com/fsspec/filesystem_spec/pull/1191
|
|
251
|
-
|
|
252
|
-
if isfilelike(lpath):
|
|
253
|
-
outfile = lpath
|
|
254
|
-
elif self.isdir(rpath):
|
|
255
|
-
os.makedirs(lpath, exist_ok=True)
|
|
256
|
-
return None
|
|
257
|
-
|
|
258
|
-
with self.open(rpath, "rb", **kwargs) as f1:
|
|
259
|
-
if outfile is None:
|
|
260
|
-
outfile = open(lpath, "wb") # noqa: SIM115
|
|
261
|
-
|
|
262
|
-
try:
|
|
263
|
-
callback.set_size(getattr(f1, "size", None))
|
|
264
|
-
data = True
|
|
265
|
-
while data:
|
|
266
|
-
data = f1.read(self.blocksize)
|
|
267
|
-
segment_len = outfile.write(data)
|
|
268
|
-
if segment_len is None:
|
|
269
|
-
segment_len = len(data)
|
|
270
|
-
callback.relative_update(segment_len)
|
|
271
|
-
finally:
|
|
272
|
-
if not isfilelike(lpath):
|
|
273
|
-
outfile.close()
|
|
@@ -258,7 +258,7 @@ class Git(Base):
|
|
|
258
258
|
self.hooks_dir.mkdir(exist_ok=True)
|
|
259
259
|
hook = self.hooks_dir / name
|
|
260
260
|
|
|
261
|
-
directive = f"#!{shutil.which(interpreter) or '/bin/sh'
|
|
261
|
+
directive = f"#!{shutil.which(interpreter) or '/bin/sh'}"
|
|
262
262
|
hook.write_text(f"{directive}\n{script}\n", encoding="utf-8")
|
|
263
263
|
hook.chmod(0o777)
|
|
264
264
|
|
|
@@ -287,7 +287,7 @@ class Git(Base):
|
|
|
287
287
|
def no_commits(self):
|
|
288
288
|
return not bool(self.get_ref("HEAD"))
|
|
289
289
|
|
|
290
|
-
# Prefer
|
|
290
|
+
# Prefer reusing the most recently used backend when possible. When
|
|
291
291
|
# changing backends (due to unimplemented calls), we close the previous
|
|
292
292
|
# backend to release any open git files/contexts that may cause conflicts
|
|
293
293
|
# with the new backend.
|
|
@@ -184,7 +184,7 @@ class GitPythonBackend(BaseGitBackend): # pylint:disable=abstract-method
|
|
|
184
184
|
# In fix_env, we delete LD_LIBRARY_PATH key if it was empty before
|
|
185
185
|
# PyInstaller modified it. GitPython, in git.Repo.clone_from, uses
|
|
186
186
|
# env to update its own internal state. When there is no key in
|
|
187
|
-
# env, this value is not updated and GitPython
|
|
187
|
+
# env, this value is not updated and GitPython reuses
|
|
188
188
|
# LD_LIBRARY_PATH that has been set by PyInstaller.
|
|
189
189
|
# See [1] for more info.
|
|
190
190
|
# [1] https://github.com/gitpython-developers/GitPython/issues/924
|
|
@@ -720,7 +720,7 @@ class Pygit2Backend(BaseGitBackend): # pylint:disable=abstract-method
|
|
|
720
720
|
for refname in remote_refs:
|
|
721
721
|
if fnmatch.fnmatch(refname, lh):
|
|
722
722
|
src = refname
|
|
723
|
-
dst = f"{rh_prefix}{refname[len(lh_prefix):]}"
|
|
723
|
+
dst = f"{rh_prefix}{refname[len(lh_prefix) :]}"
|
|
724
724
|
result[dst] = cb.result.get(
|
|
725
725
|
src, _default_status(src, dst, remote_refs)
|
|
726
726
|
)
|
|
@@ -2,10 +2,10 @@ import io
|
|
|
2
2
|
import logging
|
|
3
3
|
from typing import TYPE_CHECKING, Callable, Optional
|
|
4
4
|
|
|
5
|
-
from pygit2 import GIT_FILTER_CLEAN, Filter, Passthrough
|
|
5
|
+
from pygit2 import GIT_FILTER_CLEAN, Filter, Passthrough # type: ignore[attr-defined]
|
|
6
6
|
|
|
7
7
|
if TYPE_CHECKING:
|
|
8
|
-
from pygit2 import FilterSource
|
|
8
|
+
from pygit2 import FilterSource # type: ignore[attr-defined]
|
|
9
9
|
|
|
10
10
|
logger = logging.getLogger(__name__)
|
|
11
11
|
|
|
@@ -17,7 +17,7 @@ class LFSFilter(Filter):
|
|
|
17
17
|
self._smudge_buf: Optional[io.BytesIO] = None
|
|
18
18
|
self._smudge_root: Optional[str] = None
|
|
19
19
|
|
|
20
|
-
def check(self, src: "FilterSource", attr_values: list[str]):
|
|
20
|
+
def check(self, src: "FilterSource", attr_values: list[Optional[str]]):
|
|
21
21
|
if attr_values[0] == "lfs" and src.mode != GIT_FILTER_CLEAN:
|
|
22
22
|
self._smudge_buf = io.BytesIO()
|
|
23
23
|
self._smudge_root = src.repo.workdir or src.repo.path
|
|
@@ -173,11 +173,29 @@ class GitCredentialHelper(CredentialHelper):
|
|
|
173
173
|
if res.stderr:
|
|
174
174
|
logger.debug(res.stderr)
|
|
175
175
|
|
|
176
|
-
credentials = {}
|
|
176
|
+
credentials: dict[str, Any] = {}
|
|
177
177
|
for line in res.stdout.splitlines():
|
|
178
178
|
try:
|
|
179
179
|
key, value = line.split("=", maxsplit=1)
|
|
180
|
-
|
|
180
|
+
# Only include credential values that are used in the Credential
|
|
181
|
+
# constructor.
|
|
182
|
+
# Other values may be returned by the subprocess, but they must be
|
|
183
|
+
# ignored.
|
|
184
|
+
# e.g. osxkeychain credential helper >= 2.46.0 can return
|
|
185
|
+
# `capability[]` and `state`)
|
|
186
|
+
if key in [
|
|
187
|
+
"protocol",
|
|
188
|
+
"host",
|
|
189
|
+
"path",
|
|
190
|
+
"username",
|
|
191
|
+
"password",
|
|
192
|
+
"password_expiry_utc",
|
|
193
|
+
"url",
|
|
194
|
+
]:
|
|
195
|
+
# Garbage bytes were output from git-credential-osxkeychain from
|
|
196
|
+
# 2.45.0 to 2.47.0:
|
|
197
|
+
# https://github.com/git/git/commit/6c3c451fb6e1c3ca83f74e63079d4d0af01b2d69
|
|
198
|
+
credentials[key] = _strip_garbage_bytes(value)
|
|
181
199
|
except ValueError:
|
|
182
200
|
continue
|
|
183
201
|
if not credentials:
|
|
@@ -265,6 +283,19 @@ class GitCredentialHelper(CredentialHelper):
|
|
|
265
283
|
)
|
|
266
284
|
|
|
267
285
|
|
|
286
|
+
def _strip_garbage_bytes(s: str) -> str:
|
|
287
|
+
"""
|
|
288
|
+
Garbage (random) bytes were output from git-credential-osxkeychain from
|
|
289
|
+
2.45.0 to 2.47.0 so must be removed.
|
|
290
|
+
https://github.com/git/git/commit/6c3c451fb6e1c3ca83f74e63079d4d0af01b2d69
|
|
291
|
+
:param s: string that might contain garbage/random bytes
|
|
292
|
+
:return str: The string with the garbage bytes removed
|
|
293
|
+
"""
|
|
294
|
+
# Assume that any garbage bytes begin with a 0-byte
|
|
295
|
+
zero = s.find(chr(0))
|
|
296
|
+
return s[0:zero] if zero >= 0 else s
|
|
297
|
+
|
|
298
|
+
|
|
268
299
|
class _CredentialKey(NamedTuple):
|
|
269
300
|
protocol: str
|
|
270
301
|
host: Optional[str]
|
|
@@ -293,7 +324,7 @@ def _input_tty(prompt: str = "Username: ") -> str:
|
|
|
293
324
|
try:
|
|
294
325
|
fd = os.open(
|
|
295
326
|
"/dev/tty",
|
|
296
|
-
os.O_RDWR | os.O_NOCTTY, #
|
|
327
|
+
os.O_RDWR | os.O_NOCTTY, # type: ignore[attr-defined]
|
|
297
328
|
)
|
|
298
329
|
tty = io.FileIO(fd, "w+")
|
|
299
330
|
stack.enter_context(tty)
|
|
@@ -281,7 +281,7 @@ def _as_atomic(to_info: str, create_parents: bool = False) -> Iterator[str]:
|
|
|
281
281
|
if create_parents:
|
|
282
282
|
os.makedirs(parent, exist_ok=True)
|
|
283
283
|
|
|
284
|
-
tmp_file = NamedTemporaryFile(dir=parent, delete=False)
|
|
284
|
+
tmp_file = NamedTemporaryFile(dir=parent, delete=False) # noqa: SIM115
|
|
285
285
|
tmp_file.close()
|
|
286
286
|
try:
|
|
287
287
|
yield tmp_file.name
|
|
@@ -122,7 +122,7 @@ def _collect_objects(
|
|
|
122
122
|
and (result := _ROOT_PATH_PREFIX_REGEX.match(path := include[0]))
|
|
123
123
|
):
|
|
124
124
|
root = result.group("prefix")
|
|
125
|
-
if path in {root, f
|
|
125
|
+
if path in {root, f"{root.rstrip('/')}/**"}:
|
|
126
126
|
include = []
|
|
127
127
|
else:
|
|
128
128
|
root = "/"
|
|
@@ -47,7 +47,7 @@ class LFSStorage:
|
|
|
47
47
|
oid = obj if isinstance(obj, str) else obj.oid
|
|
48
48
|
path = self.oid_to_path(oid)
|
|
49
49
|
try:
|
|
50
|
-
return open(path, **kwargs)
|
|
50
|
+
return open(path, **kwargs)
|
|
51
51
|
except FileNotFoundError:
|
|
52
52
|
if not fetch_url or not isinstance(obj, Pointer):
|
|
53
53
|
raise
|
|
@@ -57,7 +57,7 @@ class LFSStorage:
|
|
|
57
57
|
raise FileNotFoundError(
|
|
58
58
|
errno.ENOENT, os.strerror(errno.ENOENT), path
|
|
59
59
|
) from exc
|
|
60
|
-
return open(path, **kwargs)
|
|
60
|
+
return open(path, **kwargs)
|
|
61
61
|
|
|
62
62
|
def close(self):
|
|
63
63
|
pass
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: scmrepo
|
|
3
|
-
Version: 3.3.
|
|
3
|
+
Version: 3.3.10
|
|
4
4
|
Summary: scmrepo
|
|
5
5
|
Author-email: Iterative <support@dvc.org>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -11,6 +11,7 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.10
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
15
|
Classifier: Development Status :: 4 - Beta
|
|
15
16
|
Requires-Python: >=3.9
|
|
16
17
|
Description-Content-Type: text/x-rst
|
|
@@ -37,7 +38,7 @@ Requires-Dist: pytest-sugar; extra == "tests"
|
|
|
37
38
|
Requires-Dist: pytest-test-utils<0.2,>=0.1.0; extra == "tests"
|
|
38
39
|
Requires-Dist: proxy.py; extra == "tests"
|
|
39
40
|
Provides-Extra: dev
|
|
40
|
-
Requires-Dist: mypy==1.
|
|
41
|
+
Requires-Dist: mypy==1.14.1; extra == "dev"
|
|
41
42
|
Requires-Dist: scmrepo[tests]; extra == "dev"
|
|
42
43
|
Requires-Dist: types-certifi; extra == "dev"
|
|
43
44
|
Requires-Dist: types-mock; extra == "dev"
|
|
@@ -58,7 +58,7 @@ email=dvctester@example.com
|
|
|
58
58
|
defaultBranch=master
|
|
59
59
|
"""
|
|
60
60
|
(home_dir / ".gitconfig").write_bytes(contents)
|
|
61
|
-
pygit2.settings.search_path[pygit2.GIT_CONFIG_LEVEL_GLOBAL] = str(home_dir)
|
|
61
|
+
pygit2.settings.search_path[pygit2.GIT_CONFIG_LEVEL_GLOBAL] = str(home_dir) # type: ignore[attr-defined]
|
|
62
62
|
|
|
63
63
|
|
|
64
64
|
@pytest.fixture
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import io
|
|
2
2
|
import os
|
|
3
|
+
import random
|
|
3
4
|
|
|
4
5
|
import pytest
|
|
5
6
|
|
|
@@ -45,6 +46,45 @@ def test_subprocess_get_use_http_path(git_helper, mocker):
|
|
|
45
46
|
assert creds == Credential(username="foo", password="bar")
|
|
46
47
|
|
|
47
48
|
|
|
49
|
+
def test_subprocess_ignore_unexpected_credential_keys(git_helper, mocker):
|
|
50
|
+
git_helper.use_http_path = True
|
|
51
|
+
run = mocker.patch(
|
|
52
|
+
"subprocess.run",
|
|
53
|
+
# Simulate git-credential-osxkeychain (version >=2.45)
|
|
54
|
+
return_value=mocker.Mock(
|
|
55
|
+
stdout="username=foo\npassword=bar\ncapability[]=state\nstate[]=osxkeychain:seen=1"
|
|
56
|
+
),
|
|
57
|
+
)
|
|
58
|
+
creds = git_helper.get(Credential(protocol="https", host="foo.com", path="foo.git"))
|
|
59
|
+
assert run.call_args.args[0] == ["git-credential-foo", "get"]
|
|
60
|
+
assert (
|
|
61
|
+
run.call_args.kwargs.get("input")
|
|
62
|
+
== "protocol=https\nhost=foo.com\npath=foo.git\n"
|
|
63
|
+
)
|
|
64
|
+
assert creds == Credential(username="foo", password="bar")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_subprocess_strip_trailing_garbage_bytes(git_helper, mocker):
|
|
68
|
+
"""Garbage bytes were output from git-credential-osxkeychain from 2.45.0 to 2.47.0
|
|
69
|
+
so must be removed
|
|
70
|
+
https://github.com/git/git/commit/6c3c451fb6e1c3ca83f74e63079d4d0af01b2d69"""
|
|
71
|
+
git_helper.use_http_path = True
|
|
72
|
+
run = mocker.patch(
|
|
73
|
+
"subprocess.run",
|
|
74
|
+
# Simulate git-credential-osxkeychain (version 2.45), assuming initial 0-byte
|
|
75
|
+
return_value=mocker.Mock(
|
|
76
|
+
stdout=f"username=foo\npassword=bar{chr(0)}{random.randbytes(15)}"
|
|
77
|
+
),
|
|
78
|
+
)
|
|
79
|
+
creds = git_helper.get(Credential(protocol="https", host="foo.com", path="foo.git"))
|
|
80
|
+
assert run.call_args.args[0] == ["git-credential-foo", "get"]
|
|
81
|
+
assert (
|
|
82
|
+
run.call_args.kwargs.get("input")
|
|
83
|
+
== "protocol=https\nhost=foo.com\npath=foo.git\n"
|
|
84
|
+
)
|
|
85
|
+
assert creds == Credential(username="foo", password="bar")
|
|
86
|
+
|
|
87
|
+
|
|
48
88
|
def test_subprocess_get_failed(git_helper, mocker):
|
|
49
89
|
from subprocess import CalledProcessError
|
|
50
90
|
|
|
@@ -32,7 +32,7 @@ def storage(tmp_dir_factory: TempDirFactory) -> LFSStorage:
|
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
@pytest.fixture
|
|
35
|
-
def lfs(tmp_dir: TmpDir, scm: Git) -> None:
|
|
35
|
+
def lfs(tmp_dir: TmpDir, scm: Git) -> None:
|
|
36
36
|
tmp_dir.gen(".gitattributes", "*.lfs filter=lfs diff=lfs merge=lfs -text")
|
|
37
37
|
scm.add([".gitattributes"])
|
|
38
38
|
scm.commit("init lfs attributes")
|
|
@@ -39,7 +39,7 @@ def test_pygit_resolve_refish(tmp_dir: TmpDir, scm: Git, use_sha: str):
|
|
|
39
39
|
def test_pygit_stash_apply_conflicts(
|
|
40
40
|
tmp_dir: TmpDir, scm: Git, skip_conflicts: bool, mocker: MockerFixture
|
|
41
41
|
):
|
|
42
|
-
from pygit2 import GIT_CHECKOUT_ALLOW_CONFLICTS
|
|
42
|
+
from pygit2 import GIT_CHECKOUT_ALLOW_CONFLICTS # type: ignore[attr-defined]
|
|
43
43
|
|
|
44
44
|
tmp_dir.gen("foo", "foo")
|
|
45
45
|
scm.add_commit("foo", message="foo")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|