pathlib-next 0.8.2__tar.gz → 0.8.3__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.
Files changed (110) hide show
  1. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/CHANGELOG.md +14 -1
  2. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/PKG-INFO +1 -1
  3. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/docs/benchmarks.md +10 -0
  4. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/pyproject.toml +1 -1
  5. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/uri/schemes/sftp/_asyncssh.py +15 -1
  6. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_sftp_asyncssh.py +16 -2
  7. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/.gitignore +0 -0
  8. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/LICENSE +0 -0
  9. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/README.md +0 -0
  10. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/docs/api/mempath.md +0 -0
  11. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/docs/api/path.md +0 -0
  12. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/docs/api/testing.md +0 -0
  13. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/docs/api/uri.md +0 -0
  14. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/docs/api/utils.md +0 -0
  15. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/docs/changelog.md +0 -0
  16. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/docs/divergences.md +0 -0
  17. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/docs/guides/cli.md +0 -0
  18. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/docs/guides/extending.md +0 -0
  19. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/docs/guides/schemes.md +0 -0
  20. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/docs/index.md +0 -0
  21. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/examples/az_listing.py +0 -0
  22. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/examples/data_and_archive.py +0 -0
  23. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/examples/ftp_listing.py +0 -0
  24. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/examples/github_listing.py +0 -0
  25. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/examples/gitlab_listing.py +0 -0
  26. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/examples/gs_listing.py +0 -0
  27. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/examples/http_listing.py +0 -0
  28. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/examples/local_and_mem.py +0 -0
  29. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/examples/s3_listing.py +0 -0
  30. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/examples/sftp_sync.py +0 -0
  31. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/examples/webdav_roundtrip.py +0 -0
  32. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/mkdocs.yml +0 -0
  33. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/__init__.py +0 -0
  34. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/fspath.py +0 -0
  35. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/mempath.py +0 -0
  36. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/path.py +0 -0
  37. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/protocols/__init__.py +0 -0
  38. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/protocols/fs.py +0 -0
  39. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/protocols/io.py +0 -0
  40. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/py.typed +0 -0
  41. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/testing.py +0 -0
  42. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/tools/__init__.py +0 -0
  43. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/tools/uripath.py +0 -0
  44. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/uri/__init__.py +0 -0
  45. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/uri/query.py +0 -0
  46. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/uri/schemes/__init__.py +0 -0
  47. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/uri/schemes/_gitrepo.py +0 -0
  48. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/uri/schemes/archive/__init__.py +0 -0
  49. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/uri/schemes/archive/_base.py +0 -0
  50. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/uri/schemes/archive/tar.py +0 -0
  51. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/uri/schemes/archive/zip.py +0 -0
  52. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/uri/schemes/az.py +0 -0
  53. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/uri/schemes/data.py +0 -0
  54. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/uri/schemes/dav.py +0 -0
  55. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/uri/schemes/file.py +0 -0
  56. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/uri/schemes/ftp.py +0 -0
  57. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/uri/schemes/git/__init__.py +0 -0
  58. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/uri/schemes/git/_base.py +0 -0
  59. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/uri/schemes/git/github.py +0 -0
  60. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/uri/schemes/git/gitlab.py +0 -0
  61. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/uri/schemes/github.py +0 -0
  62. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/uri/schemes/gitlab.py +0 -0
  63. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/uri/schemes/gs.py +0 -0
  64. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/uri/schemes/http.py +0 -0
  65. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/uri/schemes/s3.py +0 -0
  66. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/uri/schemes/sftp/__init__.py +0 -0
  67. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/uri/schemes/sftp/_paramiko.py +0 -0
  68. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/uri/schemes/sftp/_sshconfig.py +0 -0
  69. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/uri/source.py +0 -0
  70. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/utils/__init__.py +0 -0
  71. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/utils/archive.py +0 -0
  72. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/utils/checksum.py +0 -0
  73. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/utils/glob.py +0 -0
  74. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/utils/stat.py +0 -0
  75. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/src/pathlib_next/utils/sync.py +0 -0
  76. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/conftest.py +0 -0
  77. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_archive_uri.py +0 -0
  78. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_az.py +0 -0
  79. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_az_fake.py +0 -0
  80. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_contract.py +0 -0
  81. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_data_uri.py +0 -0
  82. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_dav.py +0 -0
  83. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_ftp.py +0 -0
  84. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_gitrepo.py +0 -0
  85. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_glob.py +0 -0
  86. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_gs.py +0 -0
  87. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_gs_fake.py +0 -0
  88. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_http.py +0 -0
  89. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_http_live.py +0 -0
  90. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_http_parser.py +0 -0
  91. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_local.py +0 -0
  92. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_mempath.py +0 -0
  93. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_parity_io.py +0 -0
  94. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_parity_pure.py +0 -0
  95. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_path_gaps.py +0 -0
  96. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_pathname.py +0 -0
  97. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_plugins.py +0 -0
  98. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_properties.py +0 -0
  99. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_query.py +0 -0
  100. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_s3.py +0 -0
  101. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_sftp.py +0 -0
  102. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_smoke.py +0 -0
  103. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_source.py +0 -0
  104. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_sync.py +0 -0
  105. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_uri_parse.py +0 -0
  106. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_uri_path.py +0 -0
  107. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_uripath_tool.py +0 -0
  108. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_utils.py +0 -0
  109. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_walk.py +0 -0
  110. {pathlib_next-0.8.2 → pathlib_next-0.8.3}/tests/test_webdav.py +0 -0
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.8.3] - 2026-07-18
11
+
12
+ ### Changed
13
+ - **`AsyncsshSftpBackend` default `max_concurrency` raised 8 → 16.** A 128-file
14
+ loopback recursive-copy/rm sweep of `mc ∈ {1,2,4,8,16}` (median of 3) showed
15
+ recursive copy improving monotonically with concurrency (mc=8→16 ≈3% faster on
16
+ top of mc=1→8 ≈1.13x) and recursive remove flat within noise, with 16
17
+ fastest-or-tied and well inside asyncssh's SFTP request window. Exposed as
18
+ `AsyncsshSftpBackend.DEFAULT_MAX_CONCURRENCY`; pass `max_concurrency=` to
19
+ override. Loopback evidence only — a high-latency remote link may warrant a
20
+ different value; 16 is a safe modest default, not a tuned optimum.
21
+
10
22
  ## [0.8.2] - 2026-07-18
11
23
 
12
24
  ### Fixed
@@ -495,7 +507,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
495
507
  - Sync error handling.
496
508
  - Generic Path Protocol based pathlib implementation for URI paths with file access support for sftp, http, file schemes.
497
509
 
498
- [Unreleased]: https://github.com/jose-pr/pathlib_next/compare/v0.8.2...HEAD
510
+ [Unreleased]: https://github.com/jose-pr/pathlib_next/compare/v0.8.3...HEAD
511
+ [0.8.3]: https://github.com/jose-pr/pathlib_next/compare/v0.8.2...v0.8.3
499
512
  [0.8.2]: https://github.com/jose-pr/pathlib_next/compare/v0.8.1...v0.8.2
500
513
  [0.8.1]: https://github.com/jose-pr/pathlib_next/compare/v0.8.0...v0.8.1
501
514
  [0.8.0]: https://github.com/jose-pr/pathlib_next/compare/v0.7.0...v0.8.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pathlib_next
3
- Version: 0.8.2
3
+ Version: 0.8.3
4
4
  Summary: Generic Path Protocol based pathlib
5
5
  Project-URL: Homepage, https://github.com/jose-pr/pathlib_next/
6
6
  Project-URL: Documentation, https://jose-pr.github.io/pathlib_next/
@@ -142,6 +142,16 @@ Legend: `p` = `paramiko`, `a` = `asyncssh`.
142
142
  was fastest at `7.6068s`, with `4` at `14.2806s` and `8` at `13.3736s`.
143
143
  Treat this as workload-specific follow-up evidence, not as a default tuning
144
144
  decision.
145
+ - **2026-07-18 (0.8.3): `AsyncsshSftpBackend` default `max_concurrency` raised
146
+ 8 → 16.** A cleaner 128-file loopback sweep of `mc ∈ {1,2,4,8,16}` (median of
147
+ 3, py3.14) showed recursive copy improving *monotonically* with concurrency
148
+ (mc=1 `1.66s` → mc=8 `1.47s` ≈ 1.13x → mc=16 `1.42s`, a further ≈3%), and
149
+ recursive remove flat within noise (median spread `0.498`..`0.551`, mc=16
150
+ marginally best). This supersedes the earlier "mc=1 fastest" loopback reading
151
+ (from the older code path / a different venv). 16 stays within asyncssh's SFTP
152
+ request window. **Loopback only** — no per-op latency; a high-latency remote
153
+ link may favour higher concurrency still, so 16 is a safe modest default, not
154
+ a tuned optimum. Override per backend via `AsyncsshSftpBackend(max_concurrency=…)`.
145
155
  - `python benchmarks/bench.py syncer` on the same local run reported
146
156
  PathSyncer copy of 128 local files at `0.4524s`, dry-run at `0.0534s`, and
147
157
  remove-missing plus copy at `0.9260s`. This suggests metadata reuse may be
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "pathlib_next"
7
- version = "0.8.2"
7
+ version = "0.8.3"
8
8
  authors = [{ name = "Jose A" }]
9
9
  description = "Generic Path Protocol based pathlib"
10
10
  readme = "README.md"
@@ -482,14 +482,28 @@ class AsyncsshSftpBackend(BaseSftpBackend):
482
482
  #: real-world OpenSSH v3 servers, or the standard opcode against v5/v6).
483
483
  supports_hardlink = True
484
484
 
485
+ #: Default bound on concurrent SFTP requests during a recursive copy/rm.
486
+ #: 16 (raised from 8 in 0.8.3): a 128-file loopback sweep of mc in
487
+ #: {1,2,4,8,16} (median of 3, 3.14) showed recursive copy improving
488
+ #: monotonically with concurrency -- mc=1 -> mc=8 ~1.13x, mc=8 -> mc=16 a
489
+ #: further ~3% -- with recursive rm flat (spread within run-to-run noise) and
490
+ #: mc=16 fastest-or-tied for both. 16 stays well inside asyncssh's SFTP
491
+ #: request window, so the extra in-flight requests carry no overload risk.
492
+ #: NOTE: loopback evidence only (no per-op network latency); a high-latency
493
+ #: remote link may favour even higher concurrency, but 16 is a safe, modest
494
+ #: default. Override per-backend via ``max_concurrency=``.
495
+ DEFAULT_MAX_CONCURRENCY = 16
496
+
485
497
  def __init__(
486
498
  self,
487
499
  connect_opts: "dict[str, _ty.Any] | None" = None,
488
500
  *,
489
- max_concurrency: int = 8,
501
+ max_concurrency: "int | None" = None,
490
502
  sftp_version: int = 4,
491
503
  ssh_config=_DEFAULT_SSH_CONFIG,
492
504
  ):
505
+ if max_concurrency is None:
506
+ max_concurrency = self.DEFAULT_MAX_CONCURRENCY
493
507
  self.connect_opts = {} if connect_opts is None else dict(connect_opts)
494
508
  if "config" not in self.connect_opts:
495
509
  if ssh_config is None:
@@ -293,9 +293,12 @@ def test_asyncssh_backend_has_max_concurrency():
293
293
  assert backend.max_concurrency == 16
294
294
 
295
295
 
296
- def test_asyncssh_backend_max_concurrency_defaults_to_8():
296
+ def test_asyncssh_backend_max_concurrency_defaults_to_16():
297
+ # Raised from 8 to 16 in 0.8.3 (loopback recursive-copy sweep); see
298
+ # DEFAULT_MAX_CONCURRENCY's rationale in _asyncssh.py.
297
299
  backend = backend_mod.AsyncsshSftpBackend()
298
- assert backend.max_concurrency == 8
300
+ assert backend.max_concurrency == 16
301
+ assert backend.max_concurrency == backend_mod.AsyncsshSftpBackend.DEFAULT_MAX_CONCURRENCY
299
302
  assert "config" not in backend.connect_opts
300
303
 
301
304
 
@@ -757,3 +760,14 @@ def test_sftppath_rm_recursive_uses_concurrent_helper(monkeypatch):
757
760
  assert recorded["kwargs"]["max_concurrency"] == 6
758
761
  assert recorded["kwargs"]["missing_ok"] is True
759
762
  assert recorded["kwargs"]["on_error"](ValueError("ignored"), src) is True
763
+
764
+
765
+ # --- default max_concurrency -------------------------------------------------
766
+
767
+
768
+ def test_explicit_max_concurrency_overrides_default():
769
+ backend = backend_mod.AsyncsshSftpBackend({"config": None}, max_concurrency=4)
770
+ assert backend.max_concurrency == 4
771
+ # 0 is honored as given (the async helpers clamp to >=1 at use time via
772
+ # `max(1, max_concurrency)`), not silently replaced by the default.
773
+ assert backend_mod.AsyncsshSftpBackend({"config": None}, max_concurrency=1).max_concurrency == 1
File without changes
File without changes
File without changes
File without changes
File without changes