diff --git a/src/borg/archiver/create_cmd.py b/src/borg/archiver/create_cmd.py index b9c45967..fb9f3cfa 100644 --- a/src/borg/archiver/create_cmd.py +++ b/src/borg/archiver/create_cmd.py @@ -16,7 +16,7 @@ from ..archive import FilesystemObjectProcessors, MetadataCollector, ChunksProce from ..cache import Cache from ..constants import * # NOQA from ..compress import CompressionSpec -from ..helpers import comment_validator, ChunkerParams +from ..helpers import comment_validator, ChunkerParams, PathSpec from ..helpers import archivename_validator, FilesCacheMode from ..helpers import eval_escapes from ..helpers import timestamp, archive_ts_now @@ -937,4 +937,6 @@ class CreateMixIn: ) subparser.add_argument("name", metavar="NAME", type=archivename_validator, help="specify the archive name") - subparser.add_argument("paths", metavar="PATH", nargs="*", type=str, action="extend", help="paths to archive") + subparser.add_argument( + "paths", metavar="PATH", nargs="*", type=PathSpec, action="extend", help="paths to archive" + ) diff --git a/src/borg/archiver/diff_cmd.py b/src/borg/archiver/diff_cmd.py index 2118b888..492a26fc 100644 --- a/src/borg/archiver/diff_cmd.py +++ b/src/borg/archiver/diff_cmd.py @@ -7,7 +7,7 @@ import os from ._common import with_repository, with_archive, build_matcher, Highlander from ..archive import Archive from ..constants import * # NOQA -from ..helpers import BaseFormatter, DiffFormatter, archivename_validator, BorgJsonEncoder +from ..helpers import BaseFormatter, DiffFormatter, archivename_validator, PathSpec, BorgJsonEncoder from ..manifest import Manifest from ..logger import create_logger @@ -167,7 +167,7 @@ class DiffMixIn: "paths", metavar="PATH", nargs="*", - type=str, + type=PathSpec, help="paths of items inside the archives to compare; patterns are supported", ) define_exclusion_group(subparser) diff --git a/src/borg/archiver/extract_cmd.py b/src/borg/archiver/extract_cmd.py index 452b9a9a..7be8a32f 100644 --- a/src/borg/archiver/extract_cmd.py +++ b/src/borg/archiver/extract_cmd.py @@ -8,7 +8,7 @@ from ._common import with_repository, with_archive from ._common import build_filter, build_matcher from ..archive import BackupError, BackupOSError from ..constants import * # NOQA -from ..helpers import archivename_validator +from ..helpers import archivename_validator, PathSpec from ..helpers import remove_surrogates from ..helpers import HardLinkManager from ..helpers import ProgressIndicatorPercent @@ -177,6 +177,6 @@ class ExtractMixIn: ) subparser.add_argument("name", metavar="NAME", type=archivename_validator, help="specify the archive name") subparser.add_argument( - "paths", metavar="PATH", nargs="*", type=str, help="paths to extract; patterns are supported" + "paths", metavar="PATH", nargs="*", type=PathSpec, help="paths to extract; patterns are supported" ) define_exclusion_group(subparser, strip_components=True) diff --git a/src/borg/archiver/key_cmds.py b/src/borg/archiver/key_cmds.py index bdc93a55..cd658b72 100644 --- a/src/borg/archiver/key_cmds.py +++ b/src/borg/archiver/key_cmds.py @@ -6,6 +6,7 @@ from ..constants import * # NOQA from ..crypto.key import AESOCBRepoKey, CHPORepoKey, Blake2AESOCBRepoKey, Blake2CHPORepoKey from ..crypto.key import AESOCBKeyfileKey, CHPOKeyfileKey, Blake2AESOCBKeyfileKey, Blake2CHPOKeyfileKey from ..crypto.keymanager import KeyManager +from ..helpers import PathSpec from ..manifest import Manifest from ._common import with_repository @@ -194,7 +195,7 @@ class KeysMixIn: help="export repository key for backup", ) subparser.set_defaults(func=self.do_key_export) - subparser.add_argument("path", metavar="PATH", nargs="?", type=str, help="where to store the backup") + subparser.add_argument("path", metavar="PATH", nargs="?", type=PathSpec, help="where to store the backup") subparser.add_argument( "--paper", dest="paper", @@ -237,7 +238,7 @@ class KeysMixIn: ) subparser.set_defaults(func=self.do_key_import) subparser.add_argument( - "path", metavar="PATH", nargs="?", type=str, help="path to the backup ('-' to read from stdin)" + "path", metavar="PATH", nargs="?", type=PathSpec, help="path to the backup ('-' to read from stdin)" ) subparser.add_argument( "--paper", diff --git a/src/borg/archiver/list_cmd.py b/src/borg/archiver/list_cmd.py index ead30776..c2a2acb0 100644 --- a/src/borg/archiver/list_cmd.py +++ b/src/borg/archiver/list_cmd.py @@ -7,7 +7,7 @@ from ._common import with_repository, build_matcher, Highlander from ..archive import Archive from ..cache import Cache from ..constants import * # NOQA -from ..helpers import ItemFormatter, BaseFormatter, archivename_validator +from ..helpers import ItemFormatter, BaseFormatter, archivename_validator, PathSpec from ..manifest import Manifest from ..logger import create_logger @@ -119,6 +119,6 @@ class ListMixIn: ) subparser.add_argument("name", metavar="NAME", type=archivename_validator, help="specify the archive name") subparser.add_argument( - "paths", metavar="PATH", nargs="*", type=str, help="paths to list; patterns are supported" + "paths", metavar="PATH", nargs="*", type=PathSpec, help="paths to list; patterns are supported" ) define_exclusion_group(subparser) diff --git a/src/borg/archiver/mount_cmds.py b/src/borg/archiver/mount_cmds.py index 5445cfd0..74c70f24 100644 --- a/src/borg/archiver/mount_cmds.py +++ b/src/borg/archiver/mount_cmds.py @@ -4,6 +4,7 @@ import os from ._common import with_repository, Highlander from ..constants import * # NOQA from ..helpers import EXIT_ERROR +from ..helpers import PathSpec from ..helpers import umount from ..manifest import Manifest from ..remote import cache_if_remote @@ -180,6 +181,6 @@ class MountMixIn: ) define_archive_filters_group(parser) parser.add_argument( - "paths", metavar="PATH", nargs="*", type=str, help="paths to extract; patterns are supported" + "paths", metavar="PATH", nargs="*", type=PathSpec, help="paths to extract; patterns are supported" ) define_exclusion_group(parser, strip_components=True) diff --git a/src/borg/archiver/recreate_cmd.py b/src/borg/archiver/recreate_cmd.py index f6b2a1ff..f1803a48 100644 --- a/src/borg/archiver/recreate_cmd.py +++ b/src/borg/archiver/recreate_cmd.py @@ -5,7 +5,7 @@ from ._common import build_matcher from ..archive import ArchiveRecreater from ..constants import * # NOQA from ..compress import CompressionSpec -from ..helpers import archivename_validator, comment_validator, ChunkerParams +from ..helpers import archivename_validator, comment_validator, PathSpec, ChunkerParams from ..helpers import timestamp from ..manifest import Manifest @@ -205,5 +205,5 @@ class RecreateMixIn: ) subparser.add_argument( - "paths", metavar="PATH", nargs="*", type=str, help="paths to recreate; patterns are supported" + "paths", metavar="PATH", nargs="*", type=PathSpec, help="paths to recreate; patterns are supported" ) diff --git a/src/borg/archiver/tar_cmds.py b/src/borg/archiver/tar_cmds.py index 0504f97e..e54e3fac 100644 --- a/src/borg/archiver/tar_cmds.py +++ b/src/borg/archiver/tar_cmds.py @@ -15,7 +15,7 @@ from ..helpers import dash_open from ..helpers import msgpack from ..helpers import create_filter_process from ..helpers import ChunkIteratorFileWrapper -from ..helpers import archivename_validator, comment_validator, ChunkerParams +from ..helpers import archivename_validator, comment_validator, PathSpec, ChunkerParams from ..helpers import remove_surrogates from ..helpers import timestamp, archive_ts_now from ..helpers import basic_json_data, json_print @@ -418,7 +418,7 @@ class TarMixIn: subparser.add_argument("name", metavar="NAME", type=archivename_validator, help="specify the archive name") subparser.add_argument("tarfile", metavar="FILE", help='output tar file. "-" to write to stdout instead.') subparser.add_argument( - "paths", metavar="PATH", nargs="*", type=str, help="paths to extract; patterns are supported" + "paths", metavar="PATH", nargs="*", type=PathSpec, help="paths to extract; patterns are supported" ) define_exclusion_group(subparser, strip_components=True) diff --git a/src/borg/helpers/__init__.py b/src/borg/helpers/__init__.py index 595928ca..64f66078 100644 --- a/src/borg/helpers/__init__.py +++ b/src/borg/helpers/__init__.py @@ -22,7 +22,7 @@ from .misc import ChunkIteratorFileWrapper, open_item, chunkit, iter_separated, from .parseformat import bin_to_hex, safe_encode, safe_decode from .parseformat import text_to_json, binary_to_json, remove_surrogates, join_cmd from .parseformat import eval_escapes, decode_dict, positive_int_validator, interval -from .parseformat import SortBySpec, ChunkerParams, FilesCacheMode, partial_format, DatetimeWrapper +from .parseformat import PathSpec, SortBySpec, ChunkerParams, FilesCacheMode, partial_format, DatetimeWrapper from .parseformat import format_file_size, parse_file_size, FileSize, parse_storage_quota from .parseformat import sizeof_fmt, sizeof_fmt_iec, sizeof_fmt_decimal, Location, text_validator from .parseformat import format_line, replace_placeholders, PlaceholderError, relative_time_marker_validator diff --git a/src/borg/helpers/parseformat.py b/src/borg/helpers/parseformat.py index 9e77e427..aeb07f83 100644 --- a/src/borg/helpers/parseformat.py +++ b/src/borg/helpers/parseformat.py @@ -284,6 +284,12 @@ class PlaceholderReplacer: replace_placeholders = PlaceholderReplacer() +def PathSpec(text): + if not text: + raise argparse.ArgumentTypeError("Empty strings are not accepted as paths.") + return text + + def SortBySpec(text): from ..manifest import AI_HUMAN_SORT_KEYS