duplicity 3.0.5__tar.gz → 3.0.6.dev7__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 (144) hide show
  1. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/CHANGELOG.md +13 -0
  2. {duplicity-3.0.5/duplicity.egg-info → duplicity-3.0.6.dev7}/PKG-INFO +5 -16
  3. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/__init__.py +2 -2
  4. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/__main__.py +1 -1
  5. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backend.py +40 -60
  6. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/multibackend.py +20 -16
  7. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/s3_boto3_backend.py +40 -18
  8. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/ssh_pexpect_backend.py +1 -1
  9. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/swiftbackend.py +3 -0
  10. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/dup_main.py +5 -3
  11. {duplicity-3.0.5 → duplicity-3.0.6.dev7/duplicity.egg-info}/PKG-INFO +5 -16
  12. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity.egg-info/requires.txt +2 -14
  13. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/man/duplicity.1 +2 -2
  14. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/af_ZA/duplicity.mo +0 -0
  15. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/ca_ES/duplicity.mo +0 -0
  16. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/en_AU/duplicity.mo +0 -0
  17. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/en_PR/duplicity.mo +0 -0
  18. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/en_US/duplicity.mo +0 -0
  19. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/he_IL/duplicity.mo +0 -0
  20. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/sr_SP/duplicity.mo +0 -0
  21. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/vi_VN/duplicity.mo +0 -0
  22. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/zh_HK/duplicity.mo +0 -0
  23. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/zh_MO/duplicity.mo +0 -0
  24. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/zh_SG/duplicity.mo +0 -0
  25. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/zh_TW/duplicity.mo +0 -0
  26. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/pyproject.toml +70 -13
  27. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/requirements.dev +1 -2
  28. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/requirements.txt +2 -0
  29. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/setup.py +10 -1
  30. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/AUTHORS.md +0 -0
  31. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/COPYING +0 -0
  32. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/README-LOG.md +0 -0
  33. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/README-REPO.md +0 -0
  34. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/README-TESTING.md +0 -0
  35. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/README.md +0 -0
  36. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/_librsyncmodule.c +0 -0
  37. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/argparse311.py +0 -0
  38. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backend_pool.py +0 -0
  39. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/__init__.py +0 -0
  40. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/_cf_cloudfiles.py +0 -0
  41. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/_cf_pyrax.py +0 -0
  42. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/_testbackend.py +0 -0
  43. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/adbackend.py +0 -0
  44. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/azurebackend.py +0 -0
  45. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/b2backend.py +0 -0
  46. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/boxbackend.py +0 -0
  47. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/cfbackend.py +0 -0
  48. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/dpbxbackend.py +0 -0
  49. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/gdocsbackend.py +0 -0
  50. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/gdrivebackend.py +0 -0
  51. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/giobackend.py +0 -0
  52. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/hsibackend.py +0 -0
  53. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/hubicbackend.py +0 -0
  54. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/idrivedbackend.py +0 -0
  55. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/imapbackend.py +0 -0
  56. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/jottacloudbackend.py +0 -0
  57. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/lftpbackend.py +0 -0
  58. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/localbackend.py +0 -0
  59. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/mediafirebackend.py +0 -0
  60. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/megabackend.py +0 -0
  61. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/megav2backend.py +0 -0
  62. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/megav3backend.py +0 -0
  63. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/ncftpbackend.py +0 -0
  64. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/onedrivebackend.py +0 -0
  65. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/par2backend.py +0 -0
  66. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/pcabackend.py +0 -0
  67. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/pydrivebackend.py +0 -0
  68. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/pyrax_identity/__init__.py +0 -0
  69. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/pyrax_identity/hubic.py +0 -0
  70. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/rclonebackend.py +0 -0
  71. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/rsyncbackend.py +0 -0
  72. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/slatebackend.py +0 -0
  73. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/ssh_paramiko_backend.py +0 -0
  74. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/sxbackend.py +0 -0
  75. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/tahoebackend.py +0 -0
  76. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/webdavbackend.py +0 -0
  77. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/backends/xorrisobackend.py +0 -0
  78. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/cached_ops.py +0 -0
  79. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/cli_data.py +0 -0
  80. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/cli_main.py +0 -0
  81. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/cli_util.py +0 -0
  82. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/config.py +0 -0
  83. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/diffdir.py +0 -0
  84. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/dup_collections.py +0 -0
  85. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/dup_tarfile.py +0 -0
  86. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/dup_temp.py +0 -0
  87. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/dup_time.py +0 -0
  88. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/errors.py +0 -0
  89. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/file_naming.py +0 -0
  90. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/filechunkio.py +0 -0
  91. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/globmatch.py +0 -0
  92. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/gpg.py +0 -0
  93. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/gpginterface.py +0 -0
  94. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/lazy.py +0 -0
  95. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/librsync.py +0 -0
  96. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/log.py +0 -0
  97. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/manifest.py +0 -0
  98. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/patchdir.py +0 -0
  99. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/path.py +0 -0
  100. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/progress.py +0 -0
  101. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/robust.py +0 -0
  102. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/selection.py +0 -0
  103. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/statistics.py +0 -0
  104. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/tempdir.py +0 -0
  105. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity/util.py +0 -0
  106. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity.egg-info/SOURCES.txt +0 -0
  107. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity.egg-info/dependency_links.txt +0 -0
  108. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity.egg-info/entry_points.txt +0 -0
  109. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/duplicity.egg-info/top_level.txt +0 -0
  110. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/ar_SA/duplicity.mo +0 -0
  111. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/cs_CZ/duplicity.mo +0 -0
  112. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/da_DK/duplicity.mo +0 -0
  113. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/de_AT/duplicity.mo +0 -0
  114. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/de_DE/duplicity.mo +0 -0
  115. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/el_GR/duplicity.mo +0 -0
  116. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/en_GB/duplicity.mo +0 -0
  117. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/es_EM/duplicity.mo +0 -0
  118. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/es_ES/duplicity.mo +0 -0
  119. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/es_MX/duplicity.mo +0 -0
  120. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/es_PR/duplicity.mo +0 -0
  121. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/es_US/duplicity.mo +0 -0
  122. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/fi_FI/duplicity.mo +0 -0
  123. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/fr_FR/duplicity.mo +0 -0
  124. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/hu_HU/duplicity.mo +0 -0
  125. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/it_IT/duplicity.mo +0 -0
  126. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/ja_JP/duplicity.mo +0 -0
  127. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/ko_KR/duplicity.mo +0 -0
  128. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/nl_BE/duplicity.mo +0 -0
  129. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/nl_NL/duplicity.mo +0 -0
  130. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/nl_SR/duplicity.mo +0 -0
  131. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/no_NO/duplicity.mo +0 -0
  132. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/pl_PL/duplicity.mo +0 -0
  133. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/pt_BR/duplicity.mo +0 -0
  134. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/pt_PT/duplicity.mo +0 -0
  135. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/ro_RO/duplicity.mo +0 -0
  136. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/ru_BY/duplicity.mo +0 -0
  137. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/ru_MD/duplicity.mo +0 -0
  138. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/ru_RU/duplicity.mo +0 -0
  139. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/ru_UA/duplicity.mo +0 -0
  140. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/sv_SE/duplicity.mo +0 -0
  141. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/tr_TR/duplicity.mo +0 -0
  142. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/uk_UA/duplicity.mo +0 -0
  143. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/po/zh_CN/duplicity.mo +0 -0
  144. {duplicity-3.0.5 → duplicity-3.0.6.dev7}/setup.cfg +0 -0
@@ -1,6 +1,19 @@
1
1
  # Changelog
2
2
 
3
3
 
4
+ ## rel.3.0.5.1 (2025-06-25)
5
+
6
+ ### Changes
7
+
8
+ * Fixes for LP builds. See #877. [Kenneth Loafman]
9
+
10
+ ### Fix
11
+
12
+ * Revert "Fix build-system.requires and requirements.txt" [Kenneth Loafman]
13
+
14
+ This reverts commit 3683aa4dd8c0a5fa4d70eee256a853310ffa9a87.
15
+
16
+
4
17
  ## rel.3.0.5 (2025-06-19)
5
18
 
6
19
  ### New
@@ -1,10 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: duplicity
3
- Version: 3.0.5
3
+ Version: 3.0.6.dev7
4
4
  Summary: Encrypted backup using rsync algorithm
5
5
  Author-email: Kenneth Loafman <kenneth@loafman.com>
6
6
  Maintainer: Edgar Soldin, Thomas Laubrock
7
- License: GPL-2.0-or-later
8
7
  Project-URL: Homepage, http://duplicity.us
9
8
  Project-URL: Download, https://gitlab.com/duplicity/duplicity/-/releases
10
9
  Project-URL: Issues, https://gitlab.com/duplicity/duplicity/-/issues
@@ -12,6 +11,7 @@ Project-URL: Contact, https://lists.nongnu.org/mailman/listinfo/duplicity-talk
12
11
  Project-URL: Documentation, http://duplicity.us
13
12
  Project-URL: Repository, https://gitlab.com/duplicity/duplicity
14
13
  Project-URL: Changelog, https://gitlab.com/duplicity/duplicity/-/blob/main/CHANGELOG.md
14
+ Platform: any
15
15
  Classifier: Development Status :: 6 - Mature
16
16
  Classifier: Environment :: Console
17
17
  Classifier: Operating System :: MacOS
@@ -25,11 +25,13 @@ Classifier: Programming Language :: Python :: 3.11
25
25
  Classifier: Programming Language :: Python :: 3.12
26
26
  Classifier: Programming Language :: Python :: 3.13
27
27
  Classifier: Topic :: System :: Archiving :: Backup
28
- Requires-Python: <3.14,>=3.8
28
+ Requires-Python: <3.15,>=3.8
29
29
  Description-Content-Type: text/markdown
30
30
  License-File: COPYING
31
31
  License-File: AUTHORS.md
32
32
  Requires-Dist: fasteners>=0.19
33
+ Requires-Dist: python-gettext>=5.0
34
+ Requires-Dist: setuptools>=78.1.0
33
35
  Requires-Dist: azure-storage-blob
34
36
  Requires-Dist: b2sdk
35
37
  Requires-Dist: boto3
@@ -53,19 +55,6 @@ Requires-Dist: pyrax
53
55
  Requires-Dist: python-swiftclient
54
56
  Requires-Dist: requests
55
57
  Requires-Dist: requests-oauthlib
56
- Provides-Extra: dev
57
- Requires-Dist: setuptools>=68.1.0; extra == "dev"
58
- Requires-Dist: black==24.8.0; extra == "dev"
59
- Requires-Dist: coverage; extra == "dev"
60
- Requires-Dist: pycodestyle; extra == "dev"
61
- Requires-Dist: pylint; extra == "dev"
62
- Requires-Dist: pytest; extra == "dev"
63
- Requires-Dist: pytest-cov; extra == "dev"
64
- Requires-Dist: gitchangelog; extra == "dev"
65
- Requires-Dist: myst-parser; extra == "dev"
66
- Requires-Dist: pystache; extra == "dev"
67
- Requires-Dist: sphinx; extra == "dev"
68
- Requires-Dist: sphinx-rtd-theme; extra == "dev"
69
58
  Dynamic: license-file
70
59
 
71
60
  # REQUIREMENTS
@@ -21,7 +21,7 @@
21
21
 
22
22
  import gettext
23
23
 
24
- __version__: str = "3.0.5"
25
- __reldate__: str = "June 19, 2025"
24
+ __version__: str = "3.0.6.dev7"
25
+ __reldate__: str = "August 02, 2025"
26
26
 
27
27
  gettext.install("duplicity", names=["ngettext"])
@@ -96,7 +96,7 @@ def dup_run():
96
96
  # For backend errors, don't show an ugly stack trace by
97
97
  # default. But do with sufficient verbosity.
98
98
  log.Info(_("Backend error detail: %s") % util.exception_traceback())
99
- log.FatalError(f"{e.__class__.__name__}: {util.uexc(e)}", log.ErrorCode.user_error, e.__class__.__name__)
99
+ log.FatalError(f"{e.__class__.__name__}: {util.uexc(e)}", log.ErrorCode.backend_error, e.__class__.__name__)
100
100
 
101
101
  except Exception as e:
102
102
  util.release_lockfile()
@@ -57,7 +57,6 @@ from duplicity.util import exception_traceback
57
57
 
58
58
  _backends = {}
59
59
  _backend_prefixes = {}
60
- _last_exception = None
61
60
 
62
61
  # These URL schemes have a backend with a notion of an RFC "network location".
63
62
  # The 'file' and 's3+http' schemes should not be in this list.
@@ -369,72 +368,53 @@ def retry(operation, fatal=True):
369
368
  # have to return a decorator function (which itself returns a function!)
370
369
  def outer_retry(fn):
371
370
  def inner_retry(self, *args):
372
- global _last_exception
373
- errors_fatal, errors_default = config.are_errors_fatal.get(operation, (True, None))
374
371
  for n in range(1, config.num_retries + 1):
375
372
  try:
376
373
  return fn(self, *args)
377
374
  except FatalBackendException as e:
378
- _last_exception = e
379
- if not errors_fatal:
380
- # backend wants to report and ignore errors
381
- return errors_default
382
- else:
383
- # die on fatal errors
384
- raise e
375
+ raise
385
376
  except Exception as e:
386
- _last_exception = e
387
- if not errors_fatal:
388
- # backend wants to report and ignore errors
389
- return errors_default
390
- else:
391
- # retry on anything else
392
- log.Debug(_("Backtrace of previous error: %s") % exception_traceback())
393
- at_end = n == config.num_retries
394
- code = _get_code_from_exception(self.backend, operation, e)
395
- if code == log.ErrorCode.backend_not_found:
396
- # If we tried to do something, but the file just isn't there,
397
- # no need to retry.
398
- at_end = True
399
- if at_end and fatal:
400
-
401
- def make_filename(f):
402
- if isinstance(f, path.ROPath):
403
- return util.escape(f.uc_name)
404
- else:
405
- return util.escape(f)
406
-
407
- extra = " ".join(
408
- [operation] + [make_filename(x) for x in args if (x and isinstance(x, str))]
409
- )
410
- if multiprocessing.parent_process():
411
- # running as a child process we need to raise an exception to signal an issue
412
- log.Error(
413
- _("Giving up after %s attempts. %s: %s. (for trace back: set log level DEBUG)")
414
- % (n, e.__class__.__name__, util.uexc(e)),
415
- code=code,
416
- extra=extra,
417
- )
418
- e.code = code
419
- raise
377
+ # retry on anything else
378
+ log.Debug(_("Backtrace of previous error: %s") % exception_traceback())
379
+ at_end = n == config.num_retries
380
+ code = _get_code_from_exception(self.backend, operation, e)
381
+ if code == log.ErrorCode.backend_not_found:
382
+ # If we tried to do something, but the file just isn't there,
383
+ # no need to retry.
384
+ at_end = True
385
+ if at_end and fatal:
386
+
387
+ def make_filename(f):
388
+ if isinstance(f, path.ROPath):
389
+ return util.escape(f.uc_name)
420
390
  else:
421
- log.FatalError(
422
- _("Giving up after %s attempts. %s: %s") % (n, e.__class__.__name__, util.uexc(e)),
423
- code=code,
424
- extra=extra,
425
- )
391
+ return util.escape(f)
392
+
393
+ extra = " ".join([operation] + [make_filename(x) for x in args if (x and isinstance(x, str))])
394
+ log.Error(
395
+ _("Giving up after %s attempts. %s: %s. (for trace back: set log level DEBUG)")
396
+ % (n, e.__class__.__name__, util.uexc(e)),
397
+ code=code,
398
+ extra=extra,
399
+ )
400
+ e.code = code
401
+ # Ensure it's a BackendException, so that __main__ top-level handler exits with
402
+ # code backend_error.
403
+ if not isinstance(e, BackendException):
404
+ e = BackendException(str(e), code=e.code)
405
+ raise e
406
+ else:
407
+ log.Warn(
408
+ _("Attempt of %s Nr. %s failed. %s: %s")
409
+ % (fn.__name__, n, e.__class__.__name__, util.uexc(e))
410
+ )
411
+ if not at_end:
412
+ if isinstance(e, TemporaryLoadException):
413
+ time.sleep(3 * config.backend_retry_delay) # wait longer before trying again
426
414
  else:
427
- log.Warn(
428
- _("Attempt of %s Nr. %s failed. %s: %s")
429
- % (fn.__name__, n, e.__class__.__name__, util.uexc(e))
430
- )
431
- if not at_end:
432
- if isinstance(e, TemporaryLoadException):
433
- time.sleep(3 * config.backend_retry_delay) # wait longer before trying again
434
- else:
435
- time.sleep(config.backend_retry_delay) # wait a bit before trying again
436
- if hasattr(self.backend, "_retry_cleanup"):
437
- self.backend._retry_cleanup()
415
+ time.sleep(config.backend_retry_delay) # wait a bit before trying again
416
+ if hasattr(self.backend, "_retry_cleanup"):
417
+ self.backend._retry_cleanup()
438
418
 
439
419
  return inner_retry
440
420
 
@@ -288,14 +288,17 @@ class MultiBackend(duplicity.backend.Backend):
288
288
  )
289
289
  raise BackendException("failed to write")
290
290
 
291
- # If we've looped around, and none of them passed, fail
292
- if (self.__write_cursor == first) and not passed:
293
- log.Log(
294
- _("MultiBackend: failed to write %s. Tried all backing stores and none succeeded")
295
- % source_path,
296
- log.ERROR,
297
- )
298
- raise BackendException("failed to write")
291
+ if self.__write_cursor == first:
292
+ if passed:
293
+ break
294
+ else:
295
+ # If we've looped around, and none of them passed, fail
296
+ log.Log(
297
+ _("MultiBackend: failed to write %s. Tried all backing stores and none succeeded")
298
+ % source_path,
299
+ log.ERROR,
300
+ )
301
+ raise BackendException("failed to write")
299
302
 
300
303
  def _get(self, remote_filename, local_path):
301
304
  # since the backend operations will be retried, we can't
@@ -325,17 +328,18 @@ class MultiBackend(duplicity.backend.Backend):
325
328
  def _list(self):
326
329
  lists = []
327
330
  for s in self.__stores:
328
- config.are_errors_fatal["list"] = (False, [])
329
- l = s.list()
331
+ try:
332
+ l = s.list()
333
+ except BackendException as e:
334
+ l = []
335
+ last_exception = e
336
+ else:
337
+ last_exception = None
330
338
  log.Notice(_("MultiBackend: %s: %d files") % (s.backend.parsed_url.strip_auth(), len(l)))
331
- if len(l) == 0 and duplicity.backend._last_exception:
339
+ if len(l) == 0 and last_exception:
332
340
  log.Warn(
333
- _(
334
- f"Exception during list of {s.backend.parsed_url.strip_auth()}: "
335
- f"{util.uexc(duplicity.backend._last_exception)}"
336
- )
341
+ _(f"Exception during list of {s.backend.parsed_url.strip_auth()}: " f"{util.uexc(last_exception)}")
337
342
  )
338
- duplicity.backend._last_exception = None
339
343
  lists.append(l)
340
344
  # combine the lists into a single flat list w/o duplicates via set:
341
345
  result = list({item for sublist in lists for item in sublist})
@@ -31,6 +31,8 @@ from duplicity.errors import (
31
31
  BackendException,
32
32
  )
33
33
 
34
+ global boto3, botocore, ClientError, S3UploadFailedError, TransferConfig
35
+
34
36
 
35
37
  # Note: current gaps with the old boto backend include:
36
38
  # - Glacier restore to S3 not implemented. Should this
@@ -66,6 +68,25 @@ class S3Boto3Backend(duplicity.backend.Backend):
66
68
  """
67
69
 
68
70
  def __init__(self, parsed_url):
71
+ global boto3, botocore, ClientError, S3UploadFailedError, TransferConfig
72
+ import boto3
73
+ import botocore
74
+ from boto3.s3.transfer import S3UploadFailedError, TransferConfig
75
+ from botocore.exceptions import ClientError
76
+
77
+ if not (boto3.__version__ < "1.36.0" and botocore.__version__ < "1.36.0"):
78
+ # TODO: remove this workaround when issue #870 is fixed.
79
+ # https://github.com/boto/boto3/issues/2913
80
+ log.Warn(
81
+ "WARNING: Using boto3 >= 1,36.0 may result in errors, so we qre applying\n"
82
+ "the workaround for https://gitlab.com/duplicity/duplicity/-/issues/870\n"
83
+ " export AWS_REQUEST_CHECKSUM_CALCULATION=when_required\n"
84
+ " export AWS_RESPONSE_CHECKSUM_VALIDATION=when_required\n"
85
+ "NOTE: This workaround is temporary and will be removed when issue is fixed.\n."
86
+ )
87
+ os.environ["AWS_REQUEST_CHECKSUM_CALCULATION"] = "when_required"
88
+ os.environ["AWS_RESPONSE_CHECKSUM_VALIDATION"] = "when_required"
89
+
69
90
  duplicity.backend.Backend.__init__(self, parsed_url)
70
91
 
71
92
  # This folds the null prefix and all null parts, which means that:
@@ -89,10 +110,6 @@ class S3Boto3Backend(duplicity.backend.Backend):
89
110
  self.tracker = UploadProgressTracker()
90
111
 
91
112
  def reset_connection(self):
92
- import boto3
93
- import botocore
94
- from botocore.exceptions import ClientError
95
-
96
113
  self.bucket = None
97
114
  self.s3 = boto3.resource(
98
115
  "s3",
@@ -116,8 +133,6 @@ class S3Boto3Backend(duplicity.backend.Backend):
116
133
  self.bucket = self.s3.Bucket(self.bucket_name) # only set if bucket is thought to exist.
117
134
 
118
135
  def _put(self, local_source_path, remote_filename):
119
- from boto3.s3.transfer import TransferConfig
120
-
121
136
  if not self.s3:
122
137
  self.reset_connection()
123
138
 
@@ -178,16 +193,24 @@ class S3Boto3Backend(duplicity.backend.Backend):
178
193
  key = self.key_prefix + remote_filename
179
194
 
180
195
  log.Info(f"Uploading {self.straight_url}/{remote_filename} to {storage_class} Storage")
181
- self.s3.Object(self.bucket.name, key).upload_file(
182
- local_source_path.uc_name,
183
- Callback=tracker.progress_cb,
184
- Config=transfer_config,
185
- ExtraArgs=extra_args,
186
- )
196
+ try:
197
+ self.s3.Object(self.bucket.name, key).upload_file(
198
+ local_source_path.uc_name,
199
+ Callback=tracker.progress_cb,
200
+ Config=transfer_config,
201
+ ExtraArgs=extra_args,
202
+ )
203
+ except S3UploadFailedError as e:
204
+ if boto3.__version__ > "1.36.0" or botocore.__version__ > "1.36.0":
205
+ log.FatalError(
206
+ f"Failed to upload file {remote_filename}, got S3UploadFailedError\n"
207
+ f"See https://gitlab.com/duplicity/duplicity/-/issues/870 for details.\n"
208
+ f"Quick fix is: [sudo] pip3 install boto3<1.36.0 botocore<1.36.0"
209
+ )
210
+ else:
211
+ raise e
187
212
 
188
213
  def _get(self, remote_filename, local_path):
189
- from botocore.exceptions import ClientError
190
-
191
214
  if not self.s3:
192
215
  self.reset_connection()
193
216
 
@@ -198,8 +221,9 @@ class S3Boto3Backend(duplicity.backend.Backend):
198
221
  except ClientError as ios:
199
222
  if ios.response["Error"]["Code"] == "InvalidObjectState":
200
223
  log.FatalError(
201
- f"File {remote_filename} seems to be in a long term storage, "
202
- f"please use AWS Console/API to initiate restore.\nAPI-Error: {ios}"
224
+ f"File {remote_filename} seems to be in a long term storage,"
225
+ f"Please use AWS Console/API to initiate restore.\n"
226
+ f"API-Error: {ios}"
203
227
  )
204
228
  else:
205
229
  raise ios
@@ -230,8 +254,6 @@ class S3Boto3Backend(duplicity.backend.Backend):
230
254
  if not self.s3:
231
255
  self.reset_connection()
232
256
 
233
- import botocore
234
-
235
257
  remote_filename = os.fsdecode(remote_filename)
236
258
  key = self.key_prefix + remote_filename
237
259
  content_length = -1
@@ -79,7 +79,7 @@ class SSHPExpectBackend(duplicity.backend.Backend):
79
79
  # make sure remote_dir is always valid
80
80
  if parsed_url.path:
81
81
  # remove leading '/'
82
- self.remote_dir = parsed_url.path.lstrip("/")
82
+ self.remote_dir = re.sub(r"^/", r"", parsed_url.path, 1)
83
83
  else:
84
84
  self.remote_dir = "."
85
85
  self.remote_prefix = f"{self.remote_dir}/"
@@ -25,6 +25,8 @@ from duplicity import config
25
25
  from duplicity import log
26
26
  from duplicity.errors import BackendException
27
27
 
28
+ global Connection, ClientException, SwiftService, SwiftUploadObject
29
+
28
30
 
29
31
  class SwiftBackend(duplicity.backend.Backend):
30
32
  """
@@ -34,6 +36,7 @@ class SwiftBackend(duplicity.backend.Backend):
34
36
  def __init__(self, parsed_url):
35
37
  duplicity.backend.Backend.__init__(self, parsed_url)
36
38
 
39
+ global Connection, ClientException, SwiftService, SwiftUploadObject
37
40
  try:
38
41
  from swiftclient.service import SwiftUploadObject
39
42
  from swiftclient.service import SwiftService
@@ -376,7 +376,8 @@ def write_multivol(backup_type, tarblock_iter, man_outfp, sig_outfp, backend):
376
376
  transfer_success = False
377
377
  manifest_written = False
378
378
 
379
- def collect_put_results(bytes_written: int, backend_pooler, command2vol_map: Dict[int, CommandMetaData]):
379
+ def collect_put_results(backend_pooler, command2vol_map: Dict[int, CommandMetaData]):
380
+ bytes_written = 0
380
381
  for result in backend_pooler.results_since_last_call():
381
382
  track_id = result.track_id
382
383
  size = result.result
@@ -392,6 +393,7 @@ def write_multivol(backup_type, tarblock_iter, man_outfp, sig_outfp, backend):
392
393
  f"Transfer of {command2vol_map[track_id].path_obj.get_filename()} with id {track_id} and size "
393
394
  f"{size} took {result.get_runtime()}"
394
395
  )
396
+ return bytes_written
395
397
 
396
398
  def write_manifest_in_sequence(mf, mf_file, command2vol_map: Dict[int, CommandMetaData]):
397
399
  """
@@ -499,7 +501,7 @@ def write_multivol(backup_type, tarblock_iter, man_outfp, sig_outfp, backend):
499
501
  progress.report_transfer(0, tdp.getsize())
500
502
  track_id = backend_pooler.command_throttled(backend.put_validated.__name__, args=(tdp, dest_filename))
501
503
  command2vol_map[track_id] = CommandMetaData(vol_num, tdp, vi)
502
- collect_put_results(bytes_written, backend_pooler, command2vol_map)
504
+ bytes_written += collect_put_results(backend_pooler, command2vol_map)
503
505
  write_manifest_in_sequence(mf, man_outfp, command2vol_map)
504
506
  except (Exception, SystemExit) as e:
505
507
  # ensure pool processes terminate clean
@@ -529,7 +531,7 @@ def write_multivol(backup_type, tarblock_iter, man_outfp, sig_outfp, backend):
529
531
  # wait for background commands, collect some stats and shutdown clean.
530
532
  log.Debug("Collecting remaining results from backend pool.")
531
533
  while True and backend_pooler:
532
- collect_put_results(bytes_written, backend_pooler, command2vol_map)
534
+ bytes_written += collect_put_results(backend_pooler, command2vol_map)
533
535
  write_manifest_in_sequence(mf, man_outfp, command2vol_map)
534
536
  if backend_pooler.get_queue_length() == 0:
535
537
  break
@@ -1,10 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: duplicity
3
- Version: 3.0.5
3
+ Version: 3.0.6.dev7
4
4
  Summary: Encrypted backup using rsync algorithm
5
5
  Author-email: Kenneth Loafman <kenneth@loafman.com>
6
6
  Maintainer: Edgar Soldin, Thomas Laubrock
7
- License: GPL-2.0-or-later
8
7
  Project-URL: Homepage, http://duplicity.us
9
8
  Project-URL: Download, https://gitlab.com/duplicity/duplicity/-/releases
10
9
  Project-URL: Issues, https://gitlab.com/duplicity/duplicity/-/issues
@@ -12,6 +11,7 @@ Project-URL: Contact, https://lists.nongnu.org/mailman/listinfo/duplicity-talk
12
11
  Project-URL: Documentation, http://duplicity.us
13
12
  Project-URL: Repository, https://gitlab.com/duplicity/duplicity
14
13
  Project-URL: Changelog, https://gitlab.com/duplicity/duplicity/-/blob/main/CHANGELOG.md
14
+ Platform: any
15
15
  Classifier: Development Status :: 6 - Mature
16
16
  Classifier: Environment :: Console
17
17
  Classifier: Operating System :: MacOS
@@ -25,11 +25,13 @@ Classifier: Programming Language :: Python :: 3.11
25
25
  Classifier: Programming Language :: Python :: 3.12
26
26
  Classifier: Programming Language :: Python :: 3.13
27
27
  Classifier: Topic :: System :: Archiving :: Backup
28
- Requires-Python: <3.14,>=3.8
28
+ Requires-Python: <3.15,>=3.8
29
29
  Description-Content-Type: text/markdown
30
30
  License-File: COPYING
31
31
  License-File: AUTHORS.md
32
32
  Requires-Dist: fasteners>=0.19
33
+ Requires-Dist: python-gettext>=5.0
34
+ Requires-Dist: setuptools>=78.1.0
33
35
  Requires-Dist: azure-storage-blob
34
36
  Requires-Dist: b2sdk
35
37
  Requires-Dist: boto3
@@ -53,19 +55,6 @@ Requires-Dist: pyrax
53
55
  Requires-Dist: python-swiftclient
54
56
  Requires-Dist: requests
55
57
  Requires-Dist: requests-oauthlib
56
- Provides-Extra: dev
57
- Requires-Dist: setuptools>=68.1.0; extra == "dev"
58
- Requires-Dist: black==24.8.0; extra == "dev"
59
- Requires-Dist: coverage; extra == "dev"
60
- Requires-Dist: pycodestyle; extra == "dev"
61
- Requires-Dist: pylint; extra == "dev"
62
- Requires-Dist: pytest; extra == "dev"
63
- Requires-Dist: pytest-cov; extra == "dev"
64
- Requires-Dist: gitchangelog; extra == "dev"
65
- Requires-Dist: myst-parser; extra == "dev"
66
- Requires-Dist: pystache; extra == "dev"
67
- Requires-Dist: sphinx; extra == "dev"
68
- Requires-Dist: sphinx-rtd-theme; extra == "dev"
69
58
  Dynamic: license-file
70
59
 
71
60
  # REQUIREMENTS
@@ -1,4 +1,6 @@
1
1
  fasteners>=0.19
2
+ python-gettext>=5.0
3
+ setuptools>=78.1.0
2
4
  azure-storage-blob
3
5
  b2sdk
4
6
  boto3
@@ -22,17 +24,3 @@ pyrax
22
24
  python-swiftclient
23
25
  requests
24
26
  requests-oauthlib
25
-
26
- [dev]
27
- setuptools>=68.1.0
28
- black==24.8.0
29
- coverage
30
- pycodestyle
31
- pylint
32
- pytest
33
- pytest-cov
34
- gitchangelog
35
- myst-parser
36
- pystache
37
- sphinx
38
- sphinx-rtd-theme
@@ -1,4 +1,4 @@
1
- .TH DUPLICITY 1 "June 19, 2025" "Version 3.0.5" "User Manuals" \" -*- nroff -*-
1
+ .TH DUPLICITY 1 "August 02, 2025" "Version 3.0.6.dev7" "User Manuals" \" -*- nroff -*-
2
2
  .\" disable justification (adjust text to left margin only)
3
3
  .\" command line examples stay readable through that
4
4
  .ad l
@@ -22,7 +22,7 @@ source_url target_directory
22
22
 
23
23
  .B duplicity collection-status
24
24
  .I [options] [--file-changed <relpath>] [--show-changes-in-set <index>]
25
- [--jsonstat]] target_url
25
+ [--jsonstat] target_url
26
26
 
27
27
  .B duplicity list-current-files
28
28
  .I [options] [--time time]
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "duplicity"
3
- version = "3.0.5"
4
- dynamic = ["dependencies", "optional-dependencies"]
3
+ version = "3.0.6.dev7"
4
+ dynamic = ["dependencies"]
5
5
  description = "Encrypted backup using rsync algorithm"
6
6
  authors = [
7
7
  { name = "Kenneth Loafman", email = "kenneth@loafman.com" },
@@ -11,9 +11,9 @@ maintainers = [
11
11
  { name = "Thomas Laubrock" },
12
12
  ]
13
13
  readme = { file = "README.md", content-type = "text/markdown" }
14
- requires-python = ">= 3.8, < 3.14"
15
- # TODO: switch to PEP 639 string after dropping 3.8 support
16
- license = { text = "GPL-2.0-or-later" }
14
+ requires-python = ">= 3.8, < 3.15"
15
+ # not all LP builders will take license=
16
+ #license = "GPL-2.0-or-later"
17
17
  classifiers = [
18
18
  "Development Status :: 6 - Mature",
19
19
  "Environment :: Console",
@@ -32,7 +32,14 @@ classifiers = [
32
32
 
33
33
 
34
34
  [build-system]
35
- requires = ["setuptools>=68.1.0"]
35
+ requires = [
36
+ "build>=1.1",
37
+ "pip>=24.0",
38
+ "pipx>=1.5",
39
+ "packaging>=20.0",
40
+ "setuptools>=78.1.0",
41
+ "wheel>=0.42.0",
42
+ ]
36
43
  build-backend = "setuptools.build_meta"
37
44
 
38
45
 
@@ -50,13 +57,37 @@ Repository = "https://gitlab.com/duplicity/duplicity"
50
57
  Changelog = "https://gitlab.com/duplicity/duplicity/-/blob/main/CHANGELOG.md"
51
58
 
52
59
 
60
+ [tool.setuptools]
61
+ platforms = [
62
+ "any"
63
+ ]
64
+
65
+
53
66
  [tool.setuptools.dynamic]
54
- dependencies = { file = "requirements.txt" }
55
- optional-dependencies.dev = { file = "requirements.dev" }
67
+ dependencies = {file = ["requirements.txt"]}
56
68
 
57
69
 
58
70
  [tool.setuptools.packages.find]
59
- include = ["duplicity*"]
71
+ where = [
72
+ ".",
73
+ ]
74
+ include = [
75
+ "duplicity",
76
+ "duplicity.backends",
77
+ "duplicity.backends.pyrax_identity",
78
+ ]
79
+ exclude = [
80
+ ".*",
81
+ "Makefile",
82
+ "crowdin.yml",
83
+ "debian*",
84
+ "docs*",
85
+ "readthedocs.yaml",
86
+ "snap*",
87
+ "testing*",
88
+ "tools*",
89
+ "venv*",
90
+ ]
60
91
 
61
92
 
62
93
  [tool.black]
@@ -97,17 +128,43 @@ testpaths = [
97
128
 
98
129
 
99
130
  [tool.cibuildwheel]
131
+
132
+ [tool.cibuildwheel.config-settings]
133
+ build-option = "--use-pep517"
134
+
135
+ [tool.cibuildwheel.linux]
100
136
  archs = [
101
137
  "x86_64",
138
+ "aarch64",
102
139
  ]
103
140
  build = [
104
- "cp{38,39,310,311,312,313}-manylinux_x86_64",
141
+ "cp{39,310,311,312,313}-manylinux_aarch64",
142
+ "cp{39,310,311,312,313}-manylinux_x86_64",
105
143
  ]
106
144
  before-build = [
107
145
  # WARNING: wheel builds are Centos based, not Debian.
108
- "yum install -y gcc gcc-c++ make git intltool lftp librsync-devel",
109
- "yum install -y libffi-devel openssl-devel openssl par2cmdline rdiff-backup tzdata",
110
- "python -m pip install -r requirements.txt",
146
+ "yum update -y",
147
+ "yum install -y gcc gcc-c++ make git intltool librsync-devel",
148
+ "yum install -y libffi-devel openssl-devel openssl tzdata",
149
+ "yum install -y libxml2-devel libxslt-devel",
150
+ "python -m pip install -q -r requirements.txt",
151
+ "python ./setup.py build_ext",
152
+ ]
153
+
154
+ [tool.cibuildwheel.macos.environment]
155
+ MACOSX_DEPLOYMENT_TARGET="15.0"
156
+
157
+ [tool.cibuildwheel.macos]
158
+ archs = [
159
+ "x86_64",
160
+ "arm64",
161
+ ]
162
+ build = [
163
+ "cp{39,310,311,312,313}-macosx_arm64",
164
+ "cp{39,310,311,312,313}-macosx_x86_64",
165
+ ]
166
+ before-build = [
167
+ "python -m pip install -q -r requirements.txt",
111
168
  "python ./setup.py build_ext",
112
169
  ]
113
170
 
@@ -1,6 +1,5 @@
1
- setuptools>=68.1.0
2
1
 
3
- ##### testing libraries #####
2
+ ##### testing libraries #####
4
3
 
5
4
  black==24.8.0
6
5
  coverage
@@ -2,6 +2,8 @@
2
2
  ##### basic requirements #####
3
3
 
4
4
  fasteners>=0.19
5
+ python-gettext>=5.0
6
+ setuptools>=78.1.0
5
7
 
6
8
  ##### backend libraries #####
7
9
 
@@ -42,7 +42,7 @@ if not ((3, 8) <= sys.version_info[:2]):
42
42
  print("Sorry, duplicity requires version 3.8 thru 3.13 of Python.", file=sys.stderr)
43
43
  sys.exit(1)
44
44
 
45
- Version: str = "3.0.5"
45
+ Version: str = "3.0.6.dev7"
46
46
  reldate: str = time.strftime("%B %d, %Y", time.gmtime(int(os.environ.get("SOURCE_DATE_EPOCH", time.time()))))
47
47
 
48
48
  # READTHEDOCS uses setup.py sdist but can't handle extensions
@@ -279,6 +279,15 @@ class SetVersionCommand(Command):
279
279
 
280
280
 
281
281
  setup(
282
+ packages=[
283
+ "duplicity",
284
+ "duplicity.backends",
285
+ "duplicity.backends.pyrax_identity",
286
+ ],
287
+ package_dir={
288
+ "duplicity": "duplicity",
289
+ "duplicity.backends": "duplicity/backends",
290
+ },
282
291
  ext_modules=ext_modules,
283
292
  data_files=get_data_files(),
284
293
  include_package_data=True,
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes