objpath 0.1.0__tar.gz → 0.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.
- {objpath-0.1.0 → objpath-0.1.1}/CHANGELOG.md +12 -1
- {objpath-0.1.0 → objpath-0.1.1}/PKG-INFO +1 -1
- {objpath-0.1.0 → objpath-0.1.1}/pyproject.toml +1 -1
- {objpath-0.1.0 → objpath-0.1.1}/src/objpath/AGENTS.md +3 -0
- {objpath-0.1.0 → objpath-0.1.1}/src/objpath/_storepath.py +11 -0
- {objpath-0.1.0 → objpath-0.1.1}/src/objpath/_uri_store.py +10 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/mutation/mutants.py +27 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/test_az_fake.py +67 -0
- {objpath-0.1.0 → objpath-0.1.1}/.gitignore +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/LICENSE +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/README.md +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/docs/api/exceptions.md +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/docs/api/paths.md +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/docs/api/uri.md +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/docs/benchmarks.md +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/docs/changelog.md +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/docs/divergences.md +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/docs/guides/paths.md +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/docs/guides/uri.md +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/docs/index.md +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/examples/az_listing.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/examples/gs_listing.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/examples/s3_listing.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/mkdocs.yml +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/src/objpath/__init__.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/src/objpath/_az.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/src/objpath/_az_backend.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/src/objpath/_az_errors.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/src/objpath/_clients.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/src/objpath/_digests.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/src/objpath/_failures.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/src/objpath/_gs.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/src/objpath/_gs_backend.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/src/objpath/_gs_errors.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/src/objpath/_keys.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/src/objpath/_optional.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/src/objpath/_places.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/src/objpath/_reader.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/src/objpath/_s3.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/src/objpath/_s3_backend.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/src/objpath/_s3_errors.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/src/objpath/_version.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/src/objpath/_writer.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/src/objpath/exceptions.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/src/objpath/py.typed +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/src/objpath/uri.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/az_fakes.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/capabilities.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/child.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/conftest.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/gs_fakes.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/hermetic.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/mutation/README.md +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/mutation/mutation_check.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/mutation/run.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/objstore_fakes.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/probes/probe_environment_leak.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/probes/probe_leaks_a_socket.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/probes/probe_session_environment.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/server_loops.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/store_support.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/test_az.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/test_az_path.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/test_backends.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/test_documentation.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/test_examples_skip.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/test_gs.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/test_gs_fake.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/test_gs_path.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/test_hermetic.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/test_isolation.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/test_packaging.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/test_public_surface.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/test_s3.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/test_s3_errors.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/test_s3_parity.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/test_s3_path.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/test_shared_helpers.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/test_shipped_header.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/test_spool_unclosed.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/test_uri_az.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/test_uri_gs.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/test_uri_s3.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/test_version.py +0 -0
- {objpath-0.1.0 → objpath-0.1.1}/tests/waits.py +0 -0
|
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.1.1] - 2026-10-17
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`rm()` and `copy()` take `max_workers`**, the keyword of `pathapi`'s recursive
|
|
15
|
+
`rm()` and `copy()` (a class runs serially unless it declares `_threadsafe`,
|
|
16
|
+
which these classes do not). A streamed copy passes it on; the batch delete and the
|
|
17
|
+
store's own copy call ignore it. Nothing changes for a call that does not give it,
|
|
18
|
+
and it works with the `pathapi` release that lacks it as long as it is not given.
|
|
19
|
+
|
|
10
20
|
## [0.1.0] - 2026-10-14
|
|
11
21
|
|
|
12
22
|
The first release of `objpath`: the Amazon S3, Google Cloud Storage and Azure Blob
|
|
@@ -109,5 +119,6 @@ name `pathlib_next`); the old names below are the ones before the move.
|
|
|
109
119
|
and emits a `ResourceWarning`; `pathlib-next` closed it, and so uploaded it. What to do:
|
|
110
120
|
close the file, or use `with`.
|
|
111
121
|
|
|
112
|
-
[Unreleased]: https://github.com/jose-pr/objpath/compare/v0.1.
|
|
122
|
+
[Unreleased]: https://github.com/jose-pr/objpath/compare/v0.1.1...HEAD
|
|
123
|
+
[0.1.1]: https://github.com/jose-pr/objpath/compare/v0.1.0...v0.1.1
|
|
113
124
|
[0.1.0]: https://github.com/jose-pr/objpath/releases/tag/v0.1.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: objpath
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: pathlib-style paths for S3, Google Cloud Storage and Azure Blob Storage, and the s3:, gs: and az: schemes for uripath
|
|
5
5
|
Project-URL: Homepage, https://github.com/jose-pr/objpath/
|
|
6
6
|
Project-URL: Documentation, https://jose-pr.github.io/objpath/
|
|
@@ -5,7 +5,7 @@ build-backend = "hatchling.build"
|
|
|
5
5
|
|
|
6
6
|
[project]
|
|
7
7
|
name = "objpath"
|
|
8
|
-
version = "0.1.
|
|
8
|
+
version = "0.1.1"
|
|
9
9
|
authors = [{ name = "Jose A" }]
|
|
10
10
|
description = "pathlib-style paths for S3, Google Cloud Storage and Azure Blob Storage, and the s3:, gs: and az: schemes for uripath"
|
|
11
11
|
readme = "README.md"
|
|
@@ -160,6 +160,9 @@ Reading and writing:
|
|
|
160
160
|
with that key's path, one error per key. `rm(recursive=True)` of a missing bucket is
|
|
161
161
|
a missing path (`missing_ok`, `ignore_error` decide). There are no symlinks or
|
|
162
162
|
bindings: `follow_symlinks=` and `follow_binds=` are checked and decide nothing.
|
|
163
|
+
`rm()` and `copy()` take `max_workers=None` (`pathapi`'s keyword, passed on to the
|
|
164
|
+
generic walks only when given; the batch delete and the store's copy call ignore it,
|
|
165
|
+
and a path never declares `_threadsafe`).
|
|
163
166
|
- `checksum(algorithm="md5")` (`NativeChecksum`) is the digest the store keeps, read with
|
|
164
167
|
one request and no byte of the content. S3: the plain MD5 that the ETag is for an object
|
|
165
168
|
one request wrote and KMS or a customer key does not encrypt, or the stored full-object
|
|
@@ -371,7 +371,10 @@ class ObjectStorePath(TreePath):
|
|
|
371
371
|
*,
|
|
372
372
|
follow_symlinks=False,
|
|
373
373
|
follow_binds=False,
|
|
374
|
+
max_workers=None,
|
|
374
375
|
):
|
|
376
|
+
"""Remove as `Path.rm()` does. `max_workers` is `Path.rm()`'s and is
|
|
377
|
+
ignored: a recursive removal is the store's batch delete."""
|
|
375
378
|
# An object store holds no symlinks or bindings: the policies are
|
|
376
379
|
# checked and have nothing to decide.
|
|
377
380
|
check_follow("follow_symlinks", follow_symlinks)
|
|
@@ -383,6 +386,8 @@ class ObjectStorePath(TreePath):
|
|
|
383
386
|
ignore_error=ignore_error,
|
|
384
387
|
follow_symlinks=follow_symlinks,
|
|
385
388
|
follow_binds=follow_binds,
|
|
389
|
+
# Only when given: pathapi 0.1.1 has no such keyword.
|
|
390
|
+
**({} if max_workers is None else {"max_workers": max_workers}),
|
|
386
391
|
)
|
|
387
392
|
|
|
388
393
|
def on_error(error, path=None):
|
|
@@ -461,7 +466,11 @@ class ObjectStorePath(TreePath):
|
|
|
461
466
|
ignore_error=None,
|
|
462
467
|
progress=None,
|
|
463
468
|
on_error=None,
|
|
469
|
+
max_workers=None,
|
|
464
470
|
):
|
|
471
|
+
"""Copy as `Path.copy()` does, with the store's own copy call for one
|
|
472
|
+
object of the same store. `max_workers` is `Path.copy()`'s and applies to
|
|
473
|
+
the streamed copy only; the store's copy call ignores it."""
|
|
465
474
|
# A prefix is copied by walking it; one that holds keys the walk
|
|
466
475
|
# cannot reach is refused up front rather than copied incompletely.
|
|
467
476
|
# That refusal is not a per-child error: `on_error` is never asked.
|
|
@@ -486,6 +495,8 @@ class ObjectStorePath(TreePath):
|
|
|
486
495
|
ignore_error=ignore_error,
|
|
487
496
|
progress=progress,
|
|
488
497
|
**({} if on_error is None else {"on_error": on_error}),
|
|
498
|
+
# Only when given: pathapi 0.1.1 has no such keyword.
|
|
499
|
+
**({} if max_workers is None else {"max_workers": max_workers}),
|
|
489
500
|
)
|
|
490
501
|
|
|
491
502
|
def _store_copy_possible(self, target) -> bool:
|
|
@@ -92,7 +92,10 @@ class ObjectStoreURIPath(DelegatingURIPath):
|
|
|
92
92
|
*,
|
|
93
93
|
follow_symlinks=False,
|
|
94
94
|
follow_binds=False,
|
|
95
|
+
max_workers=None,
|
|
95
96
|
):
|
|
97
|
+
"""Remove as `Path.rm()` does. `max_workers` is `Path.rm()`'s and is
|
|
98
|
+
ignored: a recursive removal is the store's batch delete."""
|
|
96
99
|
check_follow("follow_symlinks", follow_symlinks)
|
|
97
100
|
check_follow("follow_binds", follow_binds)
|
|
98
101
|
on_error = ignore_error
|
|
@@ -110,6 +113,7 @@ class ObjectStoreURIPath(DelegatingURIPath):
|
|
|
110
113
|
ignore_error=on_error,
|
|
111
114
|
follow_symlinks=follow_symlinks,
|
|
112
115
|
follow_binds=follow_binds,
|
|
116
|
+
max_workers=max_workers,
|
|
113
117
|
)
|
|
114
118
|
|
|
115
119
|
def copy(
|
|
@@ -123,7 +127,11 @@ class ObjectStoreURIPath(DelegatingURIPath):
|
|
|
123
127
|
ignore_error=None,
|
|
124
128
|
progress=None,
|
|
125
129
|
on_error=None,
|
|
130
|
+
max_workers=None,
|
|
126
131
|
):
|
|
132
|
+
"""Copy as `Path.copy()` does, with the store's own copy call for one
|
|
133
|
+
object of the same store. `max_workers` is `Path.copy()`'s and applies to
|
|
134
|
+
the streamed copy only; the store's copy call ignores it."""
|
|
127
135
|
# The core path's walk of a prefix is not used: children are URI paths
|
|
128
136
|
# and are copied through this method, so the refusal of a prefix that
|
|
129
137
|
# holds keys a walk cannot reach is made here, once.
|
|
@@ -159,4 +167,6 @@ class ObjectStoreURIPath(DelegatingURIPath):
|
|
|
159
167
|
ignore_error=ignore_error,
|
|
160
168
|
progress=progress,
|
|
161
169
|
**({} if offering is None else {"on_error": offering}),
|
|
170
|
+
# Only when given: pathapi 0.1.1 has no such keyword.
|
|
171
|
+
**({} if max_workers is None else {"max_workers": max_workers}),
|
|
162
172
|
)
|
|
@@ -434,3 +434,30 @@ mutant(
|
|
|
434
434
|
"tests/test_spool_unclosed.py::test_an_unclosed_stream_that_was_not_modified_is_collected_silently",
|
|
435
435
|
"a spooled stream with nothing to upload warns when collected",
|
|
436
436
|
)
|
|
437
|
+
|
|
438
|
+
mutant(
|
|
439
|
+
"objpath-store-rm-always-passes-max-workers",
|
|
440
|
+
"_storepath.py",
|
|
441
|
+
' # Only when given: pathapi 0.1.1 has no such keyword.\n **({} if max_workers is None else {"max_workers": max_workers}),\n )\n\n def on_error(',
|
|
442
|
+
" max_workers=max_workers,\n )\n\n def on_error(",
|
|
443
|
+
"tests/test_az_fake.py::test_the_generic_calls_get_max_workers_only_when_it_is_given",
|
|
444
|
+
"rm() hands pathapi a max_workers it was not given, which a release without the keyword refuses",
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
mutant(
|
|
448
|
+
"objpath-store-copy-always-passes-max-workers",
|
|
449
|
+
"_storepath.py",
|
|
450
|
+
' # Only when given: pathapi 0.1.1 has no such keyword.\n **({} if max_workers is None else {"max_workers": max_workers}),\n )\n\n def _store_copy_possible',
|
|
451
|
+
" max_workers=max_workers,\n )\n\n def _store_copy_possible",
|
|
452
|
+
"tests/test_az_fake.py::test_the_generic_calls_get_max_workers_only_when_it_is_given",
|
|
453
|
+
"copy() hands pathapi a max_workers it was not given, which a release without the keyword refuses",
|
|
454
|
+
)
|
|
455
|
+
|
|
456
|
+
mutant(
|
|
457
|
+
"objpath-uri-copy-always-passes-max-workers",
|
|
458
|
+
"_uri_store.py",
|
|
459
|
+
' # Only when given: pathapi 0.1.1 has no such keyword.\n **({} if max_workers is None else {"max_workers": max_workers}),\n',
|
|
460
|
+
" max_workers=max_workers,\n",
|
|
461
|
+
"tests/test_az_fake.py::test_the_generic_calls_get_max_workers_only_when_it_is_given",
|
|
462
|
+
"the URI copy() hands pathapi a max_workers it was not given",
|
|
463
|
+
)
|
|
@@ -1422,3 +1422,70 @@ def test_r_plus_reads_its_initial_content_as_one_version(az, monkeypatch):
|
|
|
1422
1422
|
az("az://account/container/big.bin", backend).open("r+b")
|
|
1423
1423
|
assert info.value.errno == _stale_errno()
|
|
1424
1424
|
assert container.objects["big.bin"] == NEW and container.uploads == []
|
|
1425
|
+
|
|
1426
|
+
|
|
1427
|
+
# --- max_workers ---------------------------------------------------------------------
|
|
1428
|
+
|
|
1429
|
+
|
|
1430
|
+
def _generic_owner(path, name):
|
|
1431
|
+
"""The class whose `name` the override of `path`'s class reaches with `super()`."""
|
|
1432
|
+
# A URI path hands `rm()` to its core path, whose `super()` is the generic one.
|
|
1433
|
+
if name == "rm":
|
|
1434
|
+
path = getattr(path, "_core_path", path)
|
|
1435
|
+
owners = [base for base in type(path).__mro__ if name in vars(base)]
|
|
1436
|
+
return owners[1]
|
|
1437
|
+
|
|
1438
|
+
|
|
1439
|
+
@pytest.mark.parametrize("name", ["rm", "copy"])
|
|
1440
|
+
def test_rm_and_copy_have_the_parameters_of_the_path_ones_and_max_workers(az, name):
|
|
1441
|
+
import inspect
|
|
1442
|
+
|
|
1443
|
+
from pathapi import Path
|
|
1444
|
+
|
|
1445
|
+
expected = inspect.signature(getattr(Path, name)).parameters
|
|
1446
|
+
actual = inspect.signature(getattr(type(az("az://account/c/k")), name)).parameters
|
|
1447
|
+
assert [n for n in actual if n != "max_workers"] == [
|
|
1448
|
+
n for n in expected if n != "max_workers"
|
|
1449
|
+
]
|
|
1450
|
+
assert actual["max_workers"].kind is inspect.Parameter.KEYWORD_ONLY
|
|
1451
|
+
assert actual["max_workers"].default is None
|
|
1452
|
+
for pname, parameter in expected.items():
|
|
1453
|
+
assert actual[pname].kind == parameter.kind, pname
|
|
1454
|
+
assert actual[pname].default == parameter.default, pname
|
|
1455
|
+
|
|
1456
|
+
|
|
1457
|
+
def test_the_batch_delete_and_the_service_copy_ignore_max_workers(az):
|
|
1458
|
+
backend, container = _container_with(**{"dir/a.txt": b"a", "dir/b.txt": b"b"})
|
|
1459
|
+
|
|
1460
|
+
az("az://account/container/dir/a.txt", backend).copy(
|
|
1461
|
+
az("az://account/container/c.txt", backend), max_workers=4
|
|
1462
|
+
)
|
|
1463
|
+
az("az://account/container/dir", backend).rm(recursive=True, max_workers=4)
|
|
1464
|
+
|
|
1465
|
+
assert container.copies == [("dir/a.txt", "c.txt")]
|
|
1466
|
+
assert sorted(container.objects) == ["c.txt"]
|
|
1467
|
+
|
|
1468
|
+
|
|
1469
|
+
@pytest.mark.parametrize("name", ["rm", "copy"])
|
|
1470
|
+
@pytest.mark.parametrize("workers", [None, 4])
|
|
1471
|
+
def test_the_generic_calls_get_max_workers_only_when_it_is_given(
|
|
1472
|
+
az, monkeypatch, name, workers
|
|
1473
|
+
):
|
|
1474
|
+
seen = []
|
|
1475
|
+
source = az("az://account/container/a.txt")
|
|
1476
|
+
monkeypatch.setattr(
|
|
1477
|
+
_generic_owner(source, name),
|
|
1478
|
+
name,
|
|
1479
|
+
lambda self, *args, **kw: seen.append(kw),
|
|
1480
|
+
)
|
|
1481
|
+
extra = {} if workers is None else {"max_workers": workers}
|
|
1482
|
+
|
|
1483
|
+
if name == "rm":
|
|
1484
|
+
source.rm(**extra)
|
|
1485
|
+
else:
|
|
1486
|
+
# Another backend: the streamed copy, which is the generic one.
|
|
1487
|
+
source.copy(az("az://account/container/b.txt"), **extra)
|
|
1488
|
+
|
|
1489
|
+
assert [kw.get("max_workers", "absent") for kw in seen] == [
|
|
1490
|
+
"absent" if workers is None else workers
|
|
1491
|
+
]
|
|
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
|
|
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
|