scmrepo 3.3.9__py3-none-any.whl → 3.3.11__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/fs.py CHANGED
@@ -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()
scmrepo/git/__init__.py CHANGED
@@ -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 re-using the most recently used backend when possible. When
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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  import asyncio
4
4
  import os
5
- from collections.abc import Coroutine, Iterator, Sequence
5
+ from collections.abc import Coroutine, Iterator
6
6
  from typing import (
7
7
  TYPE_CHECKING,
8
8
  Any,
@@ -18,6 +18,7 @@ from scmrepo.asyn import BaseAsyncObject, sync_wrapper
18
18
  from scmrepo.exceptions import AuthError
19
19
 
20
20
  if TYPE_CHECKING:
21
+ from collections.abc import Sequence
21
22
  from pathlib import Path
22
23
 
23
24
  from asyncssh.auth import KbdIntPrompts, KbdIntResponse
@@ -40,6 +41,12 @@ async def _read_all(read: Callable[[int], Coroutine], n: Optional[int] = None) -
40
41
  return b"".join(result)
41
42
 
42
43
 
44
+ async def _getpass(*args, **kwargs) -> str:
45
+ from getpass import getpass
46
+
47
+ return await asyncio.to_thread(getpass, *args, **kwargs)
48
+
49
+
43
50
  class _StderrWrapper:
44
51
  def __init__(self, stderr: "SSHReader", loop: asyncio.AbstractEventLoop) -> None:
45
52
  self.stderr = stderr
@@ -97,45 +104,6 @@ class AsyncSSHWrapper(BaseAsyncObject):
97
104
  close = sync_wrapper(_close)
98
105
 
99
106
 
100
- # NOTE: Github's SSH server does not strictly comply with the SSH protocol.
101
- # When validating a public key using the rsa-sha2-256 or rsa-sha2-512
102
- # signature algorithms, RFC4252 + RFC8332 state that the server should respond
103
- # with the same algorithm in SSH_MSG_USERAUTH_PK_OK. Github's server always
104
- # returns "ssh-rsa" rather than the correct sha2 algorithm name (likely for
105
- # backwards compatibility with old SSH client reasons). This behavior causes
106
- # asyncssh to fail with a key-mismatch error (since asyncssh expects the server
107
- # to behave properly).
108
- #
109
- # See also:
110
- # https://www.ietf.org/rfc/rfc4252.txt
111
- # https://www.ietf.org/rfc/rfc8332.txt
112
- def _process_public_key_ok_gh(self, _pkttype, _pktid, packet):
113
- from asyncssh.misc import ProtocolError
114
-
115
- algorithm = packet.get_string()
116
- key_data = packet.get_string()
117
- packet.check_end()
118
-
119
- # pylint: disable=protected-access
120
- if (
121
- (
122
- algorithm == b"ssh-rsa"
123
- and self._keypair.algorithm
124
- not in (
125
- b"ssh-rsa",
126
- b"rsa-sha2-256",
127
- b"rsa-sha2-512",
128
- )
129
- )
130
- or (algorithm not in (b"ssh-rsa", self._keypair.algorithm))
131
- or key_data != self._keypair.public_data
132
- ):
133
- raise ProtocolError("Key mismatch")
134
-
135
- self.create_task(self._send_signed_request())
136
- return True
137
-
138
-
139
107
  class InteractiveSSHClient(SSHClient):
140
108
  _conn: Optional["SSHClientConnection"] = None
141
109
  _keys_to_try: Optional[list["FilePath"]] = None
@@ -171,7 +139,7 @@ class InteractiveSSHClient(SSHClient):
171
139
  self._keys_to_try = []
172
140
  options = self._conn._options # pylint: disable=protected-access
173
141
  config = options.config
174
- client_keys = cast(Sequence["FilePath"], config.get("IdentityFile", ()))
142
+ client_keys = cast("Sequence[FilePath]", config.get("IdentityFile", ()))
175
143
  if not client_keys:
176
144
  client_keys = [
177
145
  os.path.expanduser(os.path.join("~", ".ssh", path))
@@ -202,8 +170,6 @@ class InteractiveSSHClient(SSHClient):
202
170
  return None
203
171
 
204
172
  async def _read_private_key_interactive(self, path: "FilePath") -> "SSHKey":
205
- from getpass import getpass
206
-
207
173
  from asyncssh.public_key import (
208
174
  KeyEncryptionError,
209
175
  KeyImportError,
@@ -215,11 +181,8 @@ class InteractiveSSHClient(SSHClient):
215
181
  if passphrase:
216
182
  return read_private_key(path, passphrase=passphrase)
217
183
 
218
- loop = asyncio.get_running_loop()
219
184
  for _ in range(3):
220
- passphrase = await loop.run_in_executor(
221
- None, getpass, f"Enter passphrase for key '{path}': "
222
- )
185
+ passphrase = await _getpass(f"Enter passphrase for key {path!r}: ")
223
186
  if passphrase:
224
187
  try:
225
188
  key = read_private_key(path, passphrase=passphrase)
@@ -239,23 +202,20 @@ class InteractiveSSHClient(SSHClient):
239
202
  lang: str,
240
203
  prompts: "KbdIntPrompts",
241
204
  ) -> Optional["KbdIntResponse"]:
242
- from getpass import getpass
243
-
244
205
  if os.environ.get("GIT_TERMINAL_PROMPT") == "0":
245
206
  return None
246
207
 
247
- def _getpass(prompt: str) -> str:
248
- return getpass(prompt=prompt).rstrip()
249
-
250
208
  if instructions:
251
209
  pass
252
- loop = asyncio.get_running_loop()
253
- return [
254
- await loop.run_in_executor(
255
- None, _getpass, f"({name}) {prompt}" if name else prompt
256
- )
257
- for prompt, _ in prompts
258
- ]
210
+
211
+ response: list[str] = []
212
+ for prompt, _echo in prompts:
213
+ p = await _getpass(f"({name}) {prompt}" if name else prompt)
214
+ response.append(p.rstrip())
215
+ return response
216
+
217
+ async def password_auth_requested(self) -> str:
218
+ return await _getpass()
259
219
 
260
220
 
261
221
  class AsyncSSHVendor(BaseAsyncObject, SSHVendor):
@@ -286,12 +246,6 @@ class AsyncSSHVendor(BaseAsyncObject, SSHVendor):
286
246
  key_filename: Optional path to private keyfile
287
247
  """
288
248
  import asyncssh
289
- from asyncssh.auth import MSG_USERAUTH_PK_OK, _ClientPublicKeyAuth
290
-
291
- # pylint: disable=protected-access
292
- _ClientPublicKeyAuth._packet_handlers[MSG_USERAUTH_PK_OK] = (
293
- _process_public_key_ok_gh
294
- )
295
249
 
296
250
  try:
297
251
  conn = await asyncssh.connect(
@@ -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 re-uses
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
@@ -289,7 +289,9 @@ class Pygit2Backend(BaseGitBackend): # pylint:disable=abstract-method
289
289
  bare = True
290
290
  try:
291
291
  with RemoteCallbacks(progress=progress) as cb:
292
- repo = clone_repository(url, to_path, callbacks=cb, bare=bare)
292
+ repo = clone_repository(
293
+ url, os.fspath(to_path), callbacks=cb, bare=bare
294
+ )
293
295
  if mirror:
294
296
  cls._set_mirror(repo, progress=progress)
295
297
  except GitError as exc:
@@ -720,7 +722,7 @@ class Pygit2Backend(BaseGitBackend): # pylint:disable=abstract-method
720
722
  for refname in remote_refs:
721
723
  if fnmatch.fnmatch(refname, lh):
722
724
  src = refname
723
- dst = f"{rh_prefix}{refname[len(lh_prefix):]}"
725
+ dst = f"{rh_prefix}{refname[len(lh_prefix) :]}"
724
726
  result[dst] = cb.result.get(
725
727
  src, _default_status(src, dst, remote_refs)
726
728
  )
@@ -66,6 +66,8 @@ class RemoteCallbacks(_RemoteCallbacks, AbstractContextManager):
66
66
  else:
67
67
  creds = Credential(username=username_from_url, url=url).fill()
68
68
  self._store_credentials = creds
69
+ assert creds.username is not None
70
+ assert creds.password is not None
69
71
  return UserPass(creds.username, creds.password)
70
72
  except CredentialNotFoundError:
71
73
  pass
@@ -324,7 +324,7 @@ def _input_tty(prompt: str = "Username: ") -> str:
324
324
  try:
325
325
  fd = os.open(
326
326
  "/dev/tty",
327
- os.O_RDWR | os.O_NOCTTY, # pylint: disable=no-member
327
+ os.O_RDWR | os.O_NOCTTY, # type: ignore[attr-defined]
328
328
  )
329
329
  tty = io.FileIO(fd, "w+")
330
330
  stack.enter_context(tty)
scmrepo/git/lfs/fetch.py CHANGED
@@ -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'{root.rstrip("/")}/**'}:
125
+ if path in {root, f"{root.rstrip('/')}/**"}:
126
126
  include = []
127
127
  else:
128
128
  root = "/"
scmrepo/git/objects.py CHANGED
@@ -160,7 +160,7 @@ class GitTrie:
160
160
  }
161
161
  )
162
162
 
163
- return cast(dict, ret)
163
+ return cast("dict", ret)
164
164
 
165
165
 
166
166
  @dataclass
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: scmrepo
3
- Version: 3.3.9
3
+ Version: 3.3.11
4
4
  Summary: scmrepo
5
5
  Author-email: Iterative <support@dvc.org>
6
6
  License: Apache-2.0
@@ -38,12 +38,13 @@ Requires-Dist: pytest-sugar; extra == "tests"
38
38
  Requires-Dist: pytest-test-utils<0.2,>=0.1.0; extra == "tests"
39
39
  Requires-Dist: proxy.py; extra == "tests"
40
40
  Provides-Extra: dev
41
- Requires-Dist: mypy==1.13.0; extra == "dev"
41
+ Requires-Dist: mypy==1.15.0; extra == "dev"
42
42
  Requires-Dist: scmrepo[tests]; extra == "dev"
43
43
  Requires-Dist: types-certifi; extra == "dev"
44
44
  Requires-Dist: types-mock; extra == "dev"
45
45
  Requires-Dist: types-paramiko; extra == "dev"
46
46
  Requires-Dist: types-tqdm; extra == "dev"
47
+ Dynamic: license-file
47
48
 
48
49
  scmrepo
49
50
  =======
@@ -2,37 +2,37 @@ scmrepo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  scmrepo/asyn.py,sha256=eckXZkVngwbNNyO04eAJUZxsTy7kvsdz0TUGaWpJpf0,1524
3
3
  scmrepo/base.py,sha256=pgddGR7UeZoEq2d5lip1vjbxTmpwTnKFH4JwHy-Fmho,3007
4
4
  scmrepo/exceptions.py,sha256=vR8BuCKgKh9lMnCemzNYGHiJctioOmhn_Kv5m8XO69Y,1053
5
- scmrepo/fs.py,sha256=ARD8_TRSdHpuiaYwF7QXrQIyfl9AFj7ctv_Ltc6pAAs,7740
5
+ scmrepo/fs.py,sha256=8efNf_D5IQb-N42w-apAkG3Uwe8gJA_HVflniY9gX6M,6639
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
9
  scmrepo/urls.py,sha256=vEfW1h1lb7GXvMVBk-TNwqctpl-5xGMA2LbOPVn7t4Q,638
10
10
  scmrepo/utils.py,sha256=_F3rVvPhES-A2JxLGob0RV8BOnHzxbA9aDPClA7_V8M,1512
11
- scmrepo/git/__init__.py,sha256=6giWgQpgAlJslOkFmSAYkw8pb7nuefrbpZZf5Np3Iyo,17189
11
+ scmrepo/git/__init__.py,sha256=elQErQSqekg-XWfxjNmEV0X00xtvy0LDFvy9nmv71Ls,17187
12
12
  scmrepo/git/config.py,sha256=0t0OBmJ9SIa5tf22QdcGzhZfdMzzppvEmceUDg8ZPyE,943
13
- scmrepo/git/credentials.py,sha256=VYk16-KHHD_JMVEeVsWI1ZKp8DUaGi8_GDxB-Fjby6g,22311
14
- scmrepo/git/objects.py,sha256=vqeFpUlMFHL9Yv1h3wTA7mbRWHCVC_4KgLy5aAISD2g,4674
13
+ scmrepo/git/credentials.py,sha256=tn8TlyCJUz8HOF7eL6F5Yj4n9vREsFwXrjLU1isi13s,22312
14
+ scmrepo/git/objects.py,sha256=qx8zAHZIrr0SDbZGD9wVShlMZK57nef8stIeRVqYdCU,4676
15
15
  scmrepo/git/stash.py,sha256=wKWnYj_xpdT_3pvHiXtE7_I_By4S-Zbxf4Lv-ZY2sxI,2785
16
16
  scmrepo/git/backend/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
17
  scmrepo/git/backend/base.py,sha256=nVMkUIeSVu-ZLCd2QPxMfTgrjnTOejM1UET9R7qKJRc,13560
18
- scmrepo/git/backend/gitpython.py,sha256=k2MHg1sHZRFNzfQir3IlTpxrQHieflKwwHtnjzlPXZU,25341
18
+ scmrepo/git/backend/gitpython.py,sha256=myfZY2x27DbDvIBUXzDjswXEViR5MKH0qgowwzcn7aY,25340
19
19
  scmrepo/git/backend/dulwich/__init__.py,sha256=7UJJyEPqgc2tfPs1WN_XdvRkMsGyFA1HVgTf7mrgLNE,35098
20
- scmrepo/git/backend/dulwich/asyncssh_vendor.py,sha256=clqsPC9ayulwgb5VLp-69hEbN9XMPvjh_0diBlH4wv4,11520
20
+ scmrepo/git/backend/dulwich/asyncssh_vendor.py,sha256=E3AyXok4S4FwsG9cMdswklf1bYRMmcmQM7Unri2fSVo,9882
21
21
  scmrepo/git/backend/dulwich/client.py,sha256=XjNBfOp0L8M3iPpqcX_4bmXsO7hwrkyqg5wMbZULD-I,2358
22
- scmrepo/git/backend/pygit2/__init__.py,sha256=Is9OjsiDdUTRBUYrG1qoK5AP6Cjcy2tAk71nsmkzLf4,36316
23
- scmrepo/git/backend/pygit2/callbacks.py,sha256=BFIFMzUpSC-CtNY50yTqksAusASgidzsQrG-B-Ry2lw,2749
22
+ scmrepo/git/backend/pygit2/__init__.py,sha256=7meRePoB_bUx0UY2wPm2nxI4DFabn5nenVP8Cyf7AZc,36366
23
+ scmrepo/git/backend/pygit2/callbacks.py,sha256=Mk-2e5dfJPNLsczRiK4rTnjHd5ZyonmegdwiFwM9Anc,2849
24
24
  scmrepo/git/backend/pygit2/filter.py,sha256=8Ibn_2oXM32YRpyovxGKYhtPZLUYTBLDi9--bScOP00,2198
25
25
  scmrepo/git/lfs/__init__.py,sha256=at5blRIKnKpg_g5dLRDsGWBFi6SbucRlF_DX6aAkGtE,257
26
26
  scmrepo/git/lfs/client.py,sha256=64u5lJijUASNJ4H_Eg7aEb8j3z3eluuNUWGnYvfNnvo,10187
27
27
  scmrepo/git/lfs/exceptions.py,sha256=cLlImmPXWJJUl44S4xcRBa2T9wYRkWTaKQGwJylwOhA,77
28
- scmrepo/git/lfs/fetch.py,sha256=45JGAKHAdANShZ9XEVXq0EYuKBgGmlMK2TX8z9Og8wg,5530
28
+ scmrepo/git/lfs/fetch.py,sha256=YynrODVZWYTfnqOmLOBHjXZQ6sf_lnyDuJHttSl3TOU,5530
29
29
  scmrepo/git/lfs/object.py,sha256=rAYY_z9EYoHPfbpF1QHwL7ecYgaETPyCl-zBx0E1oIQ,337
30
30
  scmrepo/git/lfs/pointer.py,sha256=BcVbtjoOUG9cEzyJSJDeweqehGZvq43P6NNLDYUGYEI,3181
31
31
  scmrepo/git/lfs/progress.py,sha256=AcWvygDG0ee__Jec5BlRr58F-lAj3d4Z_j7JbW3OUcI,4733
32
32
  scmrepo/git/lfs/smudge.py,sha256=1O_fznptWo4CKXqcJgUoWP6cgWWhvGAZ3d87kasG3cQ,1610
33
33
  scmrepo/git/lfs/storage.py,sha256=nx_HvHHC1sf15Qgbsj8jEOkdHXkZ8VUEh8QBtt9sLwI,2348
34
- scmrepo-3.3.9.dist-info/LICENSE,sha256=-1jhbPjoIVHR0cEgahL4Zhct75Ff4MzYCR_jOaJDPq8,11340
35
- scmrepo-3.3.9.dist-info/METADATA,sha256=rTixvRBdWMPNf_op8TVliMDccLarhPf4_hTMRaZNMcE,4792
36
- scmrepo-3.3.9.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
37
- scmrepo-3.3.9.dist-info/top_level.txt,sha256=iunjod6w3GogERsAYfLRupnANXnqzX3jbIfbeIQG5cc,8
38
- scmrepo-3.3.9.dist-info/RECORD,,
34
+ scmrepo-3.3.11.dist-info/licenses/LICENSE,sha256=-1jhbPjoIVHR0cEgahL4Zhct75Ff4MzYCR_jOaJDPq8,11340
35
+ scmrepo-3.3.11.dist-info/METADATA,sha256=2edjOxEKBOCfYNiG_cFDExx5Q8P1eXQfrMTAtSlOzZ8,4815
36
+ scmrepo-3.3.11.dist-info/WHEEL,sha256=ooBFpIzZCPdw3uqIQsOo4qqbA4ZRPxHnOH7peeONza0,91
37
+ scmrepo-3.3.11.dist-info/top_level.txt,sha256=iunjod6w3GogERsAYfLRupnANXnqzX3jbIfbeIQG5cc,8
38
+ scmrepo-3.3.11.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.6.0)
2
+ Generator: setuptools (80.0.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5