duplicity 3.0.7.dev0__tar.gz → 3.0.8.dev3__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.
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/CHANGELOG.md +26 -4
- {duplicity-3.0.7.dev0/duplicity.egg-info → duplicity-3.0.8.dev3}/PKG-INFO +1 -1
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/__init__.py +2 -2
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/s3_boto3_backend.py +19 -13
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/webdavbackend.py +7 -3
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/cli_data.py +2 -2
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/cli_util.py +1 -1
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/diffdir.py +2 -22
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/dup_main.py +38 -30
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/log.py +25 -11
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/path.py +1 -22
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/util.py +2 -24
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3/duplicity.egg-info}/PKG-INFO +1 -1
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/man/duplicity.1 +33 -33
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/pyproject.toml +1 -4
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/setup.py +1 -1
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/AUTHORS.md +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/COPYING +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/README-LOG.md +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/README-REPO.md +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/README-TESTING.md +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/README.md +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/__main__.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/_librsyncmodule.c +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/argparse311.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backend_pool.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/__init__.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/_cf_cloudfiles.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/_cf_pyrax.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/_testbackend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/adbackend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/azurebackend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/b2backend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/boxbackend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/cfbackend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/dpbxbackend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/gdocsbackend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/gdrivebackend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/giobackend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/hsibackend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/hubicbackend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/idrivedbackend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/imapbackend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/jottacloudbackend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/lftpbackend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/localbackend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/mediafirebackend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/megabackend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/megav2backend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/megav3backend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/multibackend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/ncftpbackend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/onedrivebackend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/par2backend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/pcabackend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/pydrivebackend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/pyrax_identity/__init__.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/pyrax_identity/hubic.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/rclonebackend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/rsyncbackend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/slatebackend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/ssh_paramiko_backend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/ssh_pexpect_backend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/swiftbackend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/sxbackend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/tahoebackend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/backends/xorrisobackend.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/cached_ops.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/cli_main.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/config.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/dup_collections.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/dup_tarfile.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/dup_temp.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/dup_time.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/errors.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/file_naming.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/filechunkio.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/globmatch.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/gpg.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/gpginterface.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/lazy.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/librsync.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/manifest.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/patchdir.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/progress.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/robust.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/selection.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/statistics.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity/tempdir.py +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity.egg-info/SOURCES.txt +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity.egg-info/dependency_links.txt +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity.egg-info/entry_points.txt +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity.egg-info/requires.txt +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/duplicity.egg-info/top_level.txt +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/af_ZA/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/ar_SA/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/ca_ES/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/cs_CZ/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/da_DK/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/de_AT/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/de_DE/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/el_GR/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/en_AU/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/en_GB/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/en_PR/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/en_US/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/es_EM/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/es_ES/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/es_MX/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/es_PR/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/es_US/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/fi_FI/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/fr_FR/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/he_IL/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/hu_HU/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/it_IT/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/ja_JP/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/ko_KR/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/nl_BE/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/nl_NL/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/nl_SR/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/no_NO/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/pl_PL/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/pt_BR/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/pt_PT/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/ro_RO/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/ru_BY/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/ru_MD/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/ru_RU/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/ru_UA/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/sr_SP/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/sv_SE/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/tr_TR/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/uk_UA/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/vi_VN/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/zh_CN/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/zh_HK/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/zh_MO/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/zh_SG/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/po/zh_TW/duplicity.mo +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/requirements.txt +0 -0
- {duplicity-3.0.7.dev0 → duplicity-3.0.8.dev3}/setup.cfg +0 -0
|
@@ -1,10 +1,28 @@
|
|
|
1
1
|
|
|
2
|
-
(Unreleased) / 2025-
|
|
2
|
+
(Unreleased) / 2025-12-31
|
|
3
3
|
=========================
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
rel.3.0.7 / 2025-12-31
|
|
8
|
+
======================
|
|
9
|
+
|
|
10
|
+
* ad175c22:fix: replace custom deltree with built-in shutil.rmtree.
|
|
11
|
+
* 101be7fd:fix: delete duplicate code in DirDelta.
|
|
12
|
+
* c0d3e72d:fix: webdavs with "--concurrency 1" failed because of missing auth header
|
|
13
|
+
* a08f8bef:fix: disable s3 checksum workaround, warn only...
|
|
14
|
+
* f7055a7a:fix: --log-timestamp no longer working.
|
|
15
|
+
* ac163d5e:fix: Combined fix to related issues 912 and 914
|
|
16
|
+
|
|
17
|
+
rel.3.0.6.3 / 2025-12-05
|
|
18
|
+
========================
|
|
19
|
+
|
|
20
|
+
* dd45a92d:chg: Update check_tags to use current branch.
|
|
21
|
+
* 5bbb5813:fix: Change log level from Info to Notice in get_passphrase().
|
|
22
|
+
* a9c4fbad:chg: Add key_needs_passphrase(key).
|
|
23
|
+
* abaf8485:chg: Better error message from get_remote_file().
|
|
24
|
+
* b083fca8:fix: 'duplicity --no-check-remote inc' prints spurious warning "found missing difftar(s) in backup sets"
|
|
25
|
+
* 9400fc37:chg: Add check_tags minor fix to setversion.
|
|
8
26
|
* 29227571:fix: delete unused diffdir.DirSig, diffdir.SigTarBlockIter, librsync.SigFile.
|
|
9
27
|
* 29205145:fix: Delete unused IndexedTuple.
|
|
10
28
|
* b0653566:fix: Delete unused Patch, patch_diff_tarfile, PathPatcher.
|
|
@@ -444,6 +462,10 @@ rel.2.0.1 / 2023-08-08
|
|
|
444
462
|
* fc8da777:fix: Restore pre-parser. Fixes #727.
|
|
445
463
|
* 295e641b:fix: Add missing import to cli_util.py. Fixes #730.
|
|
446
464
|
* 97fd0957:fix: Add missing import to b2backend.py. Fixes #729.
|
|
465
|
+
|
|
466
|
+
rel.2.0.0 / 2023-08-07
|
|
467
|
+
======================
|
|
468
|
+
|
|
447
469
|
* 5cb97560:fix: Adjust version to build under LP.
|
|
448
470
|
* 598352ba:fix: Adjust to build under LP Mantic.
|
|
449
471
|
|
|
@@ -74,19 +74,6 @@ class S3Boto3Backend(duplicity.backend.Backend):
|
|
|
74
74
|
from boto3.s3.transfer import S3UploadFailedError, TransferConfig
|
|
75
75
|
from botocore.exceptions import ClientError
|
|
76
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
|
-
|
|
90
77
|
duplicity.backend.Backend.__init__(self, parsed_url)
|
|
91
78
|
|
|
92
79
|
# This folds the null prefix and all null parts, which means that:
|
|
@@ -109,6 +96,25 @@ class S3Boto3Backend(duplicity.backend.Backend):
|
|
|
109
96
|
self.bucket = None
|
|
110
97
|
self.tracker = UploadProgressTracker()
|
|
111
98
|
|
|
99
|
+
if not (boto3.__version__ < "1.36.0" and botocore.__version__ < "1.36.0"):
|
|
100
|
+
# this is an issue with 3rd party s3 implementations only
|
|
101
|
+
# likely when an endpoint is given that resides not under amazonaws.com
|
|
102
|
+
# in time that workaround will probably not be needed anymore
|
|
103
|
+
# https://github.com/boto/boto3/issues/2913
|
|
104
|
+
import re
|
|
105
|
+
|
|
106
|
+
if config.s3_endpoint_url and not re.match(
|
|
107
|
+
pattern="(?i).*\\.amazonaws\\.com(/+)?$", string=config.s3_endpoint_url
|
|
108
|
+
):
|
|
109
|
+
log.Warn(
|
|
110
|
+
"WARNING: Using boto3 >= 1,36.0 with non-amazon s3 services"
|
|
111
|
+
" may result in checksum errors."
|
|
112
|
+
" a workaround is to set the following env vars\n\n"
|
|
113
|
+
" export AWS_REQUEST_CHECKSUM_CALCULATION=when_required\n"
|
|
114
|
+
" export AWS_RESPONSE_CHECKSUM_VALIDATION=when_required\n\n"
|
|
115
|
+
"see https://gitlab.com/duplicity/duplicity/-/issues/870 for details."
|
|
116
|
+
)
|
|
117
|
+
|
|
112
118
|
def reset_connection(self):
|
|
113
119
|
self.bucket = None
|
|
114
120
|
self.s3 = boto3.resource(
|
|
@@ -194,9 +194,10 @@ class WebDAVBackend(duplicity.backend.Backend):
|
|
|
194
194
|
if self.username or self.password:
|
|
195
195
|
# Workaround cpython http.client issue
|
|
196
196
|
# https://github.com/python/cpython/issues/70107
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
197
|
+
# PUT may not return 401 when ran without basic-auth but throw SSL-EOF-Error or hang
|
|
198
|
+
# as a workaround we run an OPTIONS request that adds auth if needed and creates
|
|
199
|
+
# an authenticated connection to (re)use
|
|
200
|
+
response = self.request("OPTIONS", self.directory, None)
|
|
200
201
|
response.close()
|
|
201
202
|
|
|
202
203
|
def _close(self):
|
|
@@ -226,6 +227,8 @@ class WebDAVBackend(duplicity.backend.Backend):
|
|
|
226
227
|
|
|
227
228
|
if self.digest_challenge is not None:
|
|
228
229
|
self.headers["Authorization"] = self.get_digest_authorization(path)
|
|
230
|
+
elif self.username or self.password:
|
|
231
|
+
self.headers["Authorization"] = self.get_basic_authorization()
|
|
229
232
|
|
|
230
233
|
log.Debug(_("WebDAV %s %s request with headers: %s ") % (method, quoted_path, munge_headers(self.headers)))
|
|
231
234
|
log.Debug(_("WebDAV data length: %s ") % sys.getsizeof(data))
|
|
@@ -245,6 +248,7 @@ class WebDAVBackend(duplicity.backend.Backend):
|
|
|
245
248
|
return self.request(method, self.directory, data, redirected + 1)
|
|
246
249
|
else:
|
|
247
250
|
raise FatalBackendException(_("WebDAV missing location header in redirect response."))
|
|
251
|
+
# mainly for digest-auth to recalculate with response values
|
|
248
252
|
elif response.status == 401:
|
|
249
253
|
response.read()
|
|
250
254
|
response.close()
|
|
@@ -446,9 +446,9 @@ OptionKwargs = dict(
|
|
|
446
446
|
type=set_log_file,
|
|
447
447
|
help="Logging filename to use",
|
|
448
448
|
),
|
|
449
|
-
# log_timestamp is directly applied in
|
|
449
|
+
# log_timestamp is directly applied in set_log_timestamp(), not saved in config
|
|
450
450
|
log_timestamp=dict(
|
|
451
|
-
|
|
451
|
+
nargs=0,
|
|
452
452
|
action=SetLogTimestampAction,
|
|
453
453
|
help="Whether to include timestamp and level in log",
|
|
454
454
|
default=dflt(False),
|
|
@@ -160,7 +160,7 @@ class SetLogTimestampAction(argparse._StoreConstAction):
|
|
|
160
160
|
super().__init__(option_strings, dest, **kwargs)
|
|
161
161
|
|
|
162
162
|
def __call__(self, parser, namespace, values, option_string=None):
|
|
163
|
-
log.
|
|
163
|
+
log.add_timestamp()
|
|
164
164
|
|
|
165
165
|
|
|
166
166
|
def _check_int(val):
|
|
@@ -37,7 +37,7 @@ from duplicity import dup_tarfile
|
|
|
37
37
|
from duplicity import util
|
|
38
38
|
from duplicity.path import * # pylint: disable=unused-wildcard-import,redefined-builtin
|
|
39
39
|
|
|
40
|
-
# A StatsObj will be written to this from
|
|
40
|
+
# A StatsObj will be written to this from DirDelta_WriteSig.
|
|
41
41
|
stats = None
|
|
42
42
|
tracker = None
|
|
43
43
|
|
|
@@ -55,7 +55,7 @@ def DirFull(path_iter):
|
|
|
55
55
|
will be easy to split up the tar and make the volumes the same
|
|
56
56
|
sizes).
|
|
57
57
|
"""
|
|
58
|
-
return
|
|
58
|
+
return DirDelta_WriteSig(path_iter, io.StringIO(""), None)
|
|
59
59
|
|
|
60
60
|
|
|
61
61
|
def DirFull_WriteSig(path_iter, sig_outfp):
|
|
@@ -65,26 +65,6 @@ def DirFull_WriteSig(path_iter, sig_outfp):
|
|
|
65
65
|
return DirDelta_WriteSig(path_iter, io.StringIO(""), sig_outfp)
|
|
66
66
|
|
|
67
67
|
|
|
68
|
-
def DirDelta(path_iter, dirsig_fileobj_list):
|
|
69
|
-
"""
|
|
70
|
-
Produce tarblock diff given dirsig_fileobj_list and pathiter
|
|
71
|
-
|
|
72
|
-
dirsig_fileobj_list should either be a tar fileobj or a list of
|
|
73
|
-
those, sorted so the most recent is last.
|
|
74
|
-
"""
|
|
75
|
-
global stats
|
|
76
|
-
stats = statistics.StatsDeltaProcess()
|
|
77
|
-
if isinstance(dirsig_fileobj_list, list):
|
|
78
|
-
sig_iter = combine_path_iters([sigtar2path_iter(x) for x in dirsig_fileobj_list])
|
|
79
|
-
else:
|
|
80
|
-
sig_iter = sigtar2path_iter(dirsig_fileobj_list)
|
|
81
|
-
delta_iter = get_delta_iter(path_iter, sig_iter)
|
|
82
|
-
if config.dry_run or (config.progress and not progress.tracker.has_collected_evidence()):
|
|
83
|
-
return DummyBlockIter(delta_iter)
|
|
84
|
-
else:
|
|
85
|
-
return DeltaTarBlockIter(delta_iter)
|
|
86
|
-
|
|
87
|
-
|
|
88
68
|
def delta_iter_error_handler(exc, new_path, sig_path, sig_tar=None): # pylint: disable=unused-argument
|
|
89
69
|
"""
|
|
90
70
|
Called by get_delta_iter, report error in getting delta
|
|
@@ -132,39 +132,47 @@ def get_passphrase(n, action, for_signing=False):
|
|
|
132
132
|
log.Notice(_("Reuse configured SIGN_PASSPHRASE as PASSPHRASE"))
|
|
133
133
|
return os.environ["SIGN_PASSPHRASE"]
|
|
134
134
|
|
|
135
|
-
#
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
if
|
|
143
|
-
|
|
144
|
-
for key in encrypt_keys:
|
|
145
|
-
if util.key_needs_passphrase(key):
|
|
146
|
-
log.Notice(f"Key {key} needs passphrase.")
|
|
147
|
-
need_passphrase = True
|
|
148
|
-
break
|
|
149
|
-
else:
|
|
150
|
-
log.Notice("No encryption keys need passphrase.")
|
|
151
|
-
else:
|
|
152
|
-
symmetric = True
|
|
153
|
-
need_passphrase = True
|
|
154
|
-
log.Notice("No encryption keys configured.")
|
|
155
|
-
|
|
156
|
-
skips = copy.copy(skips_sync_archive)
|
|
157
|
-
skips.remove("full")
|
|
158
|
-
if (action == "full" and asymmetric) or config.restart or action in skips:
|
|
159
|
-
log.Notice(f"Skipping passphrase request for action {action}")
|
|
135
|
+
# Next, verify we need to ask the user
|
|
136
|
+
|
|
137
|
+
# Assumptions:
|
|
138
|
+
# - encrypt-key has no passphrase
|
|
139
|
+
# - sign-key requires passphrase
|
|
140
|
+
# - gpg-agent supplies all, no user interaction
|
|
141
|
+
|
|
142
|
+
# no passphrase if --no-encryption or --use-agent
|
|
143
|
+
if not config.encryption or config.use_agent:
|
|
160
144
|
return ""
|
|
161
145
|
|
|
162
|
-
|
|
163
|
-
|
|
146
|
+
# these commands don't need a password
|
|
147
|
+
elif action in [
|
|
148
|
+
"collection-status",
|
|
149
|
+
"list-current-files",
|
|
150
|
+
"remove-all-but-n-full",
|
|
151
|
+
"remove-all-inc-of-but-n-full",
|
|
152
|
+
"remove-older-than",
|
|
153
|
+
]:
|
|
154
|
+
return ""
|
|
155
|
+
|
|
156
|
+
# for a full, inc, verify, we don't need a password if
|
|
157
|
+
# there is no sign_key and there are recipients
|
|
158
|
+
elif (
|
|
159
|
+
action in ("full", "inc", "verify")
|
|
160
|
+
and (config.gpg_profile.recipients or config.gpg_profile.hidden_recipients)
|
|
161
|
+
and (not config.gpg_profile.sign_key or (not config.restart and not for_signing))
|
|
162
|
+
):
|
|
163
|
+
return ""
|
|
164
|
+
|
|
165
|
+
elif (
|
|
166
|
+
(config.gpg_profile.recipients or config.gpg_profile.hidden_recipients)
|
|
167
|
+
and config.metadata_sync_mode == "partial"
|
|
168
|
+
and action in ["full"]
|
|
169
|
+
):
|
|
170
|
+
log.Info(_("Skipping passphrase input for full backup with encryption keys."))
|
|
164
171
|
return ""
|
|
165
172
|
|
|
173
|
+
# Finally, ask the user for the passphrase
|
|
166
174
|
else:
|
|
167
|
-
log.
|
|
175
|
+
log.Info(_("PASSPHRASE variable not set, asking user."))
|
|
168
176
|
use_cache = True
|
|
169
177
|
while True:
|
|
170
178
|
# ask the user to enter a new passphrase to avoid an infinite loop
|
|
@@ -758,7 +766,7 @@ def incremental_backup(sig_chain, col_stats=None):
|
|
|
758
766
|
if config.progress:
|
|
759
767
|
progress.tracker = progress.ProgressTracker()
|
|
760
768
|
# Fake a backup to compute total of moving bytes
|
|
761
|
-
tarblock_iter = diffdir.
|
|
769
|
+
tarblock_iter = diffdir.DirDelta_WriteSig(config.select, sig_chain.get_fileobjs(), None)
|
|
762
770
|
dummy_backup(tarblock_iter)
|
|
763
771
|
# Store computed stats to compute progress later
|
|
764
772
|
progress.tracker.set_evidence(diffdir.stats, False)
|
|
@@ -768,7 +776,7 @@ def incremental_backup(sig_chain, col_stats=None):
|
|
|
768
776
|
progress.progress_thread = progress.LogProgressThread()
|
|
769
777
|
|
|
770
778
|
if config.dry_run:
|
|
771
|
-
tarblock_iter = diffdir.
|
|
779
|
+
tarblock_iter = diffdir.DirDelta_WriteSig(config.select, sig_chain.get_fileobjs(), None)
|
|
772
780
|
bytes_written = dummy_backup(tarblock_iter)
|
|
773
781
|
else:
|
|
774
782
|
new_sig_outfp = get_sig_fileobj("new-sig")
|
|
@@ -40,7 +40,6 @@ MAX = 9
|
|
|
40
40
|
PREFIX = ""
|
|
41
41
|
|
|
42
42
|
_logger = None
|
|
43
|
-
_log_timestamp = False
|
|
44
43
|
|
|
45
44
|
|
|
46
45
|
def DupToLoggerLevel(verb):
|
|
@@ -321,7 +320,6 @@ def setup():
|
|
|
321
320
|
Initialize logging
|
|
322
321
|
"""
|
|
323
322
|
global _logger
|
|
324
|
-
global _log_timestamp
|
|
325
323
|
if _logger:
|
|
326
324
|
return
|
|
327
325
|
|
|
@@ -333,18 +331,12 @@ def setup():
|
|
|
333
331
|
|
|
334
332
|
# stdout and stderr are for different logging levels
|
|
335
333
|
outHandler = logging.StreamHandler(sys.stdout)
|
|
336
|
-
|
|
337
|
-
outHandler.setFormatter(DetailFormatter())
|
|
338
|
-
else:
|
|
339
|
-
outHandler.setFormatter(PrettyProgressFormatter())
|
|
334
|
+
outHandler.setFormatter(PrettyProgressFormatter())
|
|
340
335
|
outHandler.addFilter(OutFilter())
|
|
341
336
|
_logger.addHandler(outHandler)
|
|
342
337
|
|
|
343
338
|
errHandler = logging.StreamHandler(sys.stderr)
|
|
344
|
-
|
|
345
|
-
errHandler.setFormatter(DetailFormatter())
|
|
346
|
-
else:
|
|
347
|
-
errHandler.setFormatter(PrettyProgressFormatter())
|
|
339
|
+
errHandler.setFormatter(PrettyProgressFormatter())
|
|
348
340
|
errHandler.addFilter(ErrFilter())
|
|
349
341
|
_logger.addHandler(errHandler)
|
|
350
342
|
|
|
@@ -388,7 +380,7 @@ class DetailFormatter(logging.Formatter):
|
|
|
388
380
|
# standard 'levelname'. This is because the standard 'levelname' can
|
|
389
381
|
# be adjusted by any library anywhere in our stack without us knowing.
|
|
390
382
|
# But we control 'levelName'.
|
|
391
|
-
logging.Formatter.__init__(self, "%(asctime)s %(levelName)
|
|
383
|
+
logging.Formatter.__init__(self, "%(asctime)s %(levelName)-6s %(message)s")
|
|
392
384
|
|
|
393
385
|
def format(self, record):
|
|
394
386
|
s = logging.Formatter.format(self, record)
|
|
@@ -452,6 +444,28 @@ def add_file(filename):
|
|
|
452
444
|
_logger.addHandler(handler)
|
|
453
445
|
|
|
454
446
|
|
|
447
|
+
def add_timestamp():
|
|
448
|
+
"""
|
|
449
|
+
Add timestamp to logs written
|
|
450
|
+
"""
|
|
451
|
+
global _logger
|
|
452
|
+
|
|
453
|
+
# remove all handlers
|
|
454
|
+
for handler in _logger.handlers[:]:
|
|
455
|
+
_logger.removeHandler(handler)
|
|
456
|
+
|
|
457
|
+
# stdout and stderr are for different logging levels
|
|
458
|
+
outHandler = logging.StreamHandler(sys.stdout)
|
|
459
|
+
outHandler.setFormatter(DetailFormatter())
|
|
460
|
+
outHandler.addFilter(OutFilter())
|
|
461
|
+
_logger.addHandler(outHandler)
|
|
462
|
+
|
|
463
|
+
errHandler = logging.StreamHandler(sys.stderr)
|
|
464
|
+
errHandler.setFormatter(DetailFormatter())
|
|
465
|
+
errHandler.addFilter(ErrFilter())
|
|
466
|
+
_logger.addHandler(errHandler)
|
|
467
|
+
|
|
468
|
+
|
|
455
469
|
def setverbosity(verb):
|
|
456
470
|
"""
|
|
457
471
|
Set the verbosity level.
|
|
@@ -623,13 +623,8 @@ class Path(ROPath):
|
|
|
623
623
|
|
|
624
624
|
def deltree(self):
|
|
625
625
|
"""Remove self by recursively deleting files under it"""
|
|
626
|
-
from duplicity import selection # TODO: avoid circ. dep. issue
|
|
627
|
-
|
|
628
626
|
log.Debug(_("Deleting tree %s") % self.uc_name)
|
|
629
|
-
|
|
630
|
-
for path in selection.Select(self).set_iter():
|
|
631
|
-
itr(path.index, path)
|
|
632
|
-
itr.Finish()
|
|
627
|
+
shutil.rmtree(self.name)
|
|
633
628
|
self.setdata()
|
|
634
629
|
|
|
635
630
|
def get_parent_dir(self):
|
|
@@ -807,19 +802,3 @@ class DupPath(Path):
|
|
|
807
802
|
return gpg.GPGFile(True, self, gpg_profile)
|
|
808
803
|
else:
|
|
809
804
|
return self.open(mode)
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
class PathDeleter(ITRBranch):
|
|
813
|
-
"""Delete a directory. Called by Path.deltree"""
|
|
814
|
-
|
|
815
|
-
def start_process(self, index, path): # pylint: disable=unused-argument
|
|
816
|
-
self.path = path
|
|
817
|
-
|
|
818
|
-
def end_process(self):
|
|
819
|
-
self.path.delete()
|
|
820
|
-
|
|
821
|
-
def can_fast_process(self, index, path): # pylint: disable=unused-argument
|
|
822
|
-
return not path.isdir()
|
|
823
|
-
|
|
824
|
-
def fast_process(self, index, path): # pylint: disable=unused-argument
|
|
825
|
-
path.delete()
|
|
@@ -27,11 +27,13 @@ import atexit
|
|
|
27
27
|
import csv
|
|
28
28
|
import errno
|
|
29
29
|
import json
|
|
30
|
+
import locale
|
|
30
31
|
import multiprocessing
|
|
31
32
|
import os
|
|
32
33
|
import socket
|
|
33
34
|
import sys
|
|
34
35
|
import traceback
|
|
36
|
+
from contextlib import contextmanager
|
|
35
37
|
from io import StringIO
|
|
36
38
|
|
|
37
39
|
import fasteners
|
|
@@ -203,30 +205,6 @@ def release_lockfile():
|
|
|
203
205
|
pass
|
|
204
206
|
|
|
205
207
|
|
|
206
|
-
def key_needs_passphrase(key):
|
|
207
|
-
"""
|
|
208
|
-
Check if a key needs a passphrase.
|
|
209
|
-
"""
|
|
210
|
-
try:
|
|
211
|
-
child = pexpect.spawn("gpg", f"--pinentry-mode=loopback --dry-run --passwd {key}".split())
|
|
212
|
-
except Exception:
|
|
213
|
-
log.FatalError(f"Exception spawning gpg while checking if passphrase needed for key: {key}")
|
|
214
|
-
|
|
215
|
-
try:
|
|
216
|
-
got = child.expect(["passphrase.*:", pexpect.EOF])
|
|
217
|
-
except Exception:
|
|
218
|
-
log.FatalError(f"Exception while checking if passphrase needed for key: {key}: {str(child)}")
|
|
219
|
-
|
|
220
|
-
if got == 0:
|
|
221
|
-
log.Debug(f"Key {key} needs passphrase")
|
|
222
|
-
child.close()
|
|
223
|
-
return True
|
|
224
|
-
elif got == 1:
|
|
225
|
-
log.Debug(f"Key {key} does not need passphrase")
|
|
226
|
-
return False
|
|
227
|
-
return None
|
|
228
|
-
|
|
229
|
-
|
|
230
208
|
def copyfileobj(infp, outfp, byte_count=-1):
|
|
231
209
|
"""
|
|
232
210
|
Copy byte_count bytes from infp to outfp, or all if byte_count < 0
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
.TH DUPLICITY 1 "
|
|
1
|
+
.TH DUPLICITY 1 "January 14, 2026" "Version 3.0.8.dev3" "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
|
|
@@ -497,23 +497,6 @@ See the
|
|
|
497
497
|
.B FILE SELECTION
|
|
498
498
|
section for more information.
|
|
499
499
|
|
|
500
|
-
.TP
|
|
501
|
-
.BI "--files-from " filename
|
|
502
|
-
Read a list of files to backup from filename rather than searching the entire
|
|
503
|
-
backup source directory. Operation is otherwise normal, just on the specified
|
|
504
|
-
subset of the backup source directory.
|
|
505
|
-
|
|
506
|
-
Files must be specified one per line and relative to the backup source
|
|
507
|
-
directory. Any absolute paths will raise an error. All characters per line are
|
|
508
|
-
significant and treated as part of the path, including leading and trailing
|
|
509
|
-
whitespace. Lines are separated by newlines or nulls, depending on whether the
|
|
510
|
-
.B "--null-separator"
|
|
511
|
-
switch was given.
|
|
512
|
-
|
|
513
|
-
It is not necessary to include the parent directory of listed files, their
|
|
514
|
-
inclusion is implied. However, the content of any explicitly listed directories
|
|
515
|
-
is not implied. All required files must be listed when this option is used.
|
|
516
|
-
|
|
517
500
|
.TP
|
|
518
501
|
.BI "--file-prefix " prefix
|
|
519
502
|
.PD 0
|
|
@@ -535,12 +518,21 @@ See also
|
|
|
535
518
|
.B "A NOTE ON FILENAME PREFIXES"
|
|
536
519
|
|
|
537
520
|
.TP
|
|
538
|
-
.BI "--
|
|
539
|
-
|
|
540
|
-
.
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
521
|
+
.BI "--files-from " filename
|
|
522
|
+
Read a list of files to backup from filename rather than searching the entire
|
|
523
|
+
backup source directory. Operation is otherwise normal, just on the specified
|
|
524
|
+
subset of the backup source directory.
|
|
525
|
+
|
|
526
|
+
Files must be specified one per line and relative to the backup source
|
|
527
|
+
directory. Any absolute paths will raise an error. All characters per line are
|
|
528
|
+
significant and treated as part of the path, including leading and trailing
|
|
529
|
+
whitespace. Lines are separated by newlines or nulls, depending on whether the
|
|
530
|
+
.B "--null-separator"
|
|
531
|
+
switch was given.
|
|
532
|
+
|
|
533
|
+
It is not necessary to include the parent directory of listed files, their
|
|
534
|
+
inclusion is implied. However, the content of any explicitly listed directories
|
|
535
|
+
is not implied. All required files must be listed when this option is used.
|
|
544
536
|
|
|
545
537
|
.TP
|
|
546
538
|
.BI --filter-globbing
|
|
@@ -572,15 +564,6 @@ See the
|
|
|
572
564
|
.B FILE SELECTION
|
|
573
565
|
section for more information.
|
|
574
566
|
|
|
575
|
-
.TP
|
|
576
|
-
.BI "--full-if-older-than " time
|
|
577
|
-
Perform a full backup if an incremental backup is requested, but the
|
|
578
|
-
latest full backup in the collection is older than the given
|
|
579
|
-
.IR time .
|
|
580
|
-
See the
|
|
581
|
-
.B TIME FORMATS
|
|
582
|
-
section for more information.
|
|
583
|
-
|
|
584
567
|
.TP
|
|
585
568
|
.BI --force
|
|
586
569
|
Proceed even if data loss might result. Duplicity will let the user
|
|
@@ -596,6 +579,15 @@ out.
|
|
|
596
579
|
.BI --ftp-regular
|
|
597
580
|
Use regular (PORT) data connections.
|
|
598
581
|
|
|
582
|
+
.TP
|
|
583
|
+
.BI "--full-if-older-than " time
|
|
584
|
+
Perform a full backup if an incremental backup is requested, but the
|
|
585
|
+
latest full backup in the collection is older than the given
|
|
586
|
+
.IR time .
|
|
587
|
+
See the
|
|
588
|
+
.B TIME FORMATS
|
|
589
|
+
section for more information.
|
|
590
|
+
|
|
599
591
|
.TP
|
|
600
592
|
.BI --gio
|
|
601
593
|
Use the GIO backend and interpret any URLs as GIO would.
|
|
@@ -862,6 +854,14 @@ for Par2 recovery files (default 10%).
|
|
|
862
854
|
.BI "--par2-volumes " number
|
|
863
855
|
Number of Par2 volumes to create (default 1).
|
|
864
856
|
|
|
857
|
+
.TP
|
|
858
|
+
.BI "--path-to-restore " path
|
|
859
|
+
This option may be given in restore mode, causing only
|
|
860
|
+
.I path
|
|
861
|
+
to be restored instead of the entire contents of the backup archive.
|
|
862
|
+
.I path
|
|
863
|
+
should be given relative to the root of the directory backed up.
|
|
864
|
+
|
|
865
865
|
.TP
|
|
866
866
|
.BI --progress
|
|
867
867
|
When selected, duplicity will output the current upload progress and estimated
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "duplicity"
|
|
3
|
-
version = "3.0.
|
|
3
|
+
version = "3.0.8.dev3"
|
|
4
4
|
dynamic = ["dependencies"]
|
|
5
5
|
description = "Encrypted backup using rsync algorithm"
|
|
6
6
|
authors = [
|
|
@@ -126,9 +126,6 @@ addopts = [
|
|
|
126
126
|
"-p no:randomly",
|
|
127
127
|
"-p no:xdist",
|
|
128
128
|
]
|
|
129
|
-
markers = [
|
|
130
|
-
"slow: test runs >= 10 secs",
|
|
131
|
-
]
|
|
132
129
|
testpaths = [
|
|
133
130
|
"testing/test_code.py",
|
|
134
131
|
"testing/unit",
|
|
@@ -42,7 +42,7 @@ elif not ((3, 9) <= sys.version_info[:2] <= (3, 14)):
|
|
|
42
42
|
print("Sorry, duplicity requires version 3.9 thru 3.14 of Python.", file=sys.stderr)
|
|
43
43
|
sys.exit(1)
|
|
44
44
|
|
|
45
|
-
Version: str = "3.0.
|
|
45
|
+
Version: str = "3.0.8.dev3"
|
|
46
46
|
|
|
47
47
|
# READTHEDOCS uses setup.py sdist but can't handle extensions
|
|
48
48
|
ext_modules = list()
|
|
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
|
|
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
|