dvc-objects 5.0.0__tar.gz → 5.1.1__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.
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/.github/workflows/tests.yml +8 -8
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/.pre-commit-config.yaml +3 -8
- {dvc-objects-5.0.0/src/dvc_objects.egg-info → dvc_objects-5.1.1}/PKG-INFO +8 -7
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/noxfile.py +4 -9
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/pyproject.toml +10 -7
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/src/dvc_objects/db.py +10 -16
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/src/dvc_objects/executors.py +12 -59
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/src/dvc_objects/fs/__init__.py +3 -3
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/src/dvc_objects/fs/base.py +50 -58
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/src/dvc_objects/fs/callbacks.py +1 -1
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/src/dvc_objects/fs/errors.py +2 -2
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/src/dvc_objects/fs/generic.py +18 -17
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/src/dvc_objects/fs/local.py +2 -2
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/src/dvc_objects/fs/utils.py +9 -8
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/src/dvc_objects/obj.py +1 -1
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/src/dvc_objects/transfer.py +3 -3
- {dvc-objects-5.0.0 → dvc_objects-5.1.1/src/dvc_objects.egg-info}/PKG-INFO +8 -7
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/src/dvc_objects.egg-info/requires.txt +2 -2
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/tests/benchmarks/test_fs.py +3 -3
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/tests/fs/test_localfs.py +2 -3
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/.cruft.json +0 -0
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/.gitattributes +0 -0
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/.github/dependabot.yml +0 -0
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/.github/workflows/release.yml +0 -0
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/.github/workflows/update-template.yaml +0 -0
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/.gitignore +0 -0
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/CODE_OF_CONDUCT.rst +0 -0
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/CONTRIBUTING.rst +0 -0
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/LICENSE +0 -0
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/README.rst +0 -0
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/setup.cfg +0 -0
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/src/dvc_objects/__init__.py +0 -0
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/src/dvc_objects/compat.py +0 -0
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/src/dvc_objects/errors.py +0 -0
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/src/dvc_objects/fs/memory.py +0 -0
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/src/dvc_objects/fs/scheme.py +0 -0
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/src/dvc_objects/fs/system.py +0 -0
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/src/dvc_objects/py.typed +0 -0
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/src/dvc_objects.egg-info/SOURCES.txt +0 -0
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/src/dvc_objects.egg-info/dependency_links.txt +0 -0
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/src/dvc_objects.egg-info/top_level.txt +0 -0
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/tests/__init__.py +0 -0
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/tests/benchmarks/__init__.py +0 -0
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/tests/conftest.py +0 -0
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/tests/fs/__init__.py +0 -0
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/tests/fs/test_callbacks.py +0 -0
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/tests/fs/test_generic.py +0 -0
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/tests/fs/test_system.py +0 -0
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/tests/fs/test_utils.py +0 -0
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/tests/test_odb.py +0 -0
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/tests/test_reflink.py +0 -0
- {dvc-objects-5.0.0 → dvc_objects-5.1.1}/tests/test_transfer.py +0 -0
|
@@ -20,11 +20,10 @@ jobs:
|
|
|
20
20
|
strategy:
|
|
21
21
|
fail-fast: false
|
|
22
22
|
matrix:
|
|
23
|
-
os: [ubuntu-
|
|
24
|
-
pyv: ['3.
|
|
23
|
+
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
24
|
+
pyv: ['3.9', '3.10', '3.11', '3.12', '3.13']
|
|
25
25
|
include:
|
|
26
|
-
- {os: ubuntu-latest, pyv: 'pypy3.
|
|
27
|
-
- {os: macos-14, pyv: '3.12'}
|
|
26
|
+
- {os: ubuntu-latest, pyv: 'pypy3.9'}
|
|
28
27
|
|
|
29
28
|
steps:
|
|
30
29
|
- name: Check out the repository
|
|
@@ -37,7 +36,8 @@ jobs:
|
|
|
37
36
|
with:
|
|
38
37
|
python-version: ${{ matrix.pyv }}
|
|
39
38
|
cache: pip
|
|
40
|
-
|
|
39
|
+
allow-prereleases: true
|
|
40
|
+
cache-dependency-path: pyproject.toml
|
|
41
41
|
|
|
42
42
|
- name: Upgrade pip and nox
|
|
43
43
|
run: |
|
|
@@ -45,14 +45,14 @@ jobs:
|
|
|
45
45
|
pip --version
|
|
46
46
|
nox --version
|
|
47
47
|
|
|
48
|
-
- name: Lint code
|
|
49
|
-
run: nox -s lint
|
|
48
|
+
- name: Lint code
|
|
49
|
+
run: nox -s lint
|
|
50
50
|
|
|
51
51
|
- name: Run tests
|
|
52
52
|
run: nox -s tests-${{ matrix.nox_pyv || matrix.pyv }} -- --cov-report=xml
|
|
53
53
|
|
|
54
54
|
- name: Upload coverage report
|
|
55
|
-
uses: codecov/codecov-action@
|
|
55
|
+
uses: codecov/codecov-action@v5
|
|
56
56
|
|
|
57
57
|
- name: Run benchmarks
|
|
58
58
|
run: nox -s bench
|
|
@@ -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,18 +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.11.12'
|
|
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.1
|
|
30
30
|
hooks:
|
|
31
31
|
- id: codespell
|
|
32
32
|
additional_dependencies: ["tomli"]
|
|
33
|
-
- repo: https://github.com/asottile/pyupgrade
|
|
34
|
-
rev: v3.15.0
|
|
35
|
-
hooks:
|
|
36
|
-
- id: pyupgrade
|
|
37
|
-
args: [--py38-plus]
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: dvc-objects
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.1.1
|
|
4
4
|
Summary: dvc objects - filesystem and object-db level abstractions to use in dvc and dvc-data
|
|
5
5
|
Author-email: Iterative <support@dvc.org>
|
|
6
6
|
License: Apache-2.0
|
|
7
7
|
Project-URL: Issues, https://github.com/iterative/dvc-objects/issues
|
|
8
8
|
Project-URL: Source, https://github.com/iterative/dvc-objects
|
|
9
9
|
Classifier: Programming Language :: Python :: 3
|
|
10
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
11
10
|
Classifier: Programming Language :: Python :: 3.9
|
|
12
11
|
Classifier: Programming Language :: Python :: 3.10
|
|
13
12
|
Classifier: Programming Language :: Python :: 3.11
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
15
|
Classifier: Development Status :: 4 - Beta
|
|
16
|
-
Requires-Python: >=3.
|
|
16
|
+
Requires-Python: >=3.9
|
|
17
17
|
Description-Content-Type: text/x-rst
|
|
18
18
|
License-File: LICENSE
|
|
19
|
-
Requires-Dist: funcy>=1.14; python_version < "3.12"
|
|
20
19
|
Requires-Dist: fsspec>=2024.2.0
|
|
20
|
+
Requires-Dist: funcy>=1.14; python_version < "3.12"
|
|
21
21
|
Provides-Extra: tests
|
|
22
|
-
Requires-Dist: pytest<
|
|
22
|
+
Requires-Dist: pytest<9,>=7; extra == "tests"
|
|
23
23
|
Requires-Dist: pytest-sugar; extra == "tests"
|
|
24
24
|
Requires-Dist: pytest-cov>=4.1.0; extra == "tests"
|
|
25
25
|
Requires-Dist: pytest-mock; extra == "tests"
|
|
@@ -28,7 +28,8 @@ Requires-Dist: pytest-asyncio<1,>=0.23.2; extra == "tests"
|
|
|
28
28
|
Requires-Dist: reflink; extra == "tests"
|
|
29
29
|
Provides-Extra: dev
|
|
30
30
|
Requires-Dist: dvc-objects[tests]; extra == "dev"
|
|
31
|
-
Requires-Dist: mypy==1.
|
|
31
|
+
Requires-Dist: mypy==1.16.0; extra == "dev"
|
|
32
|
+
Dynamic: license-file
|
|
32
33
|
|
|
33
34
|
DVC objects
|
|
34
35
|
===========
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Automation using nox."""
|
|
2
|
+
|
|
2
3
|
import glob
|
|
3
4
|
import os
|
|
4
5
|
|
|
@@ -11,7 +12,9 @@ locations = "src", "tests"
|
|
|
11
12
|
pip_dev_flags = ["--use-pep517"] # reflink package is still missing wheels
|
|
12
13
|
|
|
13
14
|
|
|
14
|
-
@nox.session(
|
|
15
|
+
@nox.session(
|
|
16
|
+
python=["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "pypy3.8", "pypy3.9"]
|
|
17
|
+
)
|
|
15
18
|
def tests(session: nox.Session) -> None:
|
|
16
19
|
session.install(".[tests]", *pip_dev_flags)
|
|
17
20
|
session.run(
|
|
@@ -39,14 +42,6 @@ def lint(session: nox.Session) -> None:
|
|
|
39
42
|
session.run("python", "-m", "mypy")
|
|
40
43
|
|
|
41
44
|
|
|
42
|
-
@nox.session
|
|
43
|
-
def safety(session: nox.Session) -> None:
|
|
44
|
-
"""Scan dependencies for insecure packages."""
|
|
45
|
-
session.install(".[dev]", *pip_dev_flags)
|
|
46
|
-
session.install("safety")
|
|
47
|
-
session.run("safety", "check", "--full-report")
|
|
48
|
-
|
|
49
|
-
|
|
50
45
|
@nox.session
|
|
51
46
|
def build(session: nox.Session) -> None:
|
|
52
47
|
session.install("build", "setuptools", "twine")
|
|
@@ -12,18 +12,18 @@ license = {text = "Apache-2.0"}
|
|
|
12
12
|
authors = [{ name = "Iterative", email = "support@dvc.org" }]
|
|
13
13
|
classifiers = [
|
|
14
14
|
"Programming Language :: Python :: 3",
|
|
15
|
-
"Programming Language :: Python :: 3.8",
|
|
16
15
|
"Programming Language :: Python :: 3.9",
|
|
17
16
|
"Programming Language :: Python :: 3.10",
|
|
18
17
|
"Programming Language :: Python :: 3.11",
|
|
19
18
|
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Programming Language :: Python :: 3.13",
|
|
20
20
|
"Development Status :: 4 - Beta",
|
|
21
21
|
]
|
|
22
|
-
requires-python = ">=3.
|
|
22
|
+
requires-python = ">=3.9"
|
|
23
23
|
dynamic = ["version"]
|
|
24
24
|
dependencies = [
|
|
25
|
-
"funcy>=1.14; python_version < '3.12'",
|
|
26
25
|
"fsspec>=2024.2.0",
|
|
26
|
+
"funcy>=1.14; python_version < '3.12'",
|
|
27
27
|
]
|
|
28
28
|
|
|
29
29
|
[project.urls]
|
|
@@ -32,7 +32,7 @@ Source = "https://github.com/iterative/dvc-objects"
|
|
|
32
32
|
|
|
33
33
|
[project.optional-dependencies]
|
|
34
34
|
tests = [
|
|
35
|
-
"pytest
|
|
35
|
+
"pytest>=7,<9",
|
|
36
36
|
"pytest-sugar",
|
|
37
37
|
"pytest-cov>=4.1.0",
|
|
38
38
|
"pytest-mock",
|
|
@@ -42,7 +42,7 @@ tests = [
|
|
|
42
42
|
]
|
|
43
43
|
dev = [
|
|
44
44
|
"dvc-objects[tests]",
|
|
45
|
-
"mypy==1.
|
|
45
|
+
"mypy==1.16.0",
|
|
46
46
|
]
|
|
47
47
|
|
|
48
48
|
[tool.setuptools.packages.find]
|
|
@@ -102,8 +102,13 @@ module = [
|
|
|
102
102
|
|
|
103
103
|
[tool.codespell]
|
|
104
104
|
ignore-words-list = "cachable,"
|
|
105
|
+
skip = "CODE_OF_CONDUCT.rst"
|
|
105
106
|
|
|
106
107
|
[tool.ruff]
|
|
108
|
+
output-format = "full"
|
|
109
|
+
show-fixes = true
|
|
110
|
+
|
|
111
|
+
[tool.ruff.lint]
|
|
107
112
|
ignore = [
|
|
108
113
|
"ISC001", # single-line-implicit-string-concatenation
|
|
109
114
|
"PLR2004", # magic-value-comparison
|
|
@@ -156,8 +161,6 @@ select = [
|
|
|
156
161
|
"W", # pycodestyle - Warning
|
|
157
162
|
"YTT", # flake8-2020
|
|
158
163
|
]
|
|
159
|
-
show-source = true
|
|
160
|
-
show-fixes = true
|
|
161
164
|
|
|
162
165
|
[tool.ruff.lint.flake8-pytest-style]
|
|
163
166
|
fixture-parentheses = false
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import itertools
|
|
2
2
|
import logging
|
|
3
|
+
from collections.abc import Iterable, Iterator
|
|
3
4
|
from contextlib import suppress
|
|
4
5
|
from functools import partial
|
|
5
6
|
from io import BytesIO
|
|
@@ -7,11 +8,7 @@ from typing import (
|
|
|
7
8
|
TYPE_CHECKING,
|
|
8
9
|
BinaryIO,
|
|
9
10
|
Callable,
|
|
10
|
-
Iterable,
|
|
11
|
-
Iterator,
|
|
12
|
-
List,
|
|
13
11
|
Optional,
|
|
14
|
-
Tuple,
|
|
15
12
|
Union,
|
|
16
13
|
cast,
|
|
17
14
|
)
|
|
@@ -126,7 +123,7 @@ class ObjectDB:
|
|
|
126
123
|
raise ObjectDBPermissionError("Cannot add to read-only ODB")
|
|
127
124
|
|
|
128
125
|
if isinstance(data, bytes):
|
|
129
|
-
fobj:
|
|
126
|
+
fobj: BinaryIO = BytesIO(data)
|
|
130
127
|
size: Optional[int] = len(data)
|
|
131
128
|
else:
|
|
132
129
|
fobj = data
|
|
@@ -139,9 +136,9 @@ class ObjectDB:
|
|
|
139
136
|
|
|
140
137
|
def add(
|
|
141
138
|
self,
|
|
142
|
-
path: Union["AnyFSPath",
|
|
139
|
+
path: Union["AnyFSPath", list["AnyFSPath"]],
|
|
143
140
|
fs: "FileSystem",
|
|
144
|
-
oid: Union[str,
|
|
141
|
+
oid: Union[str, list[str]],
|
|
145
142
|
hardlink: bool = False,
|
|
146
143
|
callback: "Callback" = DEFAULT_CALLBACK,
|
|
147
144
|
check_exists: bool = True,
|
|
@@ -206,7 +203,7 @@ class ObjectDB:
|
|
|
206
203
|
for oid in self.all():
|
|
207
204
|
self.delete(oid)
|
|
208
205
|
|
|
209
|
-
def _oid_parts(self, oid: str) ->
|
|
206
|
+
def _oid_parts(self, oid: str) -> tuple[str, str]:
|
|
210
207
|
return oid[:2], oid[2:]
|
|
211
208
|
|
|
212
209
|
def oid_to_path(self, oid) -> str:
|
|
@@ -218,7 +215,7 @@ class ObjectDB:
|
|
|
218
215
|
jobs: Optional[int] = None,
|
|
219
216
|
) -> Iterator[str]:
|
|
220
217
|
if prefixes:
|
|
221
|
-
paths: Union[str,
|
|
218
|
+
paths: Union[str, list[str]] = list(map(self.oid_to_path, prefixes))
|
|
222
219
|
if len(paths) == 1:
|
|
223
220
|
paths = paths[0]
|
|
224
221
|
prefix = True
|
|
@@ -262,14 +259,11 @@ class ObjectDB:
|
|
|
262
259
|
prefixes: Optional[Iterable[str]] = None,
|
|
263
260
|
jobs: Optional[int] = None,
|
|
264
261
|
) -> Iterator[str]:
|
|
265
|
-
|
|
266
|
-
for oid in self._list_oids(prefixes=prefixes, jobs=jobs):
|
|
262
|
+
for i, oid in enumerate(self._list_oids(prefixes=prefixes, jobs=jobs), start=1):
|
|
267
263
|
yield oid
|
|
268
|
-
|
|
269
|
-
if count > limit:
|
|
264
|
+
if i > limit:
|
|
270
265
|
logger.debug(
|
|
271
|
-
"`_list_oids()` returned max %r oids, "
|
|
272
|
-
"skipping remaining results",
|
|
266
|
+
"`_list_oids()` returned max %r oids, skipping remaining results",
|
|
273
267
|
limit,
|
|
274
268
|
)
|
|
275
269
|
return
|
|
@@ -351,7 +345,7 @@ class ObjectDB:
|
|
|
351
345
|
|
|
352
346
|
yield from self._list_oids(prefixes=traverse_prefixes, jobs=jobs)
|
|
353
347
|
|
|
354
|
-
def all(self, jobs=None):
|
|
348
|
+
def all(self, jobs=None):
|
|
355
349
|
"""Iterate over all oids in this fs.
|
|
356
350
|
|
|
357
351
|
Hashes will be fetched in parallel threads according to prefix
|
|
@@ -1,22 +1,8 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
import
|
|
3
|
-
import sys
|
|
2
|
+
from collections.abc import Coroutine, Iterable, Iterator, Sequence
|
|
4
3
|
from concurrent import futures
|
|
5
4
|
from itertools import islice
|
|
6
|
-
from typing import
|
|
7
|
-
Any,
|
|
8
|
-
Callable,
|
|
9
|
-
Coroutine,
|
|
10
|
-
Dict,
|
|
11
|
-
Iterable,
|
|
12
|
-
Iterator,
|
|
13
|
-
List,
|
|
14
|
-
Optional,
|
|
15
|
-
Sequence,
|
|
16
|
-
Set,
|
|
17
|
-
Tuple,
|
|
18
|
-
TypeVar,
|
|
19
|
-
)
|
|
5
|
+
from typing import Any, Callable, Optional, TypeVar
|
|
20
6
|
|
|
21
7
|
from fsspec import Callback
|
|
22
8
|
|
|
@@ -24,18 +10,12 @@ _T = TypeVar("_T")
|
|
|
24
10
|
|
|
25
11
|
|
|
26
12
|
class ThreadPoolExecutor(futures.ThreadPoolExecutor):
|
|
27
|
-
_max_workers: int
|
|
28
|
-
|
|
29
13
|
def __init__(
|
|
30
14
|
self, max_workers: Optional[int] = None, cancel_on_error: bool = False, **kwargs
|
|
31
15
|
):
|
|
32
16
|
super().__init__(max_workers=max_workers, **kwargs)
|
|
33
17
|
self._cancel_on_error = cancel_on_error
|
|
34
18
|
|
|
35
|
-
@property
|
|
36
|
-
def max_workers(self) -> int:
|
|
37
|
-
return self._max_workers
|
|
38
|
-
|
|
39
19
|
def imap_unordered(
|
|
40
20
|
self, fn: Callable[..., _T], *iterables: Iterable[Any]
|
|
41
21
|
) -> Iterator[_T]:
|
|
@@ -43,52 +23,25 @@ class ThreadPoolExecutor(futures.ThreadPoolExecutor):
|
|
|
43
23
|
|
|
44
24
|
It does not create all the futures at once to reduce memory usage.
|
|
45
25
|
"""
|
|
46
|
-
|
|
47
26
|
it = zip(*iterables)
|
|
48
|
-
if self.
|
|
27
|
+
if self._max_workers == 1:
|
|
49
28
|
for args in it:
|
|
50
29
|
yield fn(*args)
|
|
51
30
|
return
|
|
52
31
|
|
|
53
|
-
def create_taskset(n: int) ->
|
|
32
|
+
def create_taskset(n: int) -> set[futures.Future]:
|
|
54
33
|
return {self.submit(fn, *args) for args in islice(it, n)}
|
|
55
34
|
|
|
56
|
-
tasks = create_taskset(self.
|
|
35
|
+
tasks = create_taskset(self._max_workers * 5)
|
|
57
36
|
while tasks:
|
|
58
37
|
done, tasks = futures.wait(tasks, return_when=futures.FIRST_COMPLETED)
|
|
59
38
|
for fut in done:
|
|
60
39
|
yield fut.result()
|
|
61
40
|
tasks.update(create_taskset(len(done)))
|
|
62
41
|
|
|
63
|
-
def shutdown(self, wait=True, *, cancel_futures=False):
|
|
64
|
-
if sys.version_info > (3, 9):
|
|
65
|
-
return super().shutdown(wait=wait, cancel_futures=cancel_futures)
|
|
66
|
-
else: # noqa: RET505
|
|
67
|
-
with self._shutdown_lock:
|
|
68
|
-
self._shutdown = True
|
|
69
|
-
if cancel_futures:
|
|
70
|
-
# Drain all work items from the queue, and then cancel their
|
|
71
|
-
# associated futures.
|
|
72
|
-
while True:
|
|
73
|
-
try:
|
|
74
|
-
work_item = self._work_queue.get_nowait()
|
|
75
|
-
except queue.Empty:
|
|
76
|
-
break
|
|
77
|
-
if work_item is not None:
|
|
78
|
-
work_item.future.cancel()
|
|
79
|
-
|
|
80
|
-
# Send a wake-up to prevent threads calling
|
|
81
|
-
# _work_queue.get(block=True) from permanently blocking.
|
|
82
|
-
self._work_queue.put(None) # type: ignore[arg-type]
|
|
83
|
-
if wait:
|
|
84
|
-
for t in self._threads:
|
|
85
|
-
t.join()
|
|
86
|
-
|
|
87
42
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
else:
|
|
91
|
-
self.shutdown(wait=True)
|
|
43
|
+
cancel_futures = self._cancel_on_error and exc_val is not None
|
|
44
|
+
self.shutdown(wait=True, cancel_futures=cancel_futures)
|
|
92
45
|
return False
|
|
93
46
|
|
|
94
47
|
|
|
@@ -99,7 +52,7 @@ async def batch_coros( # noqa: C901
|
|
|
99
52
|
timeout: Optional[int] = None,
|
|
100
53
|
return_exceptions: bool = False,
|
|
101
54
|
nofiles: bool = False,
|
|
102
|
-
) ->
|
|
55
|
+
) -> list[Any]:
|
|
103
56
|
"""Run the given in coroutines in parallel.
|
|
104
57
|
|
|
105
58
|
The asyncio loop will be kept saturated with up to `batch_size` tasks in
|
|
@@ -116,12 +69,12 @@ async def batch_coros( # noqa: C901
|
|
|
116
69
|
batch_size = len(coros)
|
|
117
70
|
assert batch_size > 0
|
|
118
71
|
|
|
119
|
-
def create_taskset(n: int) ->
|
|
72
|
+
def create_taskset(n: int) -> dict[asyncio.Task, int]:
|
|
120
73
|
return {asyncio.create_task(coro): i for i, coro in islice(it, n)}
|
|
121
74
|
|
|
122
|
-
it: Iterator[
|
|
75
|
+
it: Iterator[tuple[int, Coroutine]] = enumerate(coros)
|
|
123
76
|
tasks = create_taskset(batch_size)
|
|
124
|
-
results:
|
|
77
|
+
results: dict[int, Any] = {}
|
|
125
78
|
while tasks:
|
|
126
79
|
done, pending = await asyncio.wait(
|
|
127
80
|
tasks.keys(), timeout=timeout, return_when=asyncio.FIRST_COMPLETED
|
|
@@ -133,7 +86,7 @@ async def batch_coros( # noqa: C901
|
|
|
133
86
|
for fut in done:
|
|
134
87
|
try:
|
|
135
88
|
result = fut.result()
|
|
136
|
-
except Exception as exc:
|
|
89
|
+
except Exception as exc:
|
|
137
90
|
if not return_exceptions:
|
|
138
91
|
for pending_fut in pending:
|
|
139
92
|
pending_fut.cancel()
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
from collections.abc import Mapping
|
|
2
|
-
from typing import TYPE_CHECKING
|
|
1
|
+
from collections.abc import Iterator, Mapping
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
3
|
from urllib.parse import urlparse
|
|
4
4
|
|
|
5
5
|
from . import generic # noqa: F401
|
|
@@ -83,7 +83,7 @@ class Registry(Mapping):
|
|
|
83
83
|
def __init__(self, reg) -> None:
|
|
84
84
|
self._registry = reg
|
|
85
85
|
|
|
86
|
-
def __getitem__(self, key: str) ->
|
|
86
|
+
def __getitem__(self, key: str) -> type["FileSystem"]:
|
|
87
87
|
entry = self._registry.get(key) or self._registry[Schemes.LOCAL]
|
|
88
88
|
try:
|
|
89
89
|
return _import_class(entry["class"])
|
|
@@ -5,6 +5,7 @@ import ntpath
|
|
|
5
5
|
import os
|
|
6
6
|
import posixpath
|
|
7
7
|
import shutil
|
|
8
|
+
from collections.abc import Iterable, Iterator, Sequence
|
|
8
9
|
from functools import partial
|
|
9
10
|
from multiprocessing import cpu_count
|
|
10
11
|
from typing import (
|
|
@@ -12,14 +13,8 @@ from typing import (
|
|
|
12
13
|
TYPE_CHECKING,
|
|
13
14
|
Any,
|
|
14
15
|
ClassVar,
|
|
15
|
-
Dict,
|
|
16
|
-
Iterable,
|
|
17
|
-
Iterator,
|
|
18
|
-
List,
|
|
19
16
|
Literal,
|
|
20
17
|
Optional,
|
|
21
|
-
Sequence,
|
|
22
|
-
Tuple,
|
|
23
18
|
Union,
|
|
24
19
|
overload,
|
|
25
20
|
)
|
|
@@ -49,7 +44,7 @@ AnyFSPath = str
|
|
|
49
44
|
|
|
50
45
|
# An info() entry, might evolve to a TypedDict
|
|
51
46
|
# in the future (e.g for properly type 'size' etc).
|
|
52
|
-
Entry =
|
|
47
|
+
Entry = dict[str, Any]
|
|
53
48
|
|
|
54
49
|
|
|
55
50
|
class LinkError(OSError):
|
|
@@ -68,7 +63,7 @@ class FileSystem:
|
|
|
68
63
|
|
|
69
64
|
flavour = posixpath
|
|
70
65
|
protocol = "base"
|
|
71
|
-
REQUIRES: ClassVar[
|
|
66
|
+
REQUIRES: ClassVar[dict[str, str]] = {}
|
|
72
67
|
_JOBS = 4 * cpu_count()
|
|
73
68
|
|
|
74
69
|
HASH_JOBS = max(1, min(4, cpu_count() // 2))
|
|
@@ -90,13 +85,13 @@ class FileSystem:
|
|
|
90
85
|
self.fs = fs
|
|
91
86
|
|
|
92
87
|
@cached_property
|
|
93
|
-
def fs_args(self) ->
|
|
88
|
+
def fs_args(self) -> dict[str, Any]:
|
|
94
89
|
ret = {"skip_instance_cache": True}
|
|
95
90
|
ret.update(self._prepare_credentials(**self._config))
|
|
96
91
|
return ret
|
|
97
92
|
|
|
98
93
|
@property
|
|
99
|
-
def config(self) ->
|
|
94
|
+
def config(self) -> dict[str, Any]:
|
|
100
95
|
return self._config
|
|
101
96
|
|
|
102
97
|
@property
|
|
@@ -114,11 +109,11 @@ class FileSystem:
|
|
|
114
109
|
return cls.flavour.join(*parts)
|
|
115
110
|
|
|
116
111
|
@classmethod
|
|
117
|
-
def split(cls, path: str) ->
|
|
112
|
+
def split(cls, path: str) -> tuple[str, str]:
|
|
118
113
|
return cls.flavour.split(path)
|
|
119
114
|
|
|
120
115
|
@classmethod
|
|
121
|
-
def splitext(cls, path: str) ->
|
|
116
|
+
def splitext(cls, path: str) -> tuple[str, str]:
|
|
122
117
|
return cls.flavour.splitext(path)
|
|
123
118
|
|
|
124
119
|
def normpath(self, path: str) -> str:
|
|
@@ -147,7 +142,7 @@ class FileSystem:
|
|
|
147
142
|
return cls.flavour.commonpath(list(paths))
|
|
148
143
|
|
|
149
144
|
@classmethod
|
|
150
|
-
def parts(cls, path: str) ->
|
|
145
|
+
def parts(cls, path: str) -> tuple[str, ...]:
|
|
151
146
|
drive, path = cls.flavour.splitdrive(path.rstrip(cls.flavour.sep))
|
|
152
147
|
|
|
153
148
|
ret = []
|
|
@@ -229,7 +224,7 @@ class FileSystem:
|
|
|
229
224
|
start = "."
|
|
230
225
|
return self.flavour.relpath(self.abspath(path), start=self.abspath(start))
|
|
231
226
|
|
|
232
|
-
def relparts(self, path: str, start: Optional[str] = None) ->
|
|
227
|
+
def relparts(self, path: str, start: Optional[str] = None) -> tuple[str, ...]:
|
|
233
228
|
return self.parts(self.relpath(path, start=start))
|
|
234
229
|
|
|
235
230
|
@classmethod
|
|
@@ -252,7 +247,7 @@ class FileSystem:
|
|
|
252
247
|
return self._config.get("version_aware", False)
|
|
253
248
|
|
|
254
249
|
@staticmethod
|
|
255
|
-
def _get_kwargs_from_urls(urlpath: str) -> "
|
|
250
|
+
def _get_kwargs_from_urls(urlpath: str) -> "dict[str, Any]":
|
|
256
251
|
from fsspec.utils import infer_storage_options
|
|
257
252
|
|
|
258
253
|
options = infer_storage_options(urlpath)
|
|
@@ -262,14 +257,14 @@ class FileSystem:
|
|
|
262
257
|
|
|
263
258
|
def _prepare_credentials(
|
|
264
259
|
self,
|
|
265
|
-
**config:
|
|
266
|
-
) ->
|
|
260
|
+
**config: dict[str, Any],
|
|
261
|
+
) -> dict[str, Any]:
|
|
267
262
|
"""Prepare the arguments for authentication to the
|
|
268
263
|
host filesystem"""
|
|
269
264
|
return {}
|
|
270
265
|
|
|
271
266
|
@classmethod
|
|
272
|
-
def get_missing_deps(cls) ->
|
|
267
|
+
def get_missing_deps(cls) -> list[str]:
|
|
273
268
|
from importlib.util import find_spec
|
|
274
269
|
|
|
275
270
|
return [pkg for pkg, mod in cls.REQUIRES.items() if not find_spec(mod)]
|
|
@@ -301,7 +296,7 @@ class FileSystem:
|
|
|
301
296
|
return entry["size"] == 0
|
|
302
297
|
|
|
303
298
|
@overload
|
|
304
|
-
def open(
|
|
299
|
+
def open(
|
|
305
300
|
self,
|
|
306
301
|
path: AnyFSPath,
|
|
307
302
|
mode: Literal["rb", "br", "wb"],
|
|
@@ -310,15 +305,14 @@ class FileSystem:
|
|
|
310
305
|
return self.open(path, mode, **kwargs)
|
|
311
306
|
|
|
312
307
|
@overload
|
|
313
|
-
def open(
|
|
308
|
+
def open(
|
|
314
309
|
self,
|
|
315
310
|
path: AnyFSPath,
|
|
316
311
|
mode: Literal["r", "rt", "w"] = "r",
|
|
317
312
|
**kwargs: Any,
|
|
318
|
-
) -> "TextIO":
|
|
319
|
-
...
|
|
313
|
+
) -> "TextIO": ...
|
|
320
314
|
|
|
321
|
-
def open(
|
|
315
|
+
def open(
|
|
322
316
|
self,
|
|
323
317
|
path: AnyFSPath,
|
|
324
318
|
mode: str = "r",
|
|
@@ -339,21 +333,21 @@ class FileSystem:
|
|
|
339
333
|
|
|
340
334
|
def cat(
|
|
341
335
|
self,
|
|
342
|
-
path: Union[AnyFSPath,
|
|
336
|
+
path: Union[AnyFSPath, list[AnyFSPath]],
|
|
343
337
|
recursive: bool = False,
|
|
344
338
|
on_error: Literal["raise", "omit", "return"] = "raise",
|
|
345
339
|
**kwargs: Any,
|
|
346
|
-
) -> Union[bytes,
|
|
340
|
+
) -> Union[bytes, dict[AnyFSPath, bytes]]:
|
|
347
341
|
return self.fs.cat(path, recursive=recursive, on_error=on_error, **kwargs)
|
|
348
342
|
|
|
349
343
|
def cat_ranges(
|
|
350
344
|
self,
|
|
351
|
-
paths:
|
|
352
|
-
starts:
|
|
353
|
-
ends:
|
|
345
|
+
paths: list[AnyFSPath],
|
|
346
|
+
starts: list[int],
|
|
347
|
+
ends: list[int],
|
|
354
348
|
max_gap: Optional[int] = None,
|
|
355
349
|
**kwargs,
|
|
356
|
-
) ->
|
|
350
|
+
) -> list[bytes]:
|
|
357
351
|
return self.fs.cat_ranges(paths, starts, ends, max_gap=max_gap, **kwargs)
|
|
358
352
|
|
|
359
353
|
def cat_file(
|
|
@@ -409,7 +403,7 @@ class FileSystem:
|
|
|
409
403
|
|
|
410
404
|
def pipe(
|
|
411
405
|
self,
|
|
412
|
-
path: Union[AnyFSPath,
|
|
406
|
+
path: Union[AnyFSPath, dict[AnyFSPath, bytes]],
|
|
413
407
|
value: Optional[bytes] = None,
|
|
414
408
|
**kwargs: Any,
|
|
415
409
|
) -> None:
|
|
@@ -434,24 +428,22 @@ class FileSystem:
|
|
|
434
428
|
path: AnyFSPath,
|
|
435
429
|
callback: fsspec.Callback = ...,
|
|
436
430
|
batch_size: Optional[int] = ...,
|
|
437
|
-
) -> bool:
|
|
438
|
-
...
|
|
431
|
+
) -> bool: ...
|
|
439
432
|
|
|
440
433
|
@overload
|
|
441
434
|
def exists(
|
|
442
435
|
self,
|
|
443
|
-
path:
|
|
436
|
+
path: list[AnyFSPath],
|
|
444
437
|
callback: fsspec.Callback = ...,
|
|
445
438
|
batch_size: Optional[int] = ...,
|
|
446
|
-
) ->
|
|
447
|
-
...
|
|
439
|
+
) -> list[bool]: ...
|
|
448
440
|
|
|
449
441
|
def exists(
|
|
450
442
|
self,
|
|
451
|
-
path: Union[AnyFSPath,
|
|
443
|
+
path: Union[AnyFSPath, list[AnyFSPath]],
|
|
452
444
|
callback: fsspec.Callback = DEFAULT_CALLBACK,
|
|
453
445
|
batch_size: Optional[int] = None,
|
|
454
|
-
) -> Union[bool,
|
|
446
|
+
) -> Union[bool, list[bool]]:
|
|
455
447
|
if isinstance(path, str):
|
|
456
448
|
return self.fs.exists(path)
|
|
457
449
|
callback.set_size(len(path))
|
|
@@ -513,19 +505,21 @@ class FileSystem:
|
|
|
513
505
|
return not (self.is_symlink(path) or self.is_hardlink(path))
|
|
514
506
|
|
|
515
507
|
@overload
|
|
516
|
-
def ls(
|
|
517
|
-
|
|
508
|
+
def ls(
|
|
509
|
+
self, path: AnyFSPath, detail: Literal[True], **kwargs
|
|
510
|
+
) -> "Iterator[Entry]": ...
|
|
518
511
|
|
|
519
512
|
@overload
|
|
520
|
-
def ls(
|
|
521
|
-
|
|
513
|
+
def ls(
|
|
514
|
+
self, path: AnyFSPath, detail: Literal[False], **kwargs
|
|
515
|
+
) -> Iterator[str]: ...
|
|
522
516
|
|
|
523
517
|
def ls(self, path, detail=False, **kwargs):
|
|
524
518
|
return self.fs.ls(path, detail=detail, **kwargs)
|
|
525
519
|
|
|
526
520
|
def find(
|
|
527
521
|
self,
|
|
528
|
-
path: Union[AnyFSPath,
|
|
522
|
+
path: Union[AnyFSPath, list[AnyFSPath]],
|
|
529
523
|
prefix: bool = False,
|
|
530
524
|
batch_size: Optional[int] = None,
|
|
531
525
|
**kwargs,
|
|
@@ -565,7 +559,7 @@ class FileSystem:
|
|
|
565
559
|
|
|
566
560
|
def rm(
|
|
567
561
|
self,
|
|
568
|
-
path: Union[AnyFSPath,
|
|
562
|
+
path: Union[AnyFSPath, list[AnyFSPath]],
|
|
569
563
|
recursive: bool = False,
|
|
570
564
|
**kwargs,
|
|
571
565
|
) -> None:
|
|
@@ -580,17 +574,15 @@ class FileSystem:
|
|
|
580
574
|
callback: fsspec.Callback = ...,
|
|
581
575
|
batch_size: Optional[int] = ...,
|
|
582
576
|
**kwargs,
|
|
583
|
-
) -> "Entry":
|
|
584
|
-
...
|
|
577
|
+
) -> "Entry": ...
|
|
585
578
|
|
|
586
579
|
@overload
|
|
587
580
|
def info(
|
|
588
581
|
self,
|
|
589
|
-
path:
|
|
582
|
+
path: list[AnyFSPath],
|
|
590
583
|
callback: fsspec.Callback = ...,
|
|
591
584
|
batch_size: Optional[int] = ...,
|
|
592
|
-
) ->
|
|
593
|
-
...
|
|
585
|
+
) -> list["Entry"]: ...
|
|
594
586
|
|
|
595
587
|
def info(self, path, callback=DEFAULT_CALLBACK, batch_size=None, **kwargs):
|
|
596
588
|
if isinstance(path, str):
|
|
@@ -667,7 +659,7 @@ class FileSystem:
|
|
|
667
659
|
def size(self, path: AnyFSPath) -> Optional[int]:
|
|
668
660
|
return self.fs.size(path)
|
|
669
661
|
|
|
670
|
-
def sizes(self, paths:
|
|
662
|
+
def sizes(self, paths: list[AnyFSPath]) -> list[Optional[int]]:
|
|
671
663
|
return self.fs.sizes(paths)
|
|
672
664
|
|
|
673
665
|
def du(
|
|
@@ -676,13 +668,13 @@ class FileSystem:
|
|
|
676
668
|
total: bool = True,
|
|
677
669
|
maxdepth: Optional[int] = None,
|
|
678
670
|
**kwargs: Any,
|
|
679
|
-
) -> Union[int,
|
|
671
|
+
) -> Union[int, dict[AnyFSPath, int]]:
|
|
680
672
|
return self.fs.du(path, total=total, maxdepth=maxdepth, **kwargs)
|
|
681
673
|
|
|
682
674
|
def put(
|
|
683
675
|
self,
|
|
684
|
-
from_info: Union[AnyFSPath,
|
|
685
|
-
to_info: Union[AnyFSPath,
|
|
676
|
+
from_info: Union[AnyFSPath, list[AnyFSPath]],
|
|
677
|
+
to_info: Union[AnyFSPath, list[AnyFSPath]],
|
|
686
678
|
callback: fsspec.Callback = DEFAULT_CALLBACK,
|
|
687
679
|
recursive: bool = False,
|
|
688
680
|
batch_size: Optional[int] = None,
|
|
@@ -714,8 +706,8 @@ class FileSystem:
|
|
|
714
706
|
|
|
715
707
|
def get(
|
|
716
708
|
self,
|
|
717
|
-
from_info: Union[AnyFSPath,
|
|
718
|
-
to_info: Union[AnyFSPath,
|
|
709
|
+
from_info: Union[AnyFSPath, list[AnyFSPath]],
|
|
710
|
+
to_info: Union[AnyFSPath, list[AnyFSPath]],
|
|
719
711
|
callback: fsspec.Callback = DEFAULT_CALLBACK,
|
|
720
712
|
recursive: bool = False,
|
|
721
713
|
batch_size: Optional[int] = None,
|
|
@@ -730,8 +722,8 @@ class FileSystem:
|
|
|
730
722
|
self.fs.get_file(rpath, lpath, callback=child, **kwargs)
|
|
731
723
|
|
|
732
724
|
if isinstance(from_info, list) and isinstance(to_info, list):
|
|
733
|
-
from_infos:
|
|
734
|
-
to_infos:
|
|
725
|
+
from_infos: list[AnyFSPath] = from_info
|
|
726
|
+
to_infos: list[AnyFSPath] = to_info
|
|
735
727
|
else:
|
|
736
728
|
assert isinstance(from_info, str)
|
|
737
729
|
assert isinstance(to_info, str)
|
|
@@ -797,7 +789,7 @@ class ObjectFileSystem(FileSystem):
|
|
|
797
789
|
|
|
798
790
|
def find(
|
|
799
791
|
self,
|
|
800
|
-
path: Union[AnyFSPath,
|
|
792
|
+
path: Union[AnyFSPath, list[AnyFSPath]],
|
|
801
793
|
prefix: bool = False,
|
|
802
794
|
batch_size: Optional[int] = None,
|
|
803
795
|
**kwargs,
|
|
@@ -807,7 +799,7 @@ class ObjectFileSystem(FileSystem):
|
|
|
807
799
|
else:
|
|
808
800
|
paths = path
|
|
809
801
|
|
|
810
|
-
def _make_args(paths:
|
|
802
|
+
def _make_args(paths: list[AnyFSPath]) -> Iterator[tuple[str, str]]:
|
|
811
803
|
for path in paths:
|
|
812
804
|
if prefix and not path.endswith(self.flavour.sep):
|
|
813
805
|
parent = self.parent(path)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import TYPE_CHECKING,
|
|
1
|
+
from typing import TYPE_CHECKING, Optional
|
|
2
2
|
|
|
3
3
|
if TYPE_CHECKING:
|
|
4
4
|
from .base import FileSystem
|
|
@@ -18,7 +18,7 @@ class RemoteMissingDepsError(Exception):
|
|
|
18
18
|
fs: "FileSystem",
|
|
19
19
|
protocol: str,
|
|
20
20
|
url: str,
|
|
21
|
-
missing: Optional[
|
|
21
|
+
missing: Optional[list[str]] = None,
|
|
22
22
|
) -> None:
|
|
23
23
|
self.protocol = protocol
|
|
24
24
|
self.fs = fs
|
|
@@ -4,7 +4,7 @@ import logging
|
|
|
4
4
|
import os
|
|
5
5
|
from contextlib import suppress
|
|
6
6
|
from functools import wraps
|
|
7
|
-
from typing import TYPE_CHECKING, Any, Callable,
|
|
7
|
+
from typing import TYPE_CHECKING, Any, Callable, Optional, Union
|
|
8
8
|
|
|
9
9
|
from fsspec.asyn import get_loop
|
|
10
10
|
from fsspec.callbacks import DEFAULT_CALLBACK
|
|
@@ -68,9 +68,9 @@ def _link(
|
|
|
68
68
|
|
|
69
69
|
def copy(
|
|
70
70
|
from_fs: "FileSystem",
|
|
71
|
-
from_path: Union["AnyFSPath",
|
|
71
|
+
from_path: Union["AnyFSPath", list["AnyFSPath"]],
|
|
72
72
|
to_fs: "FileSystem",
|
|
73
|
-
to_path: Union["AnyFSPath",
|
|
73
|
+
to_path: Union["AnyFSPath", list["AnyFSPath"]],
|
|
74
74
|
callback: "Callback" = DEFAULT_CALLBACK,
|
|
75
75
|
batch_size: Optional[int] = None,
|
|
76
76
|
on_error: Optional[TransferErrorHandler] = None,
|
|
@@ -116,7 +116,7 @@ def copy(
|
|
|
116
116
|
return to_fs.put_file(
|
|
117
117
|
fobj, to_p, size=size, callback=child, **put_file_kwargs
|
|
118
118
|
)
|
|
119
|
-
except Exception as exc:
|
|
119
|
+
except Exception as exc:
|
|
120
120
|
if on_error is not None:
|
|
121
121
|
on_error(from_p, to_p, exc)
|
|
122
122
|
else:
|
|
@@ -133,9 +133,9 @@ def copy(
|
|
|
133
133
|
|
|
134
134
|
|
|
135
135
|
def _put( # noqa: C901
|
|
136
|
-
from_paths:
|
|
136
|
+
from_paths: list["AnyFSPath"],
|
|
137
137
|
to_fs: "FileSystem",
|
|
138
|
-
to_paths:
|
|
138
|
+
to_paths: list["AnyFSPath"],
|
|
139
139
|
callback: "Callback" = DEFAULT_CALLBACK,
|
|
140
140
|
batch_size: Optional[int] = None,
|
|
141
141
|
on_error: Optional[TransferErrorHandler] = None,
|
|
@@ -151,7 +151,7 @@ def _put( # noqa: C901
|
|
|
151
151
|
return to_fs.put_file(
|
|
152
152
|
from_path, to_path, callback=child, **put_file_kwargs
|
|
153
153
|
)
|
|
154
|
-
except Exception as exc:
|
|
154
|
+
except Exception as exc:
|
|
155
155
|
if on_error is not None:
|
|
156
156
|
on_error(from_path, to_path, exc)
|
|
157
157
|
else:
|
|
@@ -199,8 +199,8 @@ def _put( # noqa: C901
|
|
|
199
199
|
|
|
200
200
|
def _get( # noqa: C901
|
|
201
201
|
from_fs: "FileSystem",
|
|
202
|
-
from_paths:
|
|
203
|
-
to_paths:
|
|
202
|
+
from_paths: list["AnyFSPath"],
|
|
203
|
+
to_paths: list["AnyFSPath"],
|
|
204
204
|
callback: "Callback" = DEFAULT_CALLBACK,
|
|
205
205
|
batch_size: Optional[int] = None,
|
|
206
206
|
on_error: Optional[TransferErrorHandler] = None,
|
|
@@ -217,7 +217,7 @@ def _get( # noqa: C901
|
|
|
217
217
|
return from_fs.get_file(
|
|
218
218
|
from_path, tmp_file, callback=child, **get_file_kwargs
|
|
219
219
|
)
|
|
220
|
-
except Exception as exc:
|
|
220
|
+
except Exception as exc:
|
|
221
221
|
if on_error is not None:
|
|
222
222
|
on_error(from_path, to_path, exc)
|
|
223
223
|
else:
|
|
@@ -265,7 +265,7 @@ def _get( # noqa: C901
|
|
|
265
265
|
|
|
266
266
|
|
|
267
267
|
def _try_links(
|
|
268
|
-
links:
|
|
268
|
+
links: list["str"],
|
|
269
269
|
from_fs: "FileSystem",
|
|
270
270
|
from_path: "AnyFSPath",
|
|
271
271
|
to_fs: "FileSystem",
|
|
@@ -299,6 +299,7 @@ def _try_links(
|
|
|
299
299
|
errno.ENOTTY,
|
|
300
300
|
errno.ENOSYS,
|
|
301
301
|
errno.EINVAL,
|
|
302
|
+
errno.EAGAIN,
|
|
302
303
|
):
|
|
303
304
|
raise
|
|
304
305
|
error = exc
|
|
@@ -310,11 +311,11 @@ def _try_links(
|
|
|
310
311
|
|
|
311
312
|
def transfer( # noqa: PLR0912, C901
|
|
312
313
|
from_fs: "FileSystem",
|
|
313
|
-
from_path: Union["AnyFSPath",
|
|
314
|
+
from_path: Union["AnyFSPath", list["AnyFSPath"]],
|
|
314
315
|
to_fs: "FileSystem",
|
|
315
|
-
to_path: Union["AnyFSPath",
|
|
316
|
+
to_path: Union["AnyFSPath", list["AnyFSPath"]],
|
|
316
317
|
hardlink: bool = False,
|
|
317
|
-
links: Optional[
|
|
318
|
+
links: Optional[list["str"]] = None,
|
|
318
319
|
callback: "Callback" = DEFAULT_CALLBACK,
|
|
319
320
|
batch_size: Optional[int] = None,
|
|
320
321
|
on_error: Optional[TransferErrorHandler] = None,
|
|
@@ -375,7 +376,7 @@ def transfer( # noqa: PLR0912, C901
|
|
|
375
376
|
on_error(from_p, to_p, exc)
|
|
376
377
|
else:
|
|
377
378
|
raise
|
|
378
|
-
except Exception as exc:
|
|
379
|
+
except Exception as exc:
|
|
379
380
|
if on_error is not None:
|
|
380
381
|
on_error(from_p, to_p, exc)
|
|
381
382
|
else:
|
|
@@ -405,12 +406,12 @@ def _test_link(
|
|
|
405
406
|
|
|
406
407
|
|
|
407
408
|
def test_links(
|
|
408
|
-
links:
|
|
409
|
+
links: list["str"],
|
|
409
410
|
from_fs: "FileSystem",
|
|
410
411
|
from_path: "AnyFSPath",
|
|
411
412
|
to_fs: "FileSystem",
|
|
412
413
|
to_path: "AnyFSPath",
|
|
413
|
-
) ->
|
|
414
|
+
) -> list["AnyFSPath"]:
|
|
414
415
|
from .utils import tmp_fname
|
|
415
416
|
|
|
416
417
|
from_file = from_fs.join(from_path, tmp_fname())
|
|
@@ -127,8 +127,8 @@ class FsspecLocalFileSystem(fsspec.AbstractFileSystem):
|
|
|
127
127
|
self.rm_file(tmp_info)
|
|
128
128
|
raise
|
|
129
129
|
|
|
130
|
-
def open(self, path, mode="r", encoding=None, **kwargs):
|
|
131
|
-
return open(path, mode=mode, encoding=encoding)
|
|
130
|
+
def open(self, path, mode="r", encoding=None, **kwargs):
|
|
131
|
+
return open(path, mode=mode, encoding=encoding)
|
|
132
132
|
|
|
133
133
|
def symlink(self, path1, path2):
|
|
134
134
|
return self.fs.symlink(path1, path2)
|
|
@@ -5,10 +5,11 @@ import shutil
|
|
|
5
5
|
import stat
|
|
6
6
|
import sys
|
|
7
7
|
import threading
|
|
8
|
+
from collections.abc import Collection, Iterator
|
|
8
9
|
from concurrent import futures
|
|
9
10
|
from contextlib import contextmanager, suppress
|
|
10
11
|
from secrets import token_urlsafe
|
|
11
|
-
from typing import TYPE_CHECKING, Any,
|
|
12
|
+
from typing import TYPE_CHECKING, Any, Optional, Union
|
|
12
13
|
|
|
13
14
|
from fsspec.callbacks import DEFAULT_CALLBACK
|
|
14
15
|
|
|
@@ -207,7 +208,7 @@ def exists(
|
|
|
207
208
|
file_paths: Union["AnyFSPath", Collection["AnyFSPath"]],
|
|
208
209
|
callback: "Callback" = DEFAULT_CALLBACK,
|
|
209
210
|
batch_size: Optional[int] = None,
|
|
210
|
-
) ->
|
|
211
|
+
) -> dict[str, bool]:
|
|
211
212
|
"""Return batched fs.exists results.
|
|
212
213
|
|
|
213
214
|
Runs batched fs.exists() calls in parallel with fs.ls() until all paths
|
|
@@ -221,7 +222,7 @@ def exists(
|
|
|
221
222
|
return {path: fs.exists(path)}
|
|
222
223
|
|
|
223
224
|
paths_lock = threading.Lock()
|
|
224
|
-
results:
|
|
225
|
+
results: dict[str, bool] = {}
|
|
225
226
|
results_lock = threading.Lock()
|
|
226
227
|
callback.set_size(len(paths))
|
|
227
228
|
jobs = batch_size or fs.jobs
|
|
@@ -260,9 +261,9 @@ def exists(
|
|
|
260
261
|
|
|
261
262
|
def _exist_query(
|
|
262
263
|
fs: "FileSystem",
|
|
263
|
-
paths:
|
|
264
|
+
paths: set["AnyFSPath"],
|
|
264
265
|
paths_lock: threading.Lock,
|
|
265
|
-
results:
|
|
266
|
+
results: dict[str, bool],
|
|
266
267
|
results_lock: threading.Lock,
|
|
267
268
|
batch_size: int,
|
|
268
269
|
callback: "Callback",
|
|
@@ -282,9 +283,9 @@ def _exist_query(
|
|
|
282
283
|
|
|
283
284
|
def _list_query(
|
|
284
285
|
fs: "FileSystem",
|
|
285
|
-
paths:
|
|
286
|
+
paths: set["AnyFSPath"],
|
|
286
287
|
paths_lock: threading.Lock,
|
|
287
|
-
results:
|
|
288
|
+
results: dict[str, bool],
|
|
288
289
|
results_lock: threading.Lock,
|
|
289
290
|
callback: "Callback",
|
|
290
291
|
):
|
|
@@ -294,7 +295,7 @@ def _list_query(
|
|
|
294
295
|
with paths_lock:
|
|
295
296
|
if not paths:
|
|
296
297
|
return
|
|
297
|
-
kwargs:
|
|
298
|
+
kwargs: dict[str, Any] = {}
|
|
298
299
|
if fs.version_aware:
|
|
299
300
|
kwargs["versions"] = True
|
|
300
301
|
contents = fs.ls(parent, **kwargs)
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
from typing import TYPE_CHECKING, Optional
|
|
1
|
+
from typing import TYPE_CHECKING, Optional
|
|
2
2
|
|
|
3
3
|
if TYPE_CHECKING:
|
|
4
4
|
from .db import ObjectDB
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
def transfer(
|
|
8
|
-
src: "ObjectDB", dest: "ObjectDB", oids:
|
|
9
|
-
) ->
|
|
8
|
+
src: "ObjectDB", dest: "ObjectDB", oids: set["str"], jobs: Optional[int] = None
|
|
9
|
+
) -> set["str"]:
|
|
10
10
|
src_exists = set(src.oids_exist(oids, jobs=jobs))
|
|
11
11
|
src_missing = oids - src_exists
|
|
12
12
|
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: dvc-objects
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.1.1
|
|
4
4
|
Summary: dvc objects - filesystem and object-db level abstractions to use in dvc and dvc-data
|
|
5
5
|
Author-email: Iterative <support@dvc.org>
|
|
6
6
|
License: Apache-2.0
|
|
7
7
|
Project-URL: Issues, https://github.com/iterative/dvc-objects/issues
|
|
8
8
|
Project-URL: Source, https://github.com/iterative/dvc-objects
|
|
9
9
|
Classifier: Programming Language :: Python :: 3
|
|
10
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
11
10
|
Classifier: Programming Language :: Python :: 3.9
|
|
12
11
|
Classifier: Programming Language :: Python :: 3.10
|
|
13
12
|
Classifier: Programming Language :: Python :: 3.11
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
15
|
Classifier: Development Status :: 4 - Beta
|
|
16
|
-
Requires-Python: >=3.
|
|
16
|
+
Requires-Python: >=3.9
|
|
17
17
|
Description-Content-Type: text/x-rst
|
|
18
18
|
License-File: LICENSE
|
|
19
|
-
Requires-Dist: funcy>=1.14; python_version < "3.12"
|
|
20
19
|
Requires-Dist: fsspec>=2024.2.0
|
|
20
|
+
Requires-Dist: funcy>=1.14; python_version < "3.12"
|
|
21
21
|
Provides-Extra: tests
|
|
22
|
-
Requires-Dist: pytest<
|
|
22
|
+
Requires-Dist: pytest<9,>=7; extra == "tests"
|
|
23
23
|
Requires-Dist: pytest-sugar; extra == "tests"
|
|
24
24
|
Requires-Dist: pytest-cov>=4.1.0; extra == "tests"
|
|
25
25
|
Requires-Dist: pytest-mock; extra == "tests"
|
|
@@ -28,7 +28,8 @@ Requires-Dist: pytest-asyncio<1,>=0.23.2; extra == "tests"
|
|
|
28
28
|
Requires-Dist: reflink; extra == "tests"
|
|
29
29
|
Provides-Extra: dev
|
|
30
30
|
Requires-Dist: dvc-objects[tests]; extra == "dev"
|
|
31
|
-
Requires-Dist: mypy==1.
|
|
31
|
+
Requires-Dist: mypy==1.16.0; extra == "dev"
|
|
32
|
+
Dynamic: license-file
|
|
32
33
|
|
|
33
34
|
DVC objects
|
|
34
35
|
===========
|
|
@@ -44,9 +44,9 @@ def paths(original, links):
|
|
|
44
44
|
|
|
45
45
|
def test_link(benchmark, paths, links, link):
|
|
46
46
|
def setup():
|
|
47
|
-
for
|
|
48
|
-
if
|
|
49
|
-
|
|
47
|
+
for lnk in links.iterdir():
|
|
48
|
+
if lnk.is_file():
|
|
49
|
+
lnk.unlink()
|
|
50
50
|
|
|
51
51
|
def _link(paths):
|
|
52
52
|
for src, path in paths:
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from os import fspath
|
|
3
|
-
from typing import Dict, List, Set, Tuple
|
|
4
3
|
|
|
5
4
|
import pytest
|
|
6
5
|
|
|
@@ -98,11 +97,11 @@ def test_walk(dir_path):
|
|
|
98
97
|
|
|
99
98
|
def test_walk_detail(dir_path):
|
|
100
99
|
fs = LocalFileSystem()
|
|
101
|
-
walk_results:
|
|
100
|
+
walk_results: list[tuple[str, dict[str, dict], dict[str, dict]]] = list(
|
|
102
101
|
fs.walk(fspath(dir_path), detail=True)
|
|
103
102
|
)
|
|
104
103
|
|
|
105
|
-
expected:
|
|
104
|
+
expected: list[tuple[str, set[str], set[str]]] = [
|
|
106
105
|
(str(dir_path), {"data"}, {"code.py", "bar", "тест", "foo"}),
|
|
107
106
|
(str(dir_path / "data"), {"sub"}, {"file"}),
|
|
108
107
|
(str(dir_path / "data" / "sub"), set(), {"file"}),
|
|
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
|