duplicity 2.2.2__tar.gz → 2.2.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.
- {duplicity-2.2.2 → duplicity-2.2.3}/CHANGELOG.md +83 -0
- {duplicity-2.2.2/duplicity.egg-info → duplicity-2.2.3}/PKG-INFO +1 -3
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/__init__.py +2 -2
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backend.py +38 -1
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/README +10 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/_testbackend.py +74 -6
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/b2backend.py +5 -3
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/cli_data.py +1 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/cli_main.py +13 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/dup_collections.py +1 -8
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/dup_main.py +9 -26
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/log.py +1 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/path.py +4 -4
- {duplicity-2.2.2 → duplicity-2.2.3/duplicity.egg-info}/PKG-INFO +1 -3
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity.egg-info/SOURCES.txt +5 -1
- {duplicity-2.2.2 → duplicity-2.2.3}/man/duplicity.1 +47 -200
- {duplicity-2.2.2 → duplicity-2.2.3}/po/af_ZA/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/af_ZA.po +168 -165
- {duplicity-2.2.2 → duplicity-2.2.3}/po/ar_SA/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/ar_SA.po +168 -169
- {duplicity-2.2.2 → duplicity-2.2.3}/po/ca_ES/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/ca_ES.po +168 -165
- {duplicity-2.2.2 → duplicity-2.2.3}/po/cs_CZ/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/cs_CZ.po +168 -167
- {duplicity-2.2.2 → duplicity-2.2.3}/po/da_DK/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/da_DK.po +168 -165
- {duplicity-2.2.2 → duplicity-2.2.3}/po/de_AT/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/de_AT.po +168 -165
- {duplicity-2.2.2 → duplicity-2.2.3}/po/de_DE/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/de_DE.po +168 -165
- {duplicity-2.2.2 → duplicity-2.2.3}/po/duplicity.pot +167 -164
- {duplicity-2.2.2 → duplicity-2.2.3}/po/el_GR/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/el_GR.po +168 -165
- {duplicity-2.2.2 → duplicity-2.2.3}/po/en_AU/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/en_AU.po +168 -165
- {duplicity-2.2.2 → duplicity-2.2.3}/po/en_GB/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/en_GB.po +168 -165
- {duplicity-2.2.2 → duplicity-2.2.3}/po/en_PR/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/en_PR.po +168 -165
- {duplicity-2.2.2 → duplicity-2.2.3}/po/en_US/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/en_US.po +168 -165
- {duplicity-2.2.2 → duplicity-2.2.3}/po/es_EM/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/es_EM.po +168 -165
- {duplicity-2.2.2 → duplicity-2.2.3}/po/es_ES/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/es_ES.po +168 -165
- {duplicity-2.2.2 → duplicity-2.2.3}/po/es_MX/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/es_MX.po +168 -165
- {duplicity-2.2.2 → duplicity-2.2.3}/po/es_PR/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/es_PR.po +168 -165
- {duplicity-2.2.2 → duplicity-2.2.3}/po/es_US/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/es_US.po +168 -165
- {duplicity-2.2.2 → duplicity-2.2.3}/po/fi_FI/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/fi_FI.po +168 -165
- {duplicity-2.2.2 → duplicity-2.2.3}/po/fr_FR/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/fr_FR.po +168 -165
- {duplicity-2.2.2 → duplicity-2.2.3}/po/he_IL/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/he_IL.po +168 -167
- {duplicity-2.2.2 → duplicity-2.2.3}/po/hu_HU/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/hu_HU.po +168 -165
- {duplicity-2.2.2 → duplicity-2.2.3}/po/it_IT/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/it_IT.po +168 -165
- {duplicity-2.2.2 → duplicity-2.2.3}/po/ja_JP/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/ja_JP.po +168 -164
- {duplicity-2.2.2 → duplicity-2.2.3}/po/ko_KR/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/ko_KR.po +168 -164
- {duplicity-2.2.2 → duplicity-2.2.3}/po/nl_BE/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/nl_BE.po +168 -165
- {duplicity-2.2.2 → duplicity-2.2.3}/po/nl_NL/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/nl_NL.po +168 -165
- {duplicity-2.2.2 → duplicity-2.2.3}/po/nl_SR/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/nl_SR.po +168 -165
- {duplicity-2.2.2 → duplicity-2.2.3}/po/no_NO/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/no_NO.po +168 -165
- {duplicity-2.2.2 → duplicity-2.2.3}/po/pl_PL/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/pl_PL.po +168 -167
- {duplicity-2.2.2 → duplicity-2.2.3}/po/pt_BR/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/pt_BR.po +168 -165
- {duplicity-2.2.2 → duplicity-2.2.3}/po/pt_PT/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/pt_PT.po +168 -165
- {duplicity-2.2.2 → duplicity-2.2.3}/po/ro_RO/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/ro_RO.po +168 -166
- {duplicity-2.2.2 → duplicity-2.2.3}/po/ru_BY/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/ru_BY.po +168 -167
- {duplicity-2.2.2 → duplicity-2.2.3}/po/ru_MD/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/ru_MD.po +168 -167
- {duplicity-2.2.2 → duplicity-2.2.3}/po/ru_RU/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/ru_RU.po +168 -167
- {duplicity-2.2.2 → duplicity-2.2.3}/po/ru_UA/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/ru_UA.po +168 -167
- {duplicity-2.2.2 → duplicity-2.2.3}/po/sr_SP/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/sr_SP.po +168 -166
- {duplicity-2.2.2 → duplicity-2.2.3}/po/sv_SE/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/sv_SE.po +168 -165
- {duplicity-2.2.2 → duplicity-2.2.3}/po/tr_TR/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/tr_TR.po +168 -165
- {duplicity-2.2.2 → duplicity-2.2.3}/po/uk_UA/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/uk_UA.po +168 -167
- {duplicity-2.2.2 → duplicity-2.2.3}/po/vi_VN/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/vi_VN.po +168 -164
- {duplicity-2.2.2 → duplicity-2.2.3}/po/zh_CN/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/zh_CN.po +168 -164
- {duplicity-2.2.2 → duplicity-2.2.3}/po/zh_HK/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/zh_HK.po +168 -164
- {duplicity-2.2.2 → duplicity-2.2.3}/po/zh_MO/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/zh_MO.po +168 -164
- {duplicity-2.2.2 → duplicity-2.2.3}/po/zh_SG/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/zh_SG.po +168 -164
- {duplicity-2.2.2 → duplicity-2.2.3}/po/zh_TW/duplicity.mo +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/zh_TW.po +168 -164
- {duplicity-2.2.2 → duplicity-2.2.3}/pyproject.toml +18 -10
- {duplicity-2.2.2 → duplicity-2.2.3}/requirements.txt +2 -1
- {duplicity-2.2.2 → duplicity-2.2.3}/setup.py +39 -70
- {duplicity-2.2.2 → duplicity-2.2.3}/snap/snapcraft.yaml +1 -1
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/functional/__init__.py +19 -16
- duplicity-2.2.3/testing/functional/test_badupload.py +110 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/functional/test_log.py +6 -4
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/test_code.py +2 -10
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/unit/test_backend.py +87 -2
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/unit/test_patchdir.py +1 -1
- {duplicity-2.2.2 → duplicity-2.2.3}/tox.ini +0 -2
- duplicity-2.2.2/crowdin.yml +0 -6
- duplicity-2.2.2/testing/functional/test_badupload.py +0 -53
- {duplicity-2.2.2 → duplicity-2.2.3}/AUTHORS.md +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/COPYING +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/README-LOG.md +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/README-REPO.md +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/README-SNAP.md +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/README-TESTING.md +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/README.md +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/__main__.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/_librsyncmodule.c +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/argparse311.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/asyncscheduler.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/__init__.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/_cf_cloudfiles.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/_cf_pyrax.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/adbackend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/azurebackend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/boxbackend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/cfbackend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/dpbxbackend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/gdocsbackend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/gdrivebackend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/giobackend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/hsibackend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/hubicbackend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/idrivedbackend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/imapbackend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/jottacloudbackend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/lftpbackend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/localbackend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/mediafirebackend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/megabackend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/megav2backend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/megav3backend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/multibackend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/ncftpbackend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/onedrivebackend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/par2backend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/pcabackend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/pydrivebackend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/pyrax_identity/__init__.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/pyrax_identity/hubic.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/rclonebackend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/rsyncbackend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/s3_boto3_backend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/slatebackend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/ssh_paramiko_backend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/ssh_pexpect_backend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/swiftbackend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/sxbackend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/tahoebackend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/webdavbackend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/backends/xorrisobackend.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/cached_ops.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/cli_util.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/config.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/diffdir.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/dup_tarfile.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/dup_temp.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/dup_threading.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/dup_time.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/errors.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/file_naming.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/filechunkio.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/globmatch.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/gpg.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/gpginterface.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/lazy.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/librsync.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/manifest.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/patchdir.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/progress.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/robust.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/selection.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/statistics.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/tempdir.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity/util.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity.egg-info/dependency_links.txt +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity.egg-info/entry_points.txt +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/duplicity.egg-info/top_level.txt +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/LINGUAS +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/Makevars +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/POTFILES.in +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/POTFILES.skip +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/po/update-pot +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/setup.cfg +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/snap/local/debug.sh +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/snap/local/launcher.sh +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/__init__.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/conftest.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/functional/test_cleanup.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/functional/test_final.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/functional/test_restart.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/functional/test_selection.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/functional/test_verify.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/gnupg/README +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/gnupg/gpg-agent.conf +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/gnupg/gpg.conf +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/gnupg/pubring.gpg +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/gnupg/secring.gpg +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/gnupg/trustdb.gpg +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/overrides/__init__.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/overrides/bin/hsi +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/overrides/bin/lftp +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/overrides/bin/ncftpget +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/overrides/bin/ncftpls +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/overrides/bin/ncftpput +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/overrides/bin/tahoe +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/run-tests +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/testfiles.tar.gz +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/unit/__init__.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/unit/test_backend_instance.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/unit/test_cli_main.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/unit/test_collections.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/unit/test_diffdir.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/unit/test_dup_temp.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/unit/test_dup_time.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/unit/test_file_naming.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/unit/test_globmatch.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/unit/test_gpg.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/unit/test_gpginterface.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/unit/test_lazy.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/unit/test_manifest.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/unit/test_path.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/unit/test_selection.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/unit/test_statistics.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/unit/test_tarfile.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/unit/test_tempdir.py +0 -0
- {duplicity-2.2.2 → duplicity-2.2.3}/testing/unit/test_util.py +0 -0
|
@@ -1,6 +1,89 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
## rel.2.2.3 (2024-03-20)
|
|
5
|
+
|
|
6
|
+
### New
|
|
7
|
+
|
|
8
|
+
* Add test case issue725.sh. [Kenneth Loafman]
|
|
9
|
+
|
|
10
|
+
### Changes
|
|
11
|
+
|
|
12
|
+
* Remove setuptools\_scm and usages. [Kenneth Loafman]
|
|
13
|
+
|
|
14
|
+
* Run po/update-pot. [Kenneth Loafman]
|
|
15
|
+
|
|
16
|
+
* !Minor changes to pip build and test. [Kenneth Loafman]
|
|
17
|
+
|
|
18
|
+
* Pip install duplicity fails with "AssertionError: es\_PR" (or other
|
|
19
|
+
language) [Kenneth Loafman]
|
|
20
|
+
|
|
21
|
+
* Launchpad PPAs not built correctly for 2.2.[0,1], PyPI pips for
|
|
22
|
+
2.2.[0,1,2] [Kenneth Loafman]
|
|
23
|
+
|
|
24
|
+
- tested on:
|
|
25
|
+
- Ubuntu 20.04, 22.04, 24.04
|
|
26
|
+
- Python 3.8, 3.9, 3.10, 3.11, 3.12
|
|
27
|
+
- added tools/testpip
|
|
28
|
+
- added wheel builds to .gitlab-ci.yml
|
|
29
|
+
- build wheels for manylinux
|
|
30
|
+
- added tools/testpip
|
|
31
|
+
- remove disutils includes in setup.py
|
|
32
|
+
- remove old test command in setup.py
|
|
33
|
+
- moved use of setuptools-scm to scmversion command
|
|
34
|
+
- add tools to build pyenv & pythons & more.
|
|
35
|
+
- installpyenv - install pyenv and environ
|
|
36
|
+
- installpythons - install pythons and environ
|
|
37
|
+
- pushpip - push pip sdist and wheels
|
|
38
|
+
- tools/list_python_files - removed, unused
|
|
39
|
+
|
|
40
|
+
Fixes #797
|
|
41
|
+
|
|
42
|
+
* Change ngettext to \_ and fix wording. [Kenneth Loafman]
|
|
43
|
+
|
|
44
|
+
* Move validate to backend. [Thomas Laubrock]
|
|
45
|
+
|
|
46
|
+
* Run only one pipeline on merge request. [Kenneth Loafman]
|
|
47
|
+
|
|
48
|
+
* Small fixes to the test system. [Kenneth Loafman]
|
|
49
|
+
|
|
50
|
+
- run test cases with the same Python as pytest
|
|
51
|
+
- fix some comments that pointed to wrong config file
|
|
52
|
+
- code tests now run without RUN_CODE_TESTS=1
|
|
53
|
+
|
|
54
|
+
* Change from --capture=no to --capture=fd. [Kenneth Loafman]
|
|
55
|
+
|
|
56
|
+
- Should only print on test failure.
|
|
57
|
+
|
|
58
|
+
* Warn on symmetric encrypt with --use-agent. [Kenneth Loafman]
|
|
59
|
+
|
|
60
|
+
Fixes #799
|
|
61
|
+
|
|
62
|
+
* Warn on symmetric encrypt with --use-agent. [Kenneth Loafman]
|
|
63
|
+
|
|
64
|
+
Fixes #799
|
|
65
|
+
|
|
66
|
+
* Remove last of bin/duplicity. [Kenneth Loafman]
|
|
67
|
+
|
|
68
|
+
### Fix
|
|
69
|
+
|
|
70
|
+
* Add fail open condition, for backends with no support. [Thomas Laubrock]
|
|
71
|
+
|
|
72
|
+
* Update homepage. [sblondon]
|
|
73
|
+
|
|
74
|
+
* Change path logging from Info to Debug. [Kenneth Loafman]
|
|
75
|
+
|
|
76
|
+
Fixes #804
|
|
77
|
+
|
|
78
|
+
* Whoops! Remove parens in fix of #803. [Kenneth Loafman]
|
|
79
|
+
|
|
80
|
+
* Exception using --exclude-older-than. [Kenneth Loafman]
|
|
81
|
+
|
|
82
|
+
fixes #803
|
|
83
|
+
|
|
84
|
+
* Manpage remove superfluous indentions, some formatting fixes. [ede]
|
|
85
|
+
|
|
86
|
+
|
|
4
87
|
## rel.2.2.2 (2024-02-03)
|
|
5
88
|
|
|
6
89
|
### Changes
|
|
@@ -24,13 +24,14 @@ Provides a common interface to all backends and certain sevices
|
|
|
24
24
|
intended to be used by the backends themselves.
|
|
25
25
|
"""
|
|
26
26
|
|
|
27
|
+
from datetime import datetime
|
|
27
28
|
import errno
|
|
28
29
|
import getpass
|
|
29
30
|
import os
|
|
30
31
|
import re
|
|
31
|
-
import socket
|
|
32
32
|
import sys
|
|
33
33
|
import time
|
|
34
|
+
from typing import Tuple
|
|
34
35
|
import urllib.error
|
|
35
36
|
import urllib.parse
|
|
36
37
|
import urllib.request
|
|
@@ -604,6 +605,42 @@ class BackendWrapper(object):
|
|
|
604
605
|
else:
|
|
605
606
|
raise NotImplementedError()
|
|
606
607
|
|
|
608
|
+
def validate(self, remote_filename, expected_size, source_path=None) -> Tuple[bool, str]:
|
|
609
|
+
"""
|
|
610
|
+
validates a file transferred to the backend by comparing the size.
|
|
611
|
+
source_path is optional to allow the backend do further validation, e.g. by
|
|
612
|
+
calulating a hash or so.
|
|
613
|
+
Returns: return a tuple of (result, reason)
|
|
614
|
+
- result: bool: True if file validation successful otherwise False
|
|
615
|
+
- reason: str: If False a reason why validation failed; True: emtpy str or description
|
|
616
|
+
"""
|
|
617
|
+
msg = "Validation fail for unknow reaons."
|
|
618
|
+
if hasattr(self.backend, "_validate"):
|
|
619
|
+
return self.backend._validate(remote_filename, expected_size, source_path=source_path)
|
|
620
|
+
else:
|
|
621
|
+
# as some backends take some time refresh file attributes after upload
|
|
622
|
+
# retry config.num_retries times.
|
|
623
|
+
for attempt in range(0, config.num_retries + 1):
|
|
624
|
+
info = self.query_info([remote_filename])[remote_filename]
|
|
625
|
+
size = info["size"]
|
|
626
|
+
if size is None:
|
|
627
|
+
log.Warn(
|
|
628
|
+
"File size can't be validated, because of missing capabilities of the backend. "
|
|
629
|
+
"Please verify the backup separately."
|
|
630
|
+
)
|
|
631
|
+
return (True, "Backend has no capabilities to check filesize, skip validation.")
|
|
632
|
+
if size == expected_size:
|
|
633
|
+
return (True, "Validation OK, file size matches.")
|
|
634
|
+
msg = _("%s Remote filesize %d for %s does not match local size %d") % (
|
|
635
|
+
datetime.now(),
|
|
636
|
+
size,
|
|
637
|
+
util.escape(remote_filename),
|
|
638
|
+
expected_size,
|
|
639
|
+
)
|
|
640
|
+
log.Notice(f"{msg}, retrying.")
|
|
641
|
+
time.sleep(2**attempt)
|
|
642
|
+
return (False, f"{msg}.")
|
|
643
|
+
|
|
607
644
|
def pre_process_download(self, remote_filename):
|
|
608
645
|
"""
|
|
609
646
|
Manages remote access before downloading files
|
|
@@ -19,6 +19,16 @@ _delete
|
|
|
19
19
|
|
|
20
20
|
There are other methods you may optionally implement:
|
|
21
21
|
|
|
22
|
+
_validate
|
|
23
|
+
- minimum, compare the file size with the given value
|
|
24
|
+
- has two pos args: remote_filename and size in bytes
|
|
25
|
+
- accept **kwargs, which will held following arguments:
|
|
26
|
+
- source_path as path.Path object pointing to the source file in the local fs.
|
|
27
|
+
- (future arguments, that should be ignored.)
|
|
28
|
+
- must handle retries on its own if e.g. size need some time to be refreshed after upload.
|
|
29
|
+
- return a tuple of (bool, str)
|
|
30
|
+
- bool: True if file validation successful otherwise False
|
|
31
|
+
- str: If False a description why validation failed; True: emtpy str or description
|
|
22
32
|
_delete_list
|
|
23
33
|
- Delete list of files
|
|
24
34
|
- This is used in preference of _delete if defined
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
21
21
|
|
|
22
22
|
import glob
|
|
23
|
+
import hashlib
|
|
23
24
|
import inspect
|
|
24
25
|
import json
|
|
25
26
|
import logging
|
|
@@ -39,12 +40,14 @@ ERROR_ON = {}
|
|
|
39
40
|
|
|
40
41
|
|
|
41
42
|
class BackendErrors:
|
|
42
|
-
FAIL_WITH_EXCEPTION
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
LAST_BYTE_MISSING
|
|
47
|
-
|
|
43
|
+
# FAIL_WITH_EXCEPTION: set to substring mached by the filename.
|
|
44
|
+
FAIL_WITH_EXCEPTION = "DUP_FAIL_WITH_EXCEPTION"
|
|
45
|
+
# WAIT_FOR_OTHER_VOLUME: set to json string: ["file_to_delay", "file_wait_for"] (substing match)
|
|
46
|
+
WAIT_FOR_OTHER_VOLUME = "DUP_FAIL_WAIT_FOR_VOLUME"
|
|
47
|
+
# LAST_BYTE_MISSING: set to substring mached by the filename.
|
|
48
|
+
LAST_BYTE_MISSING = "DUP_FAIL_LAST_BYTE_MISSING"
|
|
49
|
+
# SKIP_PUT_SILENT: Don't put file if name contains string
|
|
50
|
+
SKIP_PUT_SILENT = "DUP_FAIL_SKIP_PUT_SILENT"
|
|
48
51
|
|
|
49
52
|
|
|
50
53
|
class _TestBackend(duplicity.backend.Backend):
|
|
@@ -145,6 +148,43 @@ class _TestBackend(duplicity.backend.Backend):
|
|
|
145
148
|
def _list(self):
|
|
146
149
|
return self.remote_pathdir.listdir()
|
|
147
150
|
|
|
151
|
+
def _validate(self, remote_filename, size, source_path=None, **kwargs):
|
|
152
|
+
# poc to show that validate can do additional things.
|
|
153
|
+
|
|
154
|
+
self._remove_last_byte(remote_filename)
|
|
155
|
+
|
|
156
|
+
results_str = []
|
|
157
|
+
results_bool = []
|
|
158
|
+
try:
|
|
159
|
+
if source_path:
|
|
160
|
+
target_path = self.remote_pathdir.append(remote_filename)
|
|
161
|
+
target_hash = self.__hash_fileobj(target_path.open())
|
|
162
|
+
source_hash = self.__hash_fileobj(source_path.open())
|
|
163
|
+
if target_hash == source_hash:
|
|
164
|
+
results_str.append(f"file hash {target_hash} matches")
|
|
165
|
+
results_bool.append(True)
|
|
166
|
+
else:
|
|
167
|
+
results_str.append(f"expected hash {source_hash} doesn't match file hash {target_hash}")
|
|
168
|
+
results_bool.append(False)
|
|
169
|
+
if size == self._query(remote_filename)["size"]:
|
|
170
|
+
results_str.append(f"file size {size}")
|
|
171
|
+
results_bool.append(True)
|
|
172
|
+
else:
|
|
173
|
+
results_str.append(
|
|
174
|
+
f'expected size {size} and file size {self._query(remote_filename)["size"]} don\'t match'
|
|
175
|
+
)
|
|
176
|
+
results_bool.append(False)
|
|
177
|
+
except FileNotFoundError as e:
|
|
178
|
+
results_bool.append(False)
|
|
179
|
+
results_str.append(f"FileNotFoundError: {e}")
|
|
180
|
+
except Exception as e:
|
|
181
|
+
log.FatalError(
|
|
182
|
+
_("Unexpected exception while validate %s.") % os.fsdecode(remote_filename),
|
|
183
|
+
log.ErrorCode.backend_verification_failed,
|
|
184
|
+
extra=f"Exception: {e}",
|
|
185
|
+
)
|
|
186
|
+
return (all(results_bool), ", ".join(results_str))
|
|
187
|
+
|
|
148
188
|
def _delete(self, filename):
|
|
149
189
|
self._fail_with_exception(filename)
|
|
150
190
|
self.remote_pathdir.append(filename).delete()
|
|
@@ -161,5 +201,33 @@ class _TestBackend(duplicity.backend.Backend):
|
|
|
161
201
|
size = target_file.getsize() if target_file.exists() else -1
|
|
162
202
|
return {"size": size}
|
|
163
203
|
|
|
204
|
+
def _query_list(self, filename_list):
|
|
205
|
+
return {x: self._query(x) for x in filename_list}
|
|
206
|
+
|
|
207
|
+
def _retry_cleanup(self):
|
|
208
|
+
pass
|
|
209
|
+
|
|
210
|
+
def _close(self):
|
|
211
|
+
pass
|
|
212
|
+
|
|
213
|
+
def _error_code(self, operation, e): # pylint: disable=unused-argument
|
|
214
|
+
return log.ErrorCode.backend_error
|
|
215
|
+
|
|
216
|
+
def __hash_file(self, filename):
|
|
217
|
+
self.__hash_fileobj(open(filename, "rb"))
|
|
218
|
+
|
|
219
|
+
def __hash_fileobj(self, fileobj):
|
|
220
|
+
h = hashlib.sha1()
|
|
221
|
+
|
|
222
|
+
# loop till the end of the file
|
|
223
|
+
chunk = 0
|
|
224
|
+
while chunk != b"":
|
|
225
|
+
# read only 1024 bytes at a time
|
|
226
|
+
chunk = fileobj.read(1024)
|
|
227
|
+
h.update(chunk)
|
|
228
|
+
fileobj.close()
|
|
229
|
+
# return the hex representation of digest
|
|
230
|
+
return h.hexdigest()
|
|
231
|
+
|
|
164
232
|
|
|
165
233
|
duplicity.backend.register_backend("fortestsonly", _TestBackend)
|
|
@@ -217,9 +217,11 @@ class B2Backend(duplicity.backend.Backend):
|
|
|
217
217
|
log.Log(f"Query: {self.path}{os.fsdecode(filename)}", log.INFO)
|
|
218
218
|
file_version_info = self.file_info(quote_plus(self.path + os.fsdecode(filename), "/"))
|
|
219
219
|
return {
|
|
220
|
-
"size":
|
|
221
|
-
|
|
222
|
-
|
|
220
|
+
"size": (
|
|
221
|
+
int(file_version_info.size)
|
|
222
|
+
if file_version_info is not None and file_version_info.size is not None
|
|
223
|
+
else -1
|
|
224
|
+
)
|
|
223
225
|
}
|
|
224
226
|
|
|
225
227
|
def file_info(self, filename):
|
|
@@ -298,6 +298,19 @@ def process_command_line(cmdline_list):
|
|
|
298
298
|
gpg_version = ".".join(map(str, config.gpg_profile.gpg_version))
|
|
299
299
|
log.Info(_(f"GPG binary is {config.gpg_binary}, version {gpg_version}"))
|
|
300
300
|
|
|
301
|
+
# --use-agent is not valid for symmetric encryption. Turn it off and notify user.
|
|
302
|
+
if (
|
|
303
|
+
config.use_agent
|
|
304
|
+
and config.gpg_profile.sign_key is None
|
|
305
|
+
and len(config.gpg_profile.recipients) == 0
|
|
306
|
+
and len(config.gpg_profile.hidden_recipients) == 0
|
|
307
|
+
):
|
|
308
|
+
config.use_agent = False
|
|
309
|
+
log.Warn(
|
|
310
|
+
"Option --gpg-agent is unsafe with symmetric encryption. Ignoring --gpg-agent.\n"
|
|
311
|
+
"Refer to https://gitlab.com/duplicity/duplicity/-/issues/799 for more information."
|
|
312
|
+
)
|
|
313
|
+
|
|
301
314
|
# shorten incremental to inc, replace backup with inc
|
|
302
315
|
if config.action in ("incremental", "backup"):
|
|
303
316
|
if config.action == "backup":
|
|
@@ -767,14 +767,7 @@ class CollectionsStatus(object):
|
|
|
767
767
|
|
|
768
768
|
# get local filename list
|
|
769
769
|
local_filename_list = self.archive_dir_path.listdir()
|
|
770
|
-
log.Debug(
|
|
771
|
-
ngettext(
|
|
772
|
-
"%d file exists in cache",
|
|
773
|
-
"%d files exist in cache",
|
|
774
|
-
len(local_filename_list),
|
|
775
|
-
)
|
|
776
|
-
% len(local_filename_list)
|
|
777
|
-
)
|
|
770
|
+
log.Debug(_("%d file(s) exist in cache") % len(local_filename_list))
|
|
778
771
|
|
|
779
772
|
# check for partial backups
|
|
780
773
|
partials = []
|
|
@@ -314,31 +314,6 @@ def write_multivol(backup_type, tarblock_iter, man_outfp, sig_outfp, backend):
|
|
|
314
314
|
end_block -= 1
|
|
315
315
|
return start_index, start_block, end_index, end_block
|
|
316
316
|
|
|
317
|
-
def validate_block(orig_size, dest_filename):
|
|
318
|
-
info = backend.query_info([dest_filename])[dest_filename]
|
|
319
|
-
size = info["size"]
|
|
320
|
-
if size is None:
|
|
321
|
-
return # error querying file
|
|
322
|
-
for attempt in range(1, config.num_retries + 1):
|
|
323
|
-
info = backend.query_info([dest_filename])[dest_filename]
|
|
324
|
-
size = info["size"]
|
|
325
|
-
if size == orig_size:
|
|
326
|
-
break
|
|
327
|
-
if size is None:
|
|
328
|
-
return
|
|
329
|
-
log.Notice(
|
|
330
|
-
_("%s Remote filesize %d for %s does not match local size %d, retrying.")
|
|
331
|
-
% (datetime.now(), size, util.escape(dest_filename), orig_size)
|
|
332
|
-
)
|
|
333
|
-
time.sleep(2**attempt)
|
|
334
|
-
if size != orig_size:
|
|
335
|
-
code_extra = f"{util.escape(dest_filename)} {int(orig_size)} {int(size)}"
|
|
336
|
-
log.FatalError(
|
|
337
|
-
_("File %s was corrupted during upload.") % os.fsdecode(dest_filename),
|
|
338
|
-
log.ErrorCode.volume_wrong_size,
|
|
339
|
-
code_extra,
|
|
340
|
-
)
|
|
341
|
-
|
|
342
317
|
def put(tdp, dest_filename, vol_num):
|
|
343
318
|
"""
|
|
344
319
|
Retrieve file size *before* calling backend.put(), which may (at least
|
|
@@ -348,7 +323,15 @@ def write_multivol(backup_type, tarblock_iter, man_outfp, sig_outfp, backend):
|
|
|
348
323
|
putsize = tdp.getsize()
|
|
349
324
|
if config.skip_volume != vol_num: # for testing purposes only
|
|
350
325
|
backend.put(tdp, dest_filename)
|
|
351
|
-
|
|
326
|
+
res, msg = backend.validate(dest_filename, putsize, source_path=tdp)
|
|
327
|
+
if not res:
|
|
328
|
+
code_extra = f"{util.escape(dest_filename)}: {msg}"
|
|
329
|
+
log.FatalError(
|
|
330
|
+
_("File %s was corrupted during upload.") % os.fsdecode(dest_filename),
|
|
331
|
+
log.ErrorCode.backend_verification_failed,
|
|
332
|
+
code_extra,
|
|
333
|
+
)
|
|
334
|
+
|
|
352
335
|
if tdp.stat:
|
|
353
336
|
tdp.delete()
|
|
354
337
|
return putsize
|
|
@@ -602,7 +602,7 @@ class Path(ROPath):
|
|
|
602
602
|
|
|
603
603
|
def mkdir(self):
|
|
604
604
|
"""Make directory(s) at specified path"""
|
|
605
|
-
log.
|
|
605
|
+
log.Debug(_("Making directory %s") % self.uc_name)
|
|
606
606
|
try:
|
|
607
607
|
os.makedirs(self.name)
|
|
608
608
|
except OSError:
|
|
@@ -612,7 +612,7 @@ class Path(ROPath):
|
|
|
612
612
|
|
|
613
613
|
def delete(self):
|
|
614
614
|
"""Remove this file"""
|
|
615
|
-
log.
|
|
615
|
+
log.Debug(_("Deleting %s") % self.uc_name)
|
|
616
616
|
if self.isdir():
|
|
617
617
|
util.ignore_missing(os.rmdir, self.name)
|
|
618
618
|
else:
|
|
@@ -621,7 +621,7 @@ class Path(ROPath):
|
|
|
621
621
|
|
|
622
622
|
def touch(self):
|
|
623
623
|
"""Open the file, write 0 bytes, close"""
|
|
624
|
-
log.
|
|
624
|
+
log.Debug(_("Touching %s") % self.uc_name)
|
|
625
625
|
fp = self.open("wb")
|
|
626
626
|
fp.close()
|
|
627
627
|
|
|
@@ -629,7 +629,7 @@ class Path(ROPath):
|
|
|
629
629
|
"""Remove self by recursively deleting files under it"""
|
|
630
630
|
from duplicity import selection # TODO: avoid circ. dep. issue
|
|
631
631
|
|
|
632
|
-
log.
|
|
632
|
+
log.Debug(_("Deleting tree %s") % self.uc_name)
|
|
633
633
|
itr = IterTreeReducer(PathDeleter, [])
|
|
634
634
|
for path in selection.Select(self).set_iter():
|
|
635
635
|
itr(path.index, path)
|
|
@@ -280,6 +280,7 @@ testing/regression/issue25.json
|
|
|
280
280
|
testing/regression/issue25.sh
|
|
281
281
|
testing/regression/issue26.sh
|
|
282
282
|
testing/regression/issue683.sh
|
|
283
|
+
testing/regression/issue725.sh
|
|
283
284
|
testing/regression/issue73.env
|
|
284
285
|
testing/regression/issue73.sh
|
|
285
286
|
testing/regression/issue781.json
|
|
@@ -308,11 +309,14 @@ testing/unit/test_statistics.py
|
|
|
308
309
|
testing/unit/test_tarfile.py
|
|
309
310
|
testing/unit/test_tempdir.py
|
|
310
311
|
testing/unit/test_util.py
|
|
312
|
+
tools/installpyenv
|
|
313
|
+
tools/installpythons
|
|
311
314
|
tools/installsnap
|
|
312
|
-
tools/list_python_files
|
|
313
315
|
tools/makechangelog
|
|
314
316
|
tools/makepip
|
|
315
317
|
tools/makesnap
|
|
318
|
+
tools/pushpip
|
|
316
319
|
tools/pushsnap
|
|
317
320
|
tools/release-prep
|
|
321
|
+
tools/testpip
|
|
318
322
|
tools/testsnap
|