scmrepo 3.3.0__py3-none-any.whl → 3.3.2__py3-none-any.whl
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/git/backend/pygit2/__init__.py +6 -5
- scmrepo/git/lfs/client.py +32 -16
- scmrepo/urls.py +21 -0
- {scmrepo-3.3.0.dist-info → scmrepo-3.3.2.dist-info}/METADATA +16 -16
- {scmrepo-3.3.0.dist-info → scmrepo-3.3.2.dist-info}/RECORD +8 -7
- {scmrepo-3.3.0.dist-info → scmrepo-3.3.2.dist-info}/LICENSE +0 -0
- {scmrepo-3.3.0.dist-info → scmrepo-3.3.2.dist-info}/WHEEL +0 -0
- {scmrepo-3.3.0.dist-info → scmrepo-3.3.2.dist-info}/top_level.txt +0 -0
|
@@ -2,7 +2,7 @@ import locale
|
|
|
2
2
|
import logging
|
|
3
3
|
import os
|
|
4
4
|
import stat
|
|
5
|
-
from collections.abc import
|
|
5
|
+
from collections.abc import Iterable, Iterator, Mapping
|
|
6
6
|
from contextlib import contextmanager
|
|
7
7
|
from io import BytesIO, StringIO, TextIOWrapper
|
|
8
8
|
from typing import (
|
|
@@ -25,6 +25,7 @@ from scmrepo.exceptions import (
|
|
|
25
25
|
from scmrepo.git.backend.base import BaseGitBackend, SyncStatus
|
|
26
26
|
from scmrepo.git.config import Config
|
|
27
27
|
from scmrepo.git.objects import GitCommit, GitObject, GitTag
|
|
28
|
+
from scmrepo.urls import is_scp_style_url
|
|
28
29
|
from scmrepo.utils import relpath
|
|
29
30
|
|
|
30
31
|
logger = logging.getLogger(__name__)
|
|
@@ -636,7 +637,7 @@ class Pygit2Backend(BaseGitBackend): # pylint:disable=abstract-method
|
|
|
636
637
|
raise SCMError("Unknown merge analysis result")
|
|
637
638
|
|
|
638
639
|
@contextmanager
|
|
639
|
-
def _get_remote(self, url: str) ->
|
|
640
|
+
def _get_remote(self, url: str) -> Iterator["Remote"]:
|
|
640
641
|
"""Return a pygit2.Remote suitable for the specified Git URL or remote name."""
|
|
641
642
|
try:
|
|
642
643
|
remote = self.repo.remotes[url]
|
|
@@ -646,11 +647,11 @@ class Pygit2Backend(BaseGitBackend): # pylint:disable=abstract-method
|
|
|
646
647
|
except KeyError as exc:
|
|
647
648
|
raise SCMError(f"'{url}' is not a valid Git remote or URL") from exc
|
|
648
649
|
|
|
649
|
-
if os.name == "nt"
|
|
650
|
-
url = url
|
|
650
|
+
if os.name == "nt":
|
|
651
|
+
url = url.removeprefix("file://")
|
|
651
652
|
remote = self.repo.remotes.create_anonymous(url)
|
|
652
653
|
parsed = urlparse(remote.url)
|
|
653
|
-
if parsed.scheme in ("git", "git+ssh", "ssh") or remote.url
|
|
654
|
+
if parsed.scheme in ("git", "git+ssh", "ssh") or is_scp_style_url(remote.url):
|
|
654
655
|
raise NotImplementedError
|
|
655
656
|
yield remote
|
|
656
657
|
|
scmrepo/git/lfs/client.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import logging
|
|
3
3
|
import os
|
|
4
|
-
import re
|
|
5
4
|
import shutil
|
|
6
5
|
from abc import abstractmethod
|
|
7
6
|
from collections.abc import Iterable, Iterator
|
|
@@ -10,6 +9,7 @@ from http import HTTPStatus
|
|
|
10
9
|
from tempfile import NamedTemporaryFile
|
|
11
10
|
from time import time
|
|
12
11
|
from typing import TYPE_CHECKING, Any, Optional
|
|
12
|
+
from urllib.parse import urlparse
|
|
13
13
|
|
|
14
14
|
import aiohttp
|
|
15
15
|
from aiohttp_retry import ExponentialRetry, RetryClient
|
|
@@ -20,6 +20,7 @@ from funcy import cached_property
|
|
|
20
20
|
|
|
21
21
|
from scmrepo.git.backend.dulwich import _get_ssh_vendor
|
|
22
22
|
from scmrepo.git.credentials import Credential, CredentialNotFoundError
|
|
23
|
+
from scmrepo.urls import SCP_REGEX, is_scp_style_url
|
|
23
24
|
|
|
24
25
|
from .exceptions import LFSError
|
|
25
26
|
from .pointer import Pointer
|
|
@@ -84,9 +85,9 @@ class LFSClient(AbstractContextManager):
|
|
|
84
85
|
|
|
85
86
|
@classmethod
|
|
86
87
|
def from_git_url(cls, git_url: str) -> "LFSClient":
|
|
87
|
-
if git_url.startswith(
|
|
88
|
+
if git_url.startswith("ssh://") or is_scp_style_url(git_url):
|
|
88
89
|
return _SSHLFSClient.from_git_url(git_url)
|
|
89
|
-
if git_url.startswith("https://"):
|
|
90
|
+
if git_url.startswith(("http://", "https://")):
|
|
90
91
|
return _HTTPLFSClient.from_git_url(git_url)
|
|
91
92
|
raise NotImplementedError(f"Unsupported Git URL: {git_url}")
|
|
92
93
|
|
|
@@ -213,11 +214,9 @@ class _HTTPLFSClient(LFSClient):
|
|
|
213
214
|
|
|
214
215
|
|
|
215
216
|
class _SSHLFSClient(LFSClient):
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
)
|
|
219
|
-
|
|
220
|
-
def __init__(self, url: str, host: str, port: int, path: str):
|
|
217
|
+
def __init__(
|
|
218
|
+
self, url: str, host: str, port: int, username: Optional[str], path: str
|
|
219
|
+
):
|
|
221
220
|
"""
|
|
222
221
|
Args:
|
|
223
222
|
url: LFS server URL.
|
|
@@ -228,25 +227,42 @@ class _SSHLFSClient(LFSClient):
|
|
|
228
227
|
super().__init__(url)
|
|
229
228
|
self.host = host
|
|
230
229
|
self.port = port
|
|
230
|
+
self.username = username
|
|
231
231
|
self.path = path
|
|
232
232
|
self._ssh = _get_ssh_vendor()
|
|
233
233
|
|
|
234
234
|
@classmethod
|
|
235
235
|
def from_git_url(cls, git_url: str) -> "_SSHLFSClient":
|
|
236
|
-
|
|
237
|
-
|
|
236
|
+
if scp_match := SCP_REGEX.match(git_url):
|
|
237
|
+
# Add an ssh:// prefix and replace the ':' with a '/'.
|
|
238
|
+
git_url = scp_match.expand(r"ssh://\1\2/\3")
|
|
239
|
+
|
|
240
|
+
parsed = urlparse(git_url)
|
|
241
|
+
if parsed.scheme != "ssh" or not parsed.hostname:
|
|
238
242
|
raise ValueError(f"Invalid Git SSH URL: {git_url}")
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
243
|
+
|
|
244
|
+
host = parsed.hostname
|
|
245
|
+
port = parsed.port or 22
|
|
246
|
+
path = parsed.path.lstrip("/")
|
|
247
|
+
username = parsed.username
|
|
248
|
+
|
|
249
|
+
url_path = path.removesuffix(".git") + ".git/info/lfs"
|
|
250
|
+
url = f"https://{host}/{url_path}"
|
|
251
|
+
return cls(url, host, port, username, path)
|
|
242
252
|
|
|
243
253
|
def _get_auth_header(self, *, upload: bool) -> dict:
|
|
244
254
|
return self._git_lfs_authenticate(
|
|
245
|
-
self.host, self.port,
|
|
255
|
+
self.host, self.port, self.username, self.path, upload=upload
|
|
246
256
|
).get("header", {})
|
|
247
257
|
|
|
248
258
|
def _git_lfs_authenticate(
|
|
249
|
-
self,
|
|
259
|
+
self,
|
|
260
|
+
host: str,
|
|
261
|
+
port: int,
|
|
262
|
+
username: Optional[str],
|
|
263
|
+
path: str,
|
|
264
|
+
*,
|
|
265
|
+
upload: bool = False,
|
|
250
266
|
) -> dict:
|
|
251
267
|
action = "upload" if upload else "download"
|
|
252
268
|
return json.loads(
|
|
@@ -254,7 +270,7 @@ class _SSHLFSClient(LFSClient):
|
|
|
254
270
|
command=f"git-lfs-authenticate {path} {action}",
|
|
255
271
|
host=host,
|
|
256
272
|
port=port,
|
|
257
|
-
username=
|
|
273
|
+
username=username,
|
|
258
274
|
).read()
|
|
259
275
|
)
|
|
260
276
|
|
scmrepo/urls.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
# from https://github.com/pypa/pip/blob/303fed36c1771de4063063a866776a9103972317/src/pip/_internal/vcs/git.py#L40
|
|
4
|
+
# SCP (Secure copy protocol) shorthand. e.g. 'git@example.com:foo/bar.git'
|
|
5
|
+
SCP_REGEX = re.compile(
|
|
6
|
+
r"""^
|
|
7
|
+
# Optional user, e.g. 'git@'
|
|
8
|
+
(\w+@)?
|
|
9
|
+
# Server, e.g. 'github.com'.
|
|
10
|
+
([^/:]+):
|
|
11
|
+
# The server-side path. e.g. 'user/project.git'. Must start with an
|
|
12
|
+
# alphanumeric character so as not to be confusable with a Windows paths
|
|
13
|
+
# like 'C:/foo/bar' or 'C:\foo\bar'.
|
|
14
|
+
(\w[^:]*)
|
|
15
|
+
$""",
|
|
16
|
+
re.VERBOSE,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def is_scp_style_url(url: str) -> bool:
|
|
21
|
+
return bool(SCP_REGEX.match(url))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: scmrepo
|
|
3
|
-
Version: 3.3.
|
|
3
|
+
Version: 3.3.2
|
|
4
4
|
Summary: scmrepo
|
|
5
5
|
Author-email: Iterative <support@dvc.org>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -16,7 +16,7 @@ Requires-Python: >=3.9
|
|
|
16
16
|
Description-Content-Type: text/x-rst
|
|
17
17
|
License-File: LICENSE
|
|
18
18
|
Requires-Dist: gitpython >3
|
|
19
|
-
Requires-Dist: dulwich >=0.
|
|
19
|
+
Requires-Dist: dulwich >=0.22.1
|
|
20
20
|
Requires-Dist: pygit2 >=1.14.0
|
|
21
21
|
Requires-Dist: pygtrie >=2.3.2
|
|
22
22
|
Requires-Dist: fsspec[tqdm] >=2024.2.0
|
|
@@ -26,22 +26,22 @@ Requires-Dist: funcy >=1.14
|
|
|
26
26
|
Requires-Dist: aiohttp-retry >=2.5.0
|
|
27
27
|
Requires-Dist: tqdm
|
|
28
28
|
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: mypy ==1.9.0 ; extra == 'dev'
|
|
29
30
|
Requires-Dist: scmrepo[tests] ; extra == 'dev'
|
|
31
|
+
Requires-Dist: types-certifi ; extra == 'dev'
|
|
32
|
+
Requires-Dist: types-mock ; extra == 'dev'
|
|
33
|
+
Requires-Dist: types-paramiko ; extra == 'dev'
|
|
34
|
+
Requires-Dist: types-tqdm ; extra == 'dev'
|
|
30
35
|
Provides-Extra: tests
|
|
31
|
-
Requires-Dist:
|
|
32
|
-
Requires-Dist:
|
|
33
|
-
Requires-Dist: pytest
|
|
34
|
-
Requires-Dist: pytest-
|
|
35
|
-
Requires-Dist:
|
|
36
|
-
Requires-Dist: pytest-
|
|
37
|
-
Requires-Dist: pytest-
|
|
38
|
-
Requires-Dist: pytest-
|
|
39
|
-
Requires-Dist:
|
|
40
|
-
Requires-Dist: paramiko ==3.4.0 ; extra == 'tests'
|
|
41
|
-
Requires-Dist: types-certifi ==2021.10.8.3 ; extra == 'tests'
|
|
42
|
-
Requires-Dist: types-mock ==5.1.0.20240311 ; extra == 'tests'
|
|
43
|
-
Requires-Dist: types-paramiko ==3.4.0.20240205 ; extra == 'tests'
|
|
44
|
-
Requires-Dist: types-tqdm ; extra == 'tests'
|
|
36
|
+
Requires-Dist: aioresponses <0.8,>=0.7 ; extra == 'tests'
|
|
37
|
+
Requires-Dist: paramiko <4,>=3.4.0 ; extra == 'tests'
|
|
38
|
+
Requires-Dist: pytest <9,>=7 ; extra == 'tests'
|
|
39
|
+
Requires-Dist: pytest-asyncio <1,>=0.23.2 ; extra == 'tests'
|
|
40
|
+
Requires-Dist: pytest-cov >=4.1.0 ; extra == 'tests'
|
|
41
|
+
Requires-Dist: pytest-docker <4,>=1 ; extra == 'tests'
|
|
42
|
+
Requires-Dist: pytest-mock ; extra == 'tests'
|
|
43
|
+
Requires-Dist: pytest-sugar ; extra == 'tests'
|
|
44
|
+
Requires-Dist: pytest-test-utils <0.2,>=0.1.0 ; extra == 'tests'
|
|
45
45
|
|
|
46
46
|
scmrepo
|
|
47
47
|
=======
|
|
@@ -6,6 +6,7 @@ scmrepo/fs.py,sha256=ARD8_TRSdHpuiaYwF7QXrQIyfl9AFj7ctv_Ltc6pAAs,7740
|
|
|
6
6
|
scmrepo/noscm.py,sha256=xraqlBek4zhFlHf1LvTMkgBM0hykgcTfMVkTNCpVlcQ,491
|
|
7
7
|
scmrepo/progress.py,sha256=fRUMvkcw6GLuVTP_tK7mGpKeJjbJulFP8rPUqyltkYQ,2157
|
|
8
8
|
scmrepo/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
scmrepo/urls.py,sha256=vEfW1h1lb7GXvMVBk-TNwqctpl-5xGMA2LbOPVn7t4Q,638
|
|
9
10
|
scmrepo/utils.py,sha256=_F3rVvPhES-A2JxLGob0RV8BOnHzxbA9aDPClA7_V8M,1512
|
|
10
11
|
scmrepo/git/__init__.py,sha256=6giWgQpgAlJslOkFmSAYkw8pb7nuefrbpZZf5Np3Iyo,17189
|
|
11
12
|
scmrepo/git/config.py,sha256=0t0OBmJ9SIa5tf22QdcGzhZfdMzzppvEmceUDg8ZPyE,943
|
|
@@ -18,11 +19,11 @@ scmrepo/git/backend/gitpython.py,sha256=RTDNUVLiJmhcRgdaJpApTQi6sMN-an9P9xKZBKwk
|
|
|
18
19
|
scmrepo/git/backend/dulwich/__init__.py,sha256=x6DsVddlEeUW39-0WPjRFjRlqc3U2f0opZ1Hm2pvBts,34820
|
|
19
20
|
scmrepo/git/backend/dulwich/asyncssh_vendor.py,sha256=y0mvx3tRR99Nv85e2n39nPYlhme8dplmQQSbSpx7FDM,11562
|
|
20
21
|
scmrepo/git/backend/dulwich/client.py,sha256=bcDroljSvNz6s5WWv9UVvZHKkOJOVTK_zU7YCq62TN4,2360
|
|
21
|
-
scmrepo/git/backend/pygit2/__init__.py,sha256=
|
|
22
|
+
scmrepo/git/backend/pygit2/__init__.py,sha256=gNOFVLgB8gpXSKfuL_-Vk7GhVkZk6DiwuJMahel0Ue0,37035
|
|
22
23
|
scmrepo/git/backend/pygit2/callbacks.py,sha256=Ky4YmUPhv9xjU_44ypBYIcaVHJixzaGb6t9HIeUmBP4,2751
|
|
23
24
|
scmrepo/git/backend/pygit2/filter.py,sha256=2NlWfQ7soXN1H7Es6-LctE74hpj3QKQTlYqXRH83VpM,2128
|
|
24
25
|
scmrepo/git/lfs/__init__.py,sha256=at5blRIKnKpg_g5dLRDsGWBFi6SbucRlF_DX6aAkGtE,257
|
|
25
|
-
scmrepo/git/lfs/client.py,sha256=
|
|
26
|
+
scmrepo/git/lfs/client.py,sha256=SLlGFC09YD55nTDQ7MjuKD9alql-eOriyNePZikFaYo,10171
|
|
26
27
|
scmrepo/git/lfs/exceptions.py,sha256=cLlImmPXWJJUl44S4xcRBa2T9wYRkWTaKQGwJylwOhA,77
|
|
27
28
|
scmrepo/git/lfs/fetch.py,sha256=ADNpskbDrvMI7ru4AiOf_c1gfw8TQ7Wct0EiN2Pq-qc,4683
|
|
28
29
|
scmrepo/git/lfs/object.py,sha256=rAYY_z9EYoHPfbpF1QHwL7ecYgaETPyCl-zBx0E1oIQ,337
|
|
@@ -30,8 +31,8 @@ scmrepo/git/lfs/pointer.py,sha256=BcVbtjoOUG9cEzyJSJDeweqehGZvq43P6NNLDYUGYEI,31
|
|
|
30
31
|
scmrepo/git/lfs/progress.py,sha256=ELlBs2SeXhAcnPDN23w3FTeBRgB9RGqBD2CFMS6n9Xs,4750
|
|
31
32
|
scmrepo/git/lfs/smudge.py,sha256=1O_fznptWo4CKXqcJgUoWP6cgWWhvGAZ3d87kasG3cQ,1610
|
|
32
33
|
scmrepo/git/lfs/storage.py,sha256=2weDldy6MFrA8IDzBczsPy8fBWCp4FKaJTT0f6eIT64,2396
|
|
33
|
-
scmrepo-3.3.
|
|
34
|
-
scmrepo-3.3.
|
|
35
|
-
scmrepo-3.3.
|
|
36
|
-
scmrepo-3.3.
|
|
37
|
-
scmrepo-3.3.
|
|
34
|
+
scmrepo-3.3.2.dist-info/LICENSE,sha256=-1jhbPjoIVHR0cEgahL4Zhct75Ff4MzYCR_jOaJDPq8,11340
|
|
35
|
+
scmrepo-3.3.2.dist-info/METADATA,sha256=RoEcoxC9ReqV3Eh0lgIQRHVnBLrZeRs5z4px6SpDZb0,4730
|
|
36
|
+
scmrepo-3.3.2.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
37
|
+
scmrepo-3.3.2.dist-info/top_level.txt,sha256=iunjod6w3GogERsAYfLRupnANXnqzX3jbIfbeIQG5cc,8
|
|
38
|
+
scmrepo-3.3.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|