From be2b3363e2d98f7755fdbe872d9fd74f1f8743d5 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Thu, 26 Feb 2026 00:30:58 +0100 Subject: [PATCH] reorg imports --- src/borg/archiver/__init__.py | 8 +- src/borg/archiver/_common.py | 3 +- src/borg/archiver/analyze_cmd.py | 6 +- src/borg/archiver/benchmark_cmd.py | 10 +-- src/borg/archiver/check_cmd.py | 7 +- src/borg/archiver/compact_cmd.py | 6 +- src/borg/archiver/completion_cmd.py | 20 ++--- src/borg/archiver/create_cmd.py | 7 +- src/borg/archiver/debug_cmd.py | 32 ++++---- src/borg/archiver/delete_cmd.py | 6 +- src/borg/archiver/diff_cmd.py | 13 ++- src/borg/archiver/extract_cmd.py | 6 +- src/borg/archiver/help_cmd.py | 4 +- src/borg/archiver/info_cmd.py | 6 +- src/borg/archiver/key_cmds.py | 14 ++-- src/borg/archiver/list_cmd.py | 7 +- src/borg/archiver/lock_cmds.py | 8 +- src/borg/archiver/mount_cmds.py | 10 +-- src/borg/archiver/prune_cmd.py | 6 +- src/borg/archiver/recreate_cmd.py | 7 +- src/borg/archiver/rename_cmd.py | 7 +- src/borg/archiver/repo_compress_cmd.py | 6 +- src/borg/archiver/repo_create_cmd.py | 7 +- src/borg/archiver/repo_delete_cmd.py | 7 +- src/borg/archiver/repo_info_cmd.py | 6 +- src/borg/archiver/repo_list_cmd.py | 7 +- src/borg/archiver/repo_space_cmd.py | 7 +- src/borg/archiver/serve_cmd.py | 6 +- src/borg/archiver/tag_cmd.py | 7 +- src/borg/archiver/tar_cmds.py | 8 +- src/borg/archiver/transfer_cmd.py | 13 +-- src/borg/archiver/undelete_cmd.py | 6 +- src/borg/archiver/version_cmd.py | 6 +- src/borg/compress.pyx | 14 +--- src/borg/helpers/jap_helpers.py | 6 +- src/borg/helpers/parseformat.py | 82 ++++++++----------- src/borg/patterns.py | 14 ++-- .../testsuite/archiver/argparsing_test.py | 8 +- src/borg/testsuite/compress_test.py | 4 +- .../testsuite/helpers/parseformat_test.py | 3 +- src/borg/testsuite/patterns_test.py | 4 +- 41 files changed, 151 insertions(+), 263 deletions(-) diff --git a/src/borg/archiver/__init__.py b/src/borg/archiver/__init__.py index 4df887479..6329221b6 100644 --- a/src/borg/archiver/__init__.py +++ b/src/borg/archiver/__init__.py @@ -15,7 +15,6 @@ else: sys.exit(2) # == EXIT_ERROR try: - import argparse import faulthandler import functools import inspect @@ -27,8 +26,6 @@ try: import signal from datetime import datetime, timezone - from jsonargparse import ArgumentParser, Namespace, SUPPRESS - from ..logger import create_logger, setup_logging logger = create_logger() @@ -42,8 +39,7 @@ try: from ..helpers import format_file_size from ..helpers import remove_surrogates, text_to_json from ..helpers import DatetimeWrapper, replace_placeholders - from ..helpers.jap_helpers import flatten_namespace - + from ..helpers.jap_helpers import flatten_namespace, ArgumentTypeError, ArgumentParser, Namespace, SUPPRESS from ..helpers import is_slow_msgpack, is_supported_msgpack, sysinfo from ..helpers import signal_handler, raising_signal_handler, SigHup, SigTerm from ..helpers import ErrorIgnoringTextIOWrapper @@ -643,7 +639,7 @@ def main(): # pragma: no cover tb = format_tb(e) print(tb, file=sys.stderr) sys.exit(e.exit_code) - except argparse.ArgumentTypeError as e: + except ArgumentTypeError as e: # we might not have logging setup yet, so get out quickly print(str(e), file=sys.stderr) sys.exit(CommandError.exit_mcode if modern_ec else EXIT_ERROR) diff --git a/src/borg/archiver/_common.py b/src/borg/archiver/_common.py index 302eefedc..ad2817d6d 100644 --- a/src/borg/archiver/_common.py +++ b/src/borg/archiver/_common.py @@ -2,8 +2,6 @@ import functools import os import textwrap -from jsonargparse import SUPPRESS - import borg from ..archive import Archive from ..constants import * # NOQA @@ -11,6 +9,7 @@ from ..cache import Cache, assert_secure from ..helpers import Error from ..helpers import SortBySpec, positive_int_validator, location_validator, Location, relative_time_marker_validator from ..helpers import Highlander +from ..helpers.jap_helpers import SUPPRESS from ..helpers.nanorst import rst_to_terminal from ..manifest import Manifest, AI_HUMAN_SORT_KEYS from ..patterns import PatternMatcher diff --git a/src/borg/archiver/analyze_cmd.py b/src/borg/archiver/analyze_cmd.py index 1d99075ad..6ea240a5a 100644 --- a/src/borg/archiver/analyze_cmd.py +++ b/src/borg/archiver/analyze_cmd.py @@ -1,14 +1,12 @@ -import argparse from collections import defaultdict import os -from jsonargparse import ArgumentParser - from ._common import with_repository, define_archive_filters_group from ..archive import Archive from ..constants import * # NOQA from ..helpers import bin_to_hex, Error from ..helpers import ProgressIndicatorPercent +from ..helpers.jap_helpers import ArgumentParser, RawDescriptionHelpFormatter from ..manifest import Manifest from ..remote import RemoteRepository from ..repository import Repository @@ -133,7 +131,7 @@ class AnalyzeMixIn: add_help=False, description=self.do_analyze.__doc__, epilog=analyze_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparsers.add_subcommand("analyze", subparser, help="analyze archives") define_archive_filters_group(subparser) diff --git a/src/borg/archiver/benchmark_cmd.py b/src/borg/archiver/benchmark_cmd.py index 8a94cfd27..abfd85b9e 100644 --- a/src/borg/archiver/benchmark_cmd.py +++ b/src/borg/archiver/benchmark_cmd.py @@ -1,4 +1,3 @@ -import argparse from contextlib import contextmanager import json import logging @@ -6,14 +5,13 @@ import os import tempfile import time -from jsonargparse import ArgumentParser - from ..constants import * # NOQA from ..crypto.key import FlexiKey from ..helpers import format_file_size from ..helpers import json_print from ..helpers import msgpack from ..helpers import get_reset_ec +from ..helpers.jap_helpers import ArgumentParser, RawDescriptionHelpFormatter from ..item import Item from ..platform import SyncFile @@ -361,7 +359,7 @@ class BenchmarkMixIn: add_help=False, description="benchmark command", epilog=benchmark_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparsers.add_subcommand("benchmark", subparser, help="benchmark command") @@ -413,7 +411,7 @@ class BenchmarkMixIn: add_help=False, description=self.do_benchmark_crud.__doc__, epilog=bench_crud_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) benchmark_parsers.add_subcommand( "crud", subparser, help="benchmarks Borg CRUD (create, extract, update, delete)." @@ -438,7 +436,7 @@ class BenchmarkMixIn: add_help=False, description=self.do_benchmark_cpu.__doc__, epilog=bench_cpu_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) benchmark_parsers.add_subcommand("cpu", subparser, help="benchmarks Borg CPU-bound operations.") subparser.add_argument("--json", action="store_true", help="format output as JSON") diff --git a/src/borg/archiver/check_cmd.py b/src/borg/archiver/check_cmd.py index a39232468..ac48febcd 100644 --- a/src/borg/archiver/check_cmd.py +++ b/src/borg/archiver/check_cmd.py @@ -1,12 +1,9 @@ -import argparse - -from jsonargparse import ArgumentParser - from ._common import with_repository, Highlander from ..archive import ArchiveChecker from ..constants import * # NOQA from ..helpers import set_ec, EXIT_WARNING, CancelledByUser, CommandError, IntegrityError from ..helpers import yes +from ..helpers.jap_helpers import ArgumentParser, RawDescriptionHelpFormatter from ..logger import create_logger @@ -190,7 +187,7 @@ class CheckMixIn: add_help=False, description=self.do_check.__doc__, epilog=check_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparsers.add_subcommand("check", subparser, help="verify the repository") subparser.add_argument( diff --git a/src/borg/archiver/compact_cmd.py b/src/borg/archiver/compact_cmd.py index 4e1cbe1c9..113f3f23d 100644 --- a/src/borg/archiver/compact_cmd.py +++ b/src/borg/archiver/compact_cmd.py @@ -1,13 +1,11 @@ -import argparse from pathlib import Path -from jsonargparse import ArgumentParser - from ._common import with_repository from ..archive import Archive from ..cache import write_chunkindex_to_repo_cache, build_chunkindex_from_repo from ..cache import files_cache_name, discover_files_cache_names from ..helpers import get_cache_dir +from ..helpers.jap_helpers import ArgumentParser, RawDescriptionHelpFormatter from ..constants import * # NOQA from ..hashindex import ChunkIndex, ChunkIndexEntry from ..helpers import set_ec, EXIT_ERROR, format_file_size, bin_to_hex @@ -264,7 +262,7 @@ class CompactMixIn: add_help=False, description=self.do_compact.__doc__, epilog=compact_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparsers.add_subcommand("compact", subparser, help="compact the repository") subparser.add_argument( diff --git a/src/borg/archiver/completion_cmd.py b/src/borg/archiver/completion_cmd.py index 90d144300..12a1b04b2 100644 --- a/src/borg/archiver/completion_cmd.py +++ b/src/borg/archiver/completion_cmd.py @@ -50,12 +50,10 @@ The following argument types have intelligent, context-aware completion: - Suggests common file size values (500M, 1G, 10G, 100G, 1T, etc.) """ -import argparse - import shtab -from jsonargparse import ArgumentParser from jsonargparse._actions import _ActionSubCommands +from jsonargparse._completions import prepare_actions_context, shtab_prepare_actions, bash_compgen_typehint from ._common import process_epilog from ..constants import * # NOQA @@ -69,6 +67,7 @@ from ..helpers import ( relative_time_marker_validator, parse_file_size, ) +from ..helpers.jap_helpers import ArgumentParser, RawDescriptionHelpFormatter from ..helpers.time import timestamp from ..compress import CompressionSpec from ..helpers.parseformat import partial_format @@ -344,7 +343,6 @@ _borg_help_topics() { } """ - # Global zsh preamble providing dynamic completion for aid: archive IDs. # # Notes: @@ -735,16 +733,8 @@ class CompletionMixIn: bash_preamble = partial_format(BASH_PREAMBLE_TMPL, mapping) zsh_preamble = partial_format(ZSH_PREAMBLE_TMPL, mapping) - from jsonargparse._completions import ( - prepare_actions_context, - shtab_prepare_actions, - norm_name, - bash_compgen_typehint, - ) - - prog = norm_name(parser.prog) - if not prog: - prog = "borg" + parser.prog = "borg" + prog = "borg" preambles = [] if args.shell == "bash": preambles.append(bash_compgen_typehint.strip().replace("%s", prog)) @@ -777,7 +767,7 @@ class CompletionMixIn: add_help=False, description=self.do_completion.__doc__, epilog=completion_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparsers.add_subcommand("completion", subparser, help="output shell completion script") subparser.add_argument( diff --git a/src/borg/archiver/create_cmd.py b/src/borg/archiver/create_cmd.py index 6313837e2..a6184832c 100644 --- a/src/borg/archiver/create_cmd.py +++ b/src/borg/archiver/create_cmd.py @@ -1,6 +1,5 @@ import errno import sys -import argparse import logging import os import posixpath @@ -9,8 +8,6 @@ import subprocess import time from io import TextIOWrapper -from jsonargparse import ArgumentParser - from ._common import with_repository, Highlander from .. import helpers from ..archive import Archive, is_special @@ -33,6 +30,7 @@ from ..helpers import sig_int, ignore_sigint from ..helpers import iter_separated from ..helpers import MakePathSafeAction from ..helpers import Error, CommandError, BackupWarning, FileChangedWarning +from ..helpers.jap_helpers import ArgumentParser, RawDescriptionHelpFormatter from ..manifest import Manifest from ..patterns import PatternMatcher from ..platform import is_win32 @@ -681,7 +679,6 @@ class CreateMixIn: macOS examples are the apfs mounts of a typical macOS installation. Therefore, when using ``--one-file-system``, you should double-check that the backup works as intended. - .. _list_item_flags: Item flags @@ -779,7 +776,7 @@ class CreateMixIn: add_help=False, description=self.do_create.__doc__, epilog=create_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparsers.add_subcommand("create", subparser, help="create a backup") diff --git a/src/borg/archiver/debug_cmd.py b/src/borg/archiver/debug_cmd.py index fa35ac295..d8feb8d9d 100644 --- a/src/borg/archiver/debug_cmd.py +++ b/src/borg/archiver/debug_cmd.py @@ -1,9 +1,6 @@ -import argparse import json import textwrap -from jsonargparse import ArgumentParser - from ..archive import Archive from ..compress import CompressionSpec from ..constants import * # NOQA @@ -14,6 +11,7 @@ from ..helpers import dash_open from ..helpers import StableDict from ..helpers import archivename_validator from ..helpers import CommandError, RTError +from ..helpers.jap_helpers import ArgumentParser, RawDescriptionHelpFormatter from ..manifest import Manifest from ..platform import get_process_id from ..repository import Repository, LIST_SCAN_LIMIT, repo_lister @@ -325,7 +323,7 @@ class DebugMixIn: add_help=False, description="debugging command (not intended for normal use)", epilog=debug_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparsers.add_subcommand("debug", subparser, help="debugging command (not intended for normal use)") @@ -343,7 +341,7 @@ class DebugMixIn: add_help=False, description=self.do_debug_info.__doc__, epilog=debug_info_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) debug_parsers.add_subcommand("info", subparser, help="show system infos for debugging / bug reports (debug)") @@ -357,7 +355,7 @@ class DebugMixIn: add_help=False, description=self.do_debug_dump_archive_items.__doc__, epilog=debug_dump_archive_items_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) debug_parsers.add_subcommand("dump-archive-items", subparser, help="dump archive items (metadata) (debug)") subparser.add_argument("name", metavar="NAME", type=archivename_validator, help="specify the archive name") @@ -372,7 +370,7 @@ class DebugMixIn: add_help=False, description=self.do_debug_dump_archive.__doc__, epilog=debug_dump_archive_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) debug_parsers.add_subcommand("dump-archive", subparser, help="dump decoded archive metadata (debug)") subparser.add_argument("name", metavar="NAME", type=archivename_validator, help="specify the archive name") @@ -388,7 +386,7 @@ class DebugMixIn: add_help=False, description=self.do_debug_dump_manifest.__doc__, epilog=debug_dump_manifest_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) debug_parsers.add_subcommand("dump-manifest", subparser, help="dump decoded repository metadata (debug)") subparser.add_argument("path", metavar="PATH", type=str, help="file to dump data into") @@ -403,7 +401,7 @@ class DebugMixIn: add_help=False, description=self.do_debug_dump_repo_objs.__doc__, epilog=debug_dump_repo_objs_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) debug_parsers.add_subcommand("dump-repo-objs", subparser, help="dump repo objects (debug)") @@ -417,7 +415,7 @@ class DebugMixIn: add_help=False, description=self.do_debug_search_repo_objs.__doc__, epilog=debug_search_repo_objs_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) debug_parsers.add_subcommand("search-repo-objs", subparser, help="search repo objects (debug)") subparser.add_argument( @@ -437,7 +435,7 @@ class DebugMixIn: add_help=False, description=self.do_debug_id_hash.__doc__, epilog=debug_id_hash_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) debug_parsers.add_subcommand("id-hash", subparser, help="compute id-hash for some file content (debug)") subparser.add_argument( @@ -455,7 +453,7 @@ class DebugMixIn: add_help=False, description=self.do_debug_parse_obj.__doc__, epilog=debug_parse_obj_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) debug_parsers.add_subcommand("parse-obj", subparser, help="parse borg object file into meta dict and data") subparser.add_argument("id", metavar="ID", type=str, help="hex object ID to get from the repo") @@ -480,7 +478,7 @@ class DebugMixIn: add_help=False, description=self.do_debug_format_obj.__doc__, epilog=debug_format_obj_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) debug_parsers.add_subcommand("format-obj", subparser, help="format file and metadata into a Borg object file") subparser.add_argument("id", metavar="ID", type=str, help="hex object ID to get from the repo") @@ -517,7 +515,7 @@ class DebugMixIn: add_help=False, description=self.do_debug_get_obj.__doc__, epilog=debug_get_obj_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) debug_parsers.add_subcommand("get-obj", subparser, help="get object from repository (debug)") subparser.add_argument("id", metavar="ID", type=str, help="hex object ID to get from the repo") @@ -533,7 +531,7 @@ class DebugMixIn: add_help=False, description=self.do_debug_put_obj.__doc__, epilog=debug_put_obj_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) debug_parsers.add_subcommand("put-obj", subparser, help="put object to repository (debug)") subparser.add_argument("id", metavar="ID", type=str, help="hex object ID to put into the repo") @@ -549,7 +547,7 @@ class DebugMixIn: add_help=False, description=self.do_debug_delete_obj.__doc__, epilog=debug_delete_obj_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) debug_parsers.add_subcommand("delete-obj", subparser, help="delete object from repository (debug)") subparser.add_argument( @@ -566,7 +564,7 @@ class DebugMixIn: add_help=False, description=self.do_debug_convert_profile.__doc__, epilog=debug_convert_profile_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) debug_parsers.add_subcommand( "convert-profile", subparser, help="convert Borg profile to Python profile (debug)" diff --git a/src/borg/archiver/delete_cmd.py b/src/borg/archiver/delete_cmd.py index 995c6e2e4..5298e5e3e 100644 --- a/src/borg/archiver/delete_cmd.py +++ b/src/borg/archiver/delete_cmd.py @@ -1,11 +1,9 @@ -import argparse import logging -from jsonargparse import ArgumentParser - from ._common import with_repository from ..constants import * # NOQA from ..helpers import format_archive, CommandError, bin_to_hex, archivename_validator +from ..helpers.jap_helpers import ArgumentParser, RawDescriptionHelpFormatter from ..manifest import Manifest from ..logger import create_logger @@ -87,7 +85,7 @@ class DeleteMixIn: add_help=False, description=self.do_delete.__doc__, epilog=delete_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparsers.add_subcommand("delete", subparser, help="delete archives") subparser.add_argument( diff --git a/src/borg/archiver/diff_cmd.py b/src/borg/archiver/diff_cmd.py index ceaa0b0d2..c3a993b64 100644 --- a/src/borg/archiver/diff_cmd.py +++ b/src/borg/archiver/diff_cmd.py @@ -1,16 +1,14 @@ -import argparse import textwrap import json import sys import os -from jsonargparse import ArgumentParser - from ._common import with_repository, build_matcher, Highlander from ..archive import Archive from ..constants import * # NOQA from ..helpers import BaseFormatter, DiffFormatter, archivename_validator, PathSpec, BorgJsonEncoder from ..helpers import IncludePatternNeverMatchedWarning, remove_surrogates +from ..helpers.jap_helpers import ArgumentParser, ArgumentTypeError, RawDescriptionHelpFormatter from ..item import ItemDiff from ..manifest import Manifest from ..logger import create_logger @@ -206,7 +204,6 @@ class DiffMixIn: The following keys are always available: - """ ) + BaseFormatter.keys_help() @@ -271,7 +268,7 @@ class DiffMixIn: def diff_sort_spec_validator(s): if not isinstance(s, str): - raise argparse.ArgumentTypeError("unsupported sort field (not a string)") + raise ArgumentTypeError("unsupported sort field (not a string)") allowed = { "path", "size_added", @@ -289,11 +286,11 @@ class DiffMixIn: } parts = [p.strip() for p in s.split(",") if p.strip()] if not parts: - raise argparse.ArgumentTypeError("unsupported sort field: empty spec") + raise ArgumentTypeError("unsupported sort field: empty spec") for spec in parts: field = spec[1:] if spec and spec[0] in (">", "<") else spec if field not in allowed: - raise argparse.ArgumentTypeError(f"unsupported sort field: {field}") + raise ArgumentTypeError(f"unsupported sort field: {field}") return ",".join(parts) subparser = ArgumentParser( @@ -301,7 +298,7 @@ class DiffMixIn: add_help=False, description=self.do_diff.__doc__, epilog=diff_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparsers.add_subcommand("diff", subparser, help="find differences in archive contents") subparser.add_argument( diff --git a/src/borg/archiver/extract_cmd.py b/src/borg/archiver/extract_cmd.py index fb5a67e76..c6fb5743d 100644 --- a/src/borg/archiver/extract_cmd.py +++ b/src/borg/archiver/extract_cmd.py @@ -1,10 +1,7 @@ import sys -import argparse import logging import stat -from jsonargparse import ArgumentParser - from ._common import with_repository, with_archive from ._common import build_filter, build_matcher from ..archive import BackupError @@ -14,6 +11,7 @@ from ..helpers import remove_surrogates from ..helpers import HardLinkManager from ..helpers import ProgressIndicatorPercent from ..helpers import BackupWarning, IncludePatternNeverMatchedWarning +from ..helpers.jap_helpers import ArgumentParser, RawDescriptionHelpFormatter from ..manifest import Manifest from ..logger import create_logger @@ -162,7 +160,7 @@ class ExtractMixIn: add_help=False, description=self.do_extract.__doc__, epilog=extract_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparsers.add_subcommand("extract", subparser, help="extract archive contents") subparser.add_argument( diff --git a/src/borg/archiver/help_cmd.py b/src/borg/archiver/help_cmd.py index 41b412481..68e138cdc 100644 --- a/src/borg/archiver/help_cmd.py +++ b/src/borg/archiver/help_cmd.py @@ -1,8 +1,7 @@ import collections import textwrap -from jsonargparse import ArgumentParser - +from ..helpers.jap_helpers import ArgumentParser from ..constants import * # NOQA from ..helpers.nanorst import rst_to_terminal @@ -162,7 +161,6 @@ class HelpMixIn: # not '/home/user/importantjunk' or '/etc/junk': $ borg create -e 'home/*/junk' archive / - # The contents of directories in '/home' are not backed up when their name # ends in '.tmp' $ borg create --exclude 're:^home/[^/]+\\.tmp/' archive / diff --git a/src/borg/archiver/info_cmd.py b/src/borg/archiver/info_cmd.py index c4a808265..97c769c92 100644 --- a/src/borg/archiver/info_cmd.py +++ b/src/borg/archiver/info_cmd.py @@ -1,13 +1,11 @@ -import argparse import textwrap from datetime import timedelta -from jsonargparse import ArgumentParser - from ._common import with_repository from ..archive import Archive from ..constants import * # NOQA from ..helpers import format_timedelta, json_print, basic_json_data, archivename_validator +from ..helpers.jap_helpers import ArgumentParser, RawDescriptionHelpFormatter from ..manifest import Manifest from ..logger import create_logger @@ -85,7 +83,7 @@ class InfoMixIn: add_help=False, description=self.do_info.__doc__, epilog=info_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparsers.add_subcommand("info", subparser, help="show repository or archive information") subparser.add_argument("--json", action="store_true", help="format output as JSON") diff --git a/src/borg/archiver/key_cmds.py b/src/borg/archiver/key_cmds.py index 0984a38fb..bd8161470 100644 --- a/src/borg/archiver/key_cmds.py +++ b/src/borg/archiver/key_cmds.py @@ -1,13 +1,11 @@ -import argparse import os -from jsonargparse import ArgumentParser - 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, CommandError +from ..helpers.jap_helpers import ArgumentParser, RawDescriptionHelpFormatter from ..manifest import Manifest from ._common import with_repository @@ -126,7 +124,7 @@ class KeysMixIn: add_help=False, description="Manage the keyfile or repokey of a repository", epilog="", - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparsers.add_subcommand("key", subparser, help="manage the repository key") @@ -168,7 +166,7 @@ class KeysMixIn: add_help=False, description=self.do_key_export.__doc__, epilog=key_export_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) key_parsers.add_subcommand("export", subparser, help="export the repository key for backup") subparser.add_argument("path", metavar="PATH", nargs="?", type=PathSpec, help="where to store the backup") @@ -208,7 +206,7 @@ class KeysMixIn: add_help=False, description=self.do_key_import.__doc__, epilog=key_import_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) key_parsers.add_subcommand("import", subparser, help="import the repository key from backup") subparser.add_argument( @@ -237,7 +235,7 @@ class KeysMixIn: add_help=False, description=self.do_key_change_passphrase.__doc__, epilog=change_passphrase_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) key_parsers.add_subcommand("change-passphrase", subparser, help="change the repository passphrase") @@ -259,7 +257,7 @@ class KeysMixIn: add_help=False, description=self.do_key_change_location.__doc__, epilog=change_location_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) key_parsers.add_subcommand("change-location", subparser, help="change the key location") subparser.add_argument( diff --git a/src/borg/archiver/list_cmd.py b/src/borg/archiver/list_cmd.py index 4a355275e..19a4d44fc 100644 --- a/src/borg/archiver/list_cmd.py +++ b/src/borg/archiver/list_cmd.py @@ -1,15 +1,13 @@ -import argparse import os import textwrap import sys -from jsonargparse import ArgumentParser - 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, PathSpec +from ..helpers.jap_helpers import ArgumentParser, RawDescriptionHelpFormatter from ..manifest import Manifest from ..logger import create_logger @@ -92,7 +90,6 @@ class ListMixIn: The following keys are always available: - """ ) + BaseFormatter.keys_help() @@ -110,7 +107,7 @@ class ListMixIn: add_help=False, description=self.do_list.__doc__, epilog=list_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparsers.add_subcommand("list", subparser, help="list archive contents") subparser.add_argument( diff --git a/src/borg/archiver/lock_cmds.py b/src/borg/archiver/lock_cmds.py index 03ab393a3..17cf90e0b 100644 --- a/src/borg/archiver/lock_cmds.py +++ b/src/borg/archiver/lock_cmds.py @@ -1,12 +1,10 @@ -import argparse import subprocess -from jsonargparse import ArgumentParser, REMAINDER - from ._common import with_repository from ..cache import Cache from ..constants import * # NOQA from ..helpers import prepare_subprocess_env, set_ec, CommandError, ThreadRunner +from ..helpers.jap_helpers import ArgumentParser, RawDescriptionHelpFormatter, REMAINDER from ..logger import create_logger @@ -52,7 +50,7 @@ class LocksMixIn: add_help=False, description=self.do_break_lock.__doc__, epilog=break_lock_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparsers.add_subcommand("break-lock", subparser, help="break the repository and cache locks") @@ -82,7 +80,7 @@ class LocksMixIn: add_help=False, description=self.do_with_lock.__doc__, epilog=with_lock_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparsers.add_subcommand("with-lock", subparser, help="run a user command with the lock held") subparser.add_argument("command", metavar="COMMAND", help="command to run") diff --git a/src/borg/archiver/mount_cmds.py b/src/borg/archiver/mount_cmds.py index 7a4164de1..9174e3fd7 100644 --- a/src/borg/archiver/mount_cmds.py +++ b/src/borg/archiver/mount_cmds.py @@ -1,13 +1,11 @@ -import argparse import os -from jsonargparse import ArgumentParser - from ._common import with_repository, Highlander from ..constants import * # NOQA from ..helpers import RTError from ..helpers import PathSpec from ..helpers import umount +from ..helpers.jap_helpers import ArgumentParser, RawDescriptionHelpFormatter from ..manifest import Manifest from ..remote import cache_if_remote @@ -158,7 +156,7 @@ class MountMixIn: add_help=False, description=self.do_mount.__doc__, epilog=mount_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparsers.add_subcommand("mount", subparser, help="mount a repository") self._define_borg_mount(subparser) @@ -176,7 +174,7 @@ class MountMixIn: add_help=False, description=self.do_umount.__doc__, epilog=umount_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparsers.add_subcommand("umount", subparser, help="unmount a repository") subparser.add_argument( @@ -187,7 +185,7 @@ class MountMixIn: assert parser.prog == "borgfs" parser.description = self.do_mount.__doc__ parser.epilog = "For more information, see borg mount --help." - parser.formatter_class = argparse.RawDescriptionHelpFormatter + parser.formatter_class = RawDescriptionHelpFormatter parser.help = "mount a repository" self._define_borg_mount(parser) return parser diff --git a/src/borg/archiver/prune_cmd.py b/src/borg/archiver/prune_cmd.py index 4f8481bdd..eeb20a866 100644 --- a/src/borg/archiver/prune_cmd.py +++ b/src/borg/archiver/prune_cmd.py @@ -1,16 +1,14 @@ -import argparse from collections import OrderedDict from datetime import datetime, timezone, timedelta import logging from operator import attrgetter import os -from jsonargparse import ArgumentParser - from ._common import with_repository, Highlander from ..constants import * # NOQA from ..helpers import ArchiveFormatter, interval, sig_int, ProgressIndicatorPercent, CommandError, Error from ..helpers import archivename_validator +from ..helpers.jap_helpers import ArgumentParser, RawDescriptionHelpFormatter from ..manifest import Manifest from ..logger import create_logger @@ -280,7 +278,7 @@ class PruneMixIn: add_help=False, description=self.do_prune.__doc__, epilog=prune_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparsers.add_subcommand("prune", subparser, help="prune archives") subparser.add_argument( diff --git a/src/borg/archiver/recreate_cmd.py b/src/borg/archiver/recreate_cmd.py index ff074261e..12d3f4a3d 100644 --- a/src/borg/archiver/recreate_cmd.py +++ b/src/borg/archiver/recreate_cmd.py @@ -1,7 +1,3 @@ -import argparse - -from jsonargparse import ArgumentParser - from ._common import with_repository, Highlander from ._common import build_matcher from ..archive import ArchiveRecreater @@ -9,6 +5,7 @@ from ..constants import * # NOQA from ..compress import CompressionSpec from ..helpers import archivename_validator, comment_validator, PathSpec, ChunkerParams, bin_to_hex from ..helpers import timestamp +from ..helpers.jap_helpers import ArgumentParser, RawDescriptionHelpFormatter from ..manifest import Manifest from ..logger import create_logger @@ -109,7 +106,7 @@ class RecreateMixIn: add_help=False, description=self.do_recreate.__doc__, epilog=recreate_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparsers.add_subcommand("recreate", subparser, help=self.do_recreate.__doc__) subparser.add_argument( diff --git a/src/borg/archiver/rename_cmd.py b/src/borg/archiver/rename_cmd.py index 316f9734c..9fadaabf5 100644 --- a/src/borg/archiver/rename_cmd.py +++ b/src/borg/archiver/rename_cmd.py @@ -1,10 +1,7 @@ -import argparse - -from jsonargparse import ArgumentParser - from ._common import with_repository, with_archive from ..constants import * # NOQA from ..helpers import archivename_validator +from ..helpers.jap_helpers import ArgumentParser, RawDescriptionHelpFormatter from ..manifest import Manifest from ..logger import create_logger @@ -35,7 +32,7 @@ class RenameMixIn: add_help=False, description=self.do_rename.__doc__, epilog=rename_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparsers.add_subcommand("rename", subparser, help="rename an archive") subparser.add_argument( diff --git a/src/borg/archiver/repo_compress_cmd.py b/src/borg/archiver/repo_compress_cmd.py index 7bf38e31c..7c547ac79 100644 --- a/src/borg/archiver/repo_compress_cmd.py +++ b/src/borg/archiver/repo_compress_cmd.py @@ -1,13 +1,11 @@ -import argparse from collections import defaultdict -from jsonargparse import ArgumentParser - from ._common import with_repository, Highlander from ..constants import * # NOQA from ..compress import CompressionSpec, ObfuscateSize, Auto, COMPRESSOR_TABLE from ..hashindex import ChunkIndex from ..helpers import sig_int, ProgressIndicatorPercent, Error +from ..helpers.jap_helpers import ArgumentParser, RawDescriptionHelpFormatter from ..repository import Repository from ..remote import RemoteRepository from ..manifest import Manifest @@ -187,7 +185,7 @@ class RepoCompressMixIn: add_help=False, description=self.do_repo_compress.__doc__, epilog=repo_compress_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparsers.add_subcommand("repo-compress", subparser, help=self.do_repo_compress.__doc__) diff --git a/src/borg/archiver/repo_create_cmd.py b/src/borg/archiver/repo_create_cmd.py index 7856e46fc..97b2c5a8e 100644 --- a/src/borg/archiver/repo_create_cmd.py +++ b/src/borg/archiver/repo_create_cmd.py @@ -1,13 +1,10 @@ -import argparse - -from jsonargparse import ArgumentParser - from ._common import with_repository, with_other_repository, Highlander from ..cache import Cache from ..constants import * # NOQA from ..crypto.key import key_creator, key_argument_names from ..helpers import CancelledByUser from ..helpers import location_validator, Location +from ..helpers.jap_helpers import ArgumentParser, RawDescriptionHelpFormatter from ..manifest import Manifest from ..logger import create_logger @@ -197,7 +194,7 @@ class RepoCreateMixIn: add_help=False, description=self.do_repo_create.__doc__, epilog=repo_create_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparsers.add_subcommand("repo-create", subparser, help="create a new, empty repository") subparser.add_argument( diff --git a/src/borg/archiver/repo_delete_cmd.py b/src/borg/archiver/repo_delete_cmd.py index 51319c6a3..7d0638e5f 100644 --- a/src/borg/archiver/repo_delete_cmd.py +++ b/src/borg/archiver/repo_delete_cmd.py @@ -1,7 +1,3 @@ -import argparse - -from jsonargparse import ArgumentParser - from ._common import with_repository from ..cache import Cache, SecurityManager from ..constants import * # NOQA @@ -9,6 +5,7 @@ from ..helpers import CancelledByUser from ..helpers import format_archive from ..helpers import bin_to_hex from ..helpers import yes +from ..helpers.jap_helpers import ArgumentParser, RawDescriptionHelpFormatter from ..manifest import Manifest, NoManifestError from ..logger import create_logger @@ -109,7 +106,7 @@ class RepoDeleteMixIn: add_help=False, description=self.do_repo_delete.__doc__, epilog=repo_delete_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparsers.add_subcommand("repo-delete", subparser, help="delete a repository") subparser.add_argument( diff --git a/src/borg/archiver/repo_info_cmd.py b/src/borg/archiver/repo_info_cmd.py index 43c37d503..914cf433d 100644 --- a/src/borg/archiver/repo_info_cmd.py +++ b/src/borg/archiver/repo_info_cmd.py @@ -1,11 +1,9 @@ -import argparse import textwrap -from jsonargparse import ArgumentParser - from ._common import with_repository from ..constants import * # NOQA from ..helpers import bin_to_hex, json_print, basic_json_data +from ..helpers.jap_helpers import ArgumentParser, RawDescriptionHelpFormatter from ..manifest import Manifest from ..logger import create_logger @@ -70,7 +68,7 @@ class RepoInfoMixIn: add_help=False, description=self.do_repo_info.__doc__, epilog=repo_info_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparsers.add_subcommand("repo-info", subparser, help="show repository information") subparser.add_argument("--json", action="store_true", help="format output as JSON") diff --git a/src/borg/archiver/repo_list_cmd.py b/src/borg/archiver/repo_list_cmd.py index a91c4a53b..39487ac4f 100644 --- a/src/borg/archiver/repo_list_cmd.py +++ b/src/borg/archiver/repo_list_cmd.py @@ -1,13 +1,11 @@ -import argparse import os import textwrap import sys -from jsonargparse import ArgumentParser - from ._common import with_repository, Highlander from ..constants import * # NOQA from ..helpers import BaseFormatter, ArchiveFormatter, json_print, basic_json_data +from ..helpers.jap_helpers import ArgumentParser, RawDescriptionHelpFormatter from ..manifest import Manifest from ..logger import create_logger @@ -74,7 +72,6 @@ class RepoListMixIn: The following keys are always available: - """ ) + BaseFormatter.keys_help() @@ -92,7 +89,7 @@ class RepoListMixIn: add_help=False, description=self.do_repo_list.__doc__, epilog=repo_list_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparsers.add_subcommand("repo-list", subparser, help="list repository contents") subparser.add_argument( diff --git a/src/borg/archiver/repo_space_cmd.py b/src/borg/archiver/repo_space_cmd.py index 72a03007d..329f5715d 100644 --- a/src/borg/archiver/repo_space_cmd.py +++ b/src/borg/archiver/repo_space_cmd.py @@ -1,14 +1,12 @@ -import argparse import math import os -from jsonargparse import ArgumentParser - from borgstore.store import ItemInfo from ._common import with_repository, Highlander from ..constants import * # NOQA from ..helpers import parse_file_size, format_file_size +from ..helpers.jap_helpers import ArgumentParser, RawDescriptionHelpFormatter from ..logger import create_logger @@ -84,7 +82,6 @@ class RepoSpaceMixIn: $ borg compact -v # only this actually frees space of deleted archives $ borg repo-space --reserve 1G # reserve space again for next time - Reserved space is always rounded up to full reservation blocks of 64 MiB. """ ) @@ -93,7 +90,7 @@ class RepoSpaceMixIn: add_help=False, description=self.do_repo_space.__doc__, epilog=repo_space_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparsers.add_subcommand("repo-space", subparser, help="manage reserved space in a repository") subparser.add_argument( diff --git a/src/borg/archiver/serve_cmd.py b/src/borg/archiver/serve_cmd.py index 6774d65a2..68b4d78f8 100644 --- a/src/borg/archiver/serve_cmd.py +++ b/src/borg/archiver/serve_cmd.py @@ -1,9 +1,8 @@ -import argparse - from ..constants import * # NOQA from ..remote import RepositoryServer from ..logger import create_logger +from ..helpers.jap_helpers import RawDescriptionHelpFormatter, ArgumentParser logger = create_logger() @@ -19,7 +18,6 @@ class ServeMixIn: ).serve() def build_parser_serve(self, subparsers, common_parser, mid_common_parser): - from jsonargparse import ArgumentParser from ._common import process_epilog serve_epilog = process_epilog( @@ -58,7 +56,7 @@ class ServeMixIn: add_help=False, description=self.do_serve.__doc__, epilog=serve_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparsers.add_subcommand("serve", subparser, help="start the repository server process") subparser.add_argument( diff --git a/src/borg/archiver/tag_cmd.py b/src/borg/archiver/tag_cmd.py index 23f9c9d12..e6de40a43 100644 --- a/src/borg/archiver/tag_cmd.py +++ b/src/borg/archiver/tag_cmd.py @@ -1,11 +1,8 @@ -import argparse - -from jsonargparse import ArgumentParser - from ._common import with_repository, define_archive_filters_group from ..archive import Archive from ..constants import * # NOQA from ..helpers import bin_to_hex, archivename_validator, tag_validator, Error +from ..helpers.jap_helpers import ArgumentParser, RawDescriptionHelpFormatter from ..manifest import Manifest from ..logger import create_logger @@ -87,7 +84,7 @@ class TagMixIn: add_help=False, description=self.do_tag.__doc__, epilog=tag_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparsers.add_subcommand("tag", subparser, help="tag archives") subparser.add_argument("--set", dest="set_tags", metavar="TAG", type=tag_validator, nargs="+", help="set tags") diff --git a/src/borg/archiver/tar_cmds.py b/src/borg/archiver/tar_cmds.py index 8830ebcdc..416b55fc2 100644 --- a/src/borg/archiver/tar_cmds.py +++ b/src/borg/archiver/tar_cmds.py @@ -1,12 +1,9 @@ -import argparse import base64 import logging import os import stat import tarfile -from jsonargparse import ArgumentParser - from ..archive import Archive, TarfileObjectProcessors, ChunksProcessor from ..compress import CompressionSpec from ..constants import * # NOQA @@ -21,6 +18,7 @@ from ..helpers import remove_surrogates from ..helpers import timestamp, archive_ts_now from ..helpers import basic_json_data, json_print from ..helpers import log_multi +from ..helpers.jap_helpers import ArgumentParser, RawDescriptionHelpFormatter from ..manifest import Manifest from ._common import with_repository, with_archive, Highlander, define_exclusion_group @@ -391,7 +389,7 @@ class TarMixIn: add_help=False, description=self.do_export_tar.__doc__, epilog=export_tar_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparsers.add_subcommand("export-tar", subparser, help="create tarball from archive") subparser.add_argument( @@ -465,7 +463,7 @@ class TarMixIn: add_help=False, description=self.do_import_tar.__doc__, epilog=import_tar_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparsers.add_subcommand("import-tar", subparser, help=self.do_import_tar.__doc__) subparser.add_argument( diff --git a/src/borg/archiver/transfer_cmd.py b/src/borg/archiver/transfer_cmd.py index a034b0635..62001f080 100644 --- a/src/borg/archiver/transfer_cmd.py +++ b/src/borg/archiver/transfer_cmd.py @@ -1,7 +1,3 @@ -import argparse - -from jsonargparse import ArgumentParser - from ._common import with_repository, with_other_repository, Highlander from ..archive import Archive, cached_hash, DownloadPipeline from ..chunkers import get_chunker @@ -12,6 +8,7 @@ from ..helpers import Error from ..helpers import location_validator, Location, archivename_validator, comment_validator from ..helpers import format_file_size, bin_to_hex from ..helpers import ChunkerParams, ChunkIteratorFileWrapper +from ..helpers.jap_helpers import ArgumentParser, ArgumentTypeError, RawDescriptionHelpFormatter from ..item import ChunkListEntry from ..manifest import Manifest from ..legacyrepository import LegacyRepository @@ -158,7 +155,7 @@ class TransferMixIn: for archive_info in archive_infos: try: archivename_validator(archive_info.name) - except argparse.ArgumentTypeError as err: + except ArgumentTypeError as err: an_errors.append(str(err)) if an_errors: an_errors.insert(0, "Invalid archive names detected, please rename them before transfer:") @@ -169,7 +166,7 @@ class TransferMixIn: archive = Archive(other_manifest, archive_info.id) try: comment_validator(archive.metadata.get("comment", "")) - except argparse.ArgumentTypeError as err: + except ArgumentTypeError as err: ac_errors.append(f"{archive_info.name}: {err}") if ac_errors: ac_errors.insert(0, "Invalid archive comments detected, please fix them before transfer:") @@ -311,7 +308,6 @@ class TransferMixIn: borg --repo=DST_REPO transfer --other-repo=SRC_REPO # do it! borg --repo=DST_REPO transfer --other-repo=SRC_REPO --dry-run # check! anything left? - Data migration / upgrade from borg 1.x ++++++++++++++++++++++++++++++++++++++ @@ -332,7 +328,6 @@ class TransferMixIn: borg --repo=DST_REPO transfer --other-repo=SRC_REPO \\ --chunker-params=buzhash,19,23,21,4095 - """ ) subparser = ArgumentParser( @@ -340,7 +335,7 @@ class TransferMixIn: add_help=False, description=self.do_transfer.__doc__, epilog=transfer_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparsers.add_subcommand("transfer", subparser, help="transfer of archives from another repository") subparser.add_argument( diff --git a/src/borg/archiver/undelete_cmd.py b/src/borg/archiver/undelete_cmd.py index 1c2bdf682..e0f5da7f0 100644 --- a/src/borg/archiver/undelete_cmd.py +++ b/src/borg/archiver/undelete_cmd.py @@ -1,11 +1,9 @@ -import argparse import logging -from jsonargparse import ArgumentParser - from ._common import with_repository from ..constants import * # NOQA from ..helpers import format_archive, CommandError, bin_to_hex, archivename_validator +from ..helpers.jap_helpers import ArgumentParser, RawDescriptionHelpFormatter from ..manifest import Manifest from ..logger import create_logger @@ -79,7 +77,7 @@ class UnDeleteMixIn: add_help=False, description=self.do_undelete.__doc__, epilog=undelete_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparsers.add_subcommand("undelete", subparser, help="undelete archives") subparser.add_argument( diff --git a/src/borg/archiver/version_cmd.py b/src/borg/archiver/version_cmd.py index bfa825ba0..4cd8d0ddd 100644 --- a/src/borg/archiver/version_cmd.py +++ b/src/borg/archiver/version_cmd.py @@ -1,7 +1,6 @@ -import argparse - from .. import __version__ from ..constants import * # NOQA +from ..helpers.jap_helpers import RawDescriptionHelpFormatter, ArgumentParser from ..remote import RemoteRepository from ..logger import create_logger @@ -23,7 +22,6 @@ class VersionMixIn: print(f"{format_version(client_version)} / {format_version(server_version)}") def build_parser_version(self, subparsers, common_parser, mid_common_parser): - from jsonargparse import ArgumentParser from ._common import process_epilog version_epilog = process_epilog( @@ -57,6 +55,6 @@ class VersionMixIn: add_help=False, description=self.do_version.__doc__, epilog=version_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparsers.add_subcommand("version", subparser, help="display the Borg client and server versions") diff --git a/src/borg/compress.pyx b/src/borg/compress.pyx index a5f04998f..33a772043 100644 --- a/src/borg/compress.pyx +++ b/src/borg/compress.pyx @@ -15,10 +15,10 @@ which compressor has been used to compress the data and dispatch to the correct decompressor. """ -from argparse import ArgumentTypeError import math import random from struct import Struct +import sys import zlib try: @@ -28,15 +28,13 @@ except ImportError: from .constants import MAX_DATA_SIZE, ROBJ_FILE_STREAM from .helpers import Buffer, DecompressionError - -import sys +from .helpers.jap_helpers import ArgumentTypeError if sys.version_info >= (3, 14): from compression import zstd else: from backports import zstd - cdef extern from "lz4.h": int LZ4_compress_default(const char* source, char* dest, int inputSize, int maxOutputSize) nogil int LZ4_decompress_safe(const char* source, char* dest, int inputSize, int maxOutputSize) nogil @@ -120,7 +118,6 @@ cdef class CompressorBase: else: pass # raise ValueError("size not present and not in legacy mode") - cdef class DecidingCompressor(CompressorBase): """ base class for (de)compression classes that (based on an internal _decide @@ -188,7 +185,6 @@ class CNONE(CompressorBase): self.check_fix_size(meta, data) return meta, data - class LZ4(DecidingCompressor): """ raw LZ4 compression / decompression (liblz4). @@ -260,7 +256,6 @@ class LZ4(DecidingCompressor): self.check_fix_size(meta, data) return meta, data - class LZMA(DecidingCompressor): """ lzma compression / decompression @@ -355,7 +350,6 @@ class ZLIB(DecidingCompressor): except zlib.error as e: raise DecompressionError(str(e)) from None - class ZLIB_legacy(CompressorBase): """ zlib compression / decompression (python stdlib) @@ -402,7 +396,6 @@ class ZLIB_legacy(CompressorBase): except zlib.error as e: raise DecompressionError(str(e)) from None - class Auto(CompressorBase): """ Meta-Compressor that decides which compression to use based on LZ4's ratio. @@ -484,7 +477,6 @@ class Auto(CompressorBase): def detect(cls, data): raise NotImplementedError - class ObfuscateSize(CompressorBase): """ Meta-Compressor that obfuscates the compressed data size. @@ -569,7 +561,6 @@ class ObfuscateSize(CompressorBase): self.compressor = compressor_cls() return self.compressor.decompress(meta, compressed_data) # decompress data - # Maps valid compressor names to their class COMPRESSOR_TABLE = { CNONE.name: CNONE, @@ -624,7 +615,6 @@ class Compressor: else: raise ValueError('No decompressor for this data found: %r.', data[:2]) - class CompressionSpec: def __init__(self, s): if isinstance(s, CompressionSpec): diff --git a/src/borg/helpers/jap_helpers.py b/src/borg/helpers/jap_helpers.py index 80691b379..a726a6d8e 100644 --- a/src/borg/helpers/jap_helpers.py +++ b/src/borg/helpers/jap_helpers.py @@ -1,6 +1,10 @@ -from jsonargparse import Namespace from typing import Any +# here are the only imports from argparse and jsonargparse, +# all other imports of these names import them from here: +from argparse import Action, ArgumentError, ArgumentTypeError, RawDescriptionHelpFormatter # noqa: F401 +from jsonargparse import ArgumentParser, Namespace, SUPPRESS, REMAINDER # noqa: F401 + def flatten_namespace(ns: Any) -> Namespace: """ diff --git a/src/borg/helpers/parseformat.py b/src/borg/helpers/parseformat.py index 87b4b731e..aaee4816a 100644 --- a/src/borg/helpers/parseformat.py +++ b/src/borg/helpers/parseformat.py @@ -1,5 +1,4 @@ import abc -import argparse import base64 import binascii import hashlib @@ -24,6 +23,7 @@ logger = create_logger() from .errors import Error from .fs import get_keys_dir, make_path_safe, slashify +from .jap_helpers import Action, ArgumentError, ArgumentTypeError from .msgpack import Timestamp from .time import OutputTimestamp, format_time, safe_timestamp from .. import __version__ as borg_version @@ -124,7 +124,7 @@ def positive_int_validator(value): """argparse type for positive integers, N > 0.""" int_value = int(value) if int_value <= 0: - raise argparse.ArgumentTypeError("A positive integer is required: %s" % value) + raise ArgumentTypeError("A positive integer is required: %s" % value) return int_value @@ -152,7 +152,7 @@ def interval(s): number = s[:-1] suffix = s[-1] else: - raise argparse.ArgumentTypeError(f'Unexpected time unit "{s[-1]}": choose from {", ".join(multiplier)}') + raise ArgumentTypeError(f'Unexpected time unit "{s[-1]}": choose from {", ".join(multiplier)}') try: seconds = int(number) * multiplier[suffix] @@ -160,7 +160,7 @@ def interval(s): seconds = -1 if seconds <= 0: - raise argparse.ArgumentTypeError(f'Invalid number "{number}": expected positive integer') + raise ArgumentTypeError(f'Invalid number "{number}": expected positive integer') return seconds @@ -171,7 +171,7 @@ def ChunkerParams(s): params = s.strip().split(",") count = len(params) if count == 0: - raise argparse.ArgumentTypeError("no chunker params given") + raise ArgumentTypeError("no chunker params given") algo = params[0].lower() if algo == CH_FAIL and count == 3: block_size = int(params[1]) @@ -186,47 +186,37 @@ def ChunkerParams(s): # or in-memory chunk management. # choose the block (chunk) size wisely: if you have a lot of data and you cut # it into very small chunks, you are asking for trouble! - raise argparse.ArgumentTypeError("block_size must not be less than 64 Bytes") + raise ArgumentTypeError("block_size must not be less than 64 Bytes") if block_size > MAX_DATA_SIZE or header_size > MAX_DATA_SIZE: - raise argparse.ArgumentTypeError( - "block_size and header_size must not exceed MAX_DATA_SIZE [%d]" % MAX_DATA_SIZE - ) + raise ArgumentTypeError("block_size and header_size must not exceed MAX_DATA_SIZE [%d]" % MAX_DATA_SIZE) return algo, block_size, header_size if algo == "default" and count == 1: # default return CHUNKER_PARAMS if algo == CH_BUZHASH64 and count == 5: # buzhash64, chunk_min, chunk_max, chunk_mask, window_size chunk_min, chunk_max, chunk_mask, window_size = (int(p) for p in params[1:]) if not (chunk_min <= chunk_mask <= chunk_max): - raise argparse.ArgumentTypeError("required: chunk_min <= chunk_mask <= chunk_max") + raise ArgumentTypeError("required: chunk_min <= chunk_mask <= chunk_max") if chunk_min < 6: # see comment in 'fixed' algo check - raise argparse.ArgumentTypeError( - "min. chunk size exponent must not be less than 6 (2^6 = 64B min. chunk size)" - ) + raise ArgumentTypeError("min. chunk size exponent must not be less than 6 (2^6 = 64B min. chunk size)") if chunk_max > 23: - raise argparse.ArgumentTypeError( - "max. chunk size exponent must not be more than 23 (2^23 = 8MiB max. chunk size)" - ) + raise ArgumentTypeError("max. chunk size exponent must not be more than 23 (2^23 = 8MiB max. chunk size)") # note that for buzhash64, there is no problem with even window_size. return CH_BUZHASH64, chunk_min, chunk_max, chunk_mask, window_size # this must stay last as it deals with old-style compat mode (no algorithm, 4 params, buzhash): if algo == CH_BUZHASH and count == 5 or count == 4: # [buzhash, ]chunk_min, chunk_max, chunk_mask, window_size chunk_min, chunk_max, chunk_mask, window_size = (int(p) for p in params[count - 4 :]) if not (chunk_min <= chunk_mask <= chunk_max): - raise argparse.ArgumentTypeError("required: chunk_min <= chunk_mask <= chunk_max") + raise ArgumentTypeError("required: chunk_min <= chunk_mask <= chunk_max") if chunk_min < 6: # see comment in 'fixed' algo check - raise argparse.ArgumentTypeError( - "min. chunk size exponent must not be less than 6 (2^6 = 64B min. chunk size)" - ) + raise ArgumentTypeError("min. chunk size exponent must not be less than 6 (2^6 = 64B min. chunk size)") if chunk_max > 23: - raise argparse.ArgumentTypeError( - "max. chunk size exponent must not be more than 23 (2^23 = 8MiB max. chunk size)" - ) + raise ArgumentTypeError("max. chunk size exponent must not be more than 23 (2^23 = 8MiB max. chunk size)") if window_size % 2 == 0: - raise argparse.ArgumentTypeError("window_size must be an uneven (odd) number") + raise ArgumentTypeError("window_size must be an uneven (odd) number") return CH_BUZHASH, chunk_min, chunk_max, chunk_mask, window_size - raise argparse.ArgumentTypeError("invalid chunker params") + raise ArgumentTypeError("invalid chunker params") def FilesCacheMode(s): @@ -236,13 +226,11 @@ def FilesCacheMode(s): return s entries = set(s.strip().split(",")) if not entries <= set(ENTRIES_MAP): - raise argparse.ArgumentTypeError( - "cache mode must be a comma-separated list of: %s" % ",".join(sorted(ENTRIES_MAP)) - ) + raise ArgumentTypeError("cache mode must be a comma-separated list of: %s" % ",".join(sorted(ENTRIES_MAP))) short_entries = {ENTRIES_MAP[entry] for entry in entries} mode = "".join(sorted(short_entries)) if mode not in VALID_MODES: - raise argparse.ArgumentTypeError("cache mode short must be one of: %s" % ",".join(VALID_MODES)) + raise ArgumentTypeError("cache mode short must be one of: %s" % ",".join(VALID_MODES)) return mode @@ -338,13 +326,13 @@ replace_placeholders = PlaceholderReplacer() def PathSpec(text): if not text: - raise argparse.ArgumentTypeError("Empty strings are not accepted as paths.") + raise ArgumentTypeError("Empty strings are not accepted as paths.") return text def FilesystemPathSpec(text): if not text: - raise argparse.ArgumentTypeError("Empty strings are not accepted as paths.") + raise ArgumentTypeError("Empty strings are not accepted as paths.") return slashify(text) @@ -353,7 +341,7 @@ def SortBySpec(text): for sort_key in text.split(","): if sort_key not in AI_HUMAN_SORT_KEYS and sort_key != "ts": # idempotency: do not reject ts - raise argparse.ArgumentTypeError("Invalid sort key: %s" % sort_key) + raise ArgumentTypeError("Invalid sort key: %s" % sort_key) return text.replace("timestamp", "ts").replace("archive", "name") @@ -643,12 +631,12 @@ def location_validator(proto=None, other=False): try: loc = Location(text, other=other) except ValueError as err: - raise argparse.ArgumentTypeError(str(err)) from None + raise ArgumentTypeError(str(err)) from None if proto is not None and loc.proto != proto: if proto == "file": - raise argparse.ArgumentTypeError('"%s": Repository must be local' % text) + raise ArgumentTypeError('"%s": Repository must be local' % text) else: - raise argparse.ArgumentTypeError('"%s": Repository must be remote' % text) + raise ArgumentTypeError('"%s": Repository must be remote' % text) return loc return validator @@ -658,7 +646,7 @@ def relative_time_marker_validator(text: str): time_marker_regex = r"^\d+[ymwdHMS]$" match = re.compile(time_marker_regex).search(text) if not match: - raise argparse.ArgumentTypeError(f"Invalid relative time marker used: {text}, choose from y, m, w, d, H, M, S") + raise ArgumentTypeError(f"Invalid relative time marker used: {text}, choose from y, m, w, d, H, M, S") else: return text @@ -667,22 +655,20 @@ def text_validator(*, name, max_length, min_length=0, invalid_ctrl_chars="\0", i def validator(text): assert isinstance(text, str) if len(text) < min_length: - raise argparse.ArgumentTypeError(f'Invalid {name}: "{text}" [length < {min_length}]') + raise ArgumentTypeError(f'Invalid {name}: "{text}" [length < {min_length}]') if len(text) > max_length: - raise argparse.ArgumentTypeError(f'Invalid {name}: "{text}" [length > {max_length}]') + raise ArgumentTypeError(f'Invalid {name}: "{text}" [length > {max_length}]') if invalid_ctrl_chars and re.search(f"[{re.escape(invalid_ctrl_chars)}]", text): - raise argparse.ArgumentTypeError(f'Invalid {name}: "{text}" [invalid control chars detected]') + raise ArgumentTypeError(f'Invalid {name}: "{text}" [invalid control chars detected]') if invalid_chars and re.search(f"[{re.escape(invalid_chars)}]", text): - raise argparse.ArgumentTypeError( - f'Invalid {name}: "{text}" [invalid chars detected matching "{invalid_chars}"]' - ) + raise ArgumentTypeError(f'Invalid {name}: "{text}" [invalid chars detected matching "{invalid_chars}"]') if no_blanks and (text.startswith(" ") or text.endswith(" ")): - raise argparse.ArgumentTypeError(f'Invalid {name}: "{text}" [leading or trailing blanks detected]') + raise ArgumentTypeError(f'Invalid {name}: "{text}" [leading or trailing blanks detected]') try: text.encode("utf-8", errors="strict") except UnicodeEncodeError: # looks like text contains surrogate-escapes - raise argparse.ArgumentTypeError(f'Invalid {name}: "{text}" [contains non-unicode characters]') + raise ArgumentTypeError(f'Invalid {name}: "{text}" [contains non-unicode characters]') return text return validator @@ -1318,7 +1304,7 @@ def prepare_dump_dict(d): return decode(d) -class Highlander(argparse.Action): +class Highlander(Action): """make sure some option is only given once""" def __init__(self, *args, **kwargs): @@ -1327,7 +1313,7 @@ class Highlander(argparse.Action): def __call__(self, parser, namespace, values, option_string=None): if self.__called: - raise argparse.ArgumentError(self, "There can be only one.") + raise ArgumentError(self, "There can be only one.") self.__called = True setattr(namespace, self.dest, values) @@ -1337,7 +1323,7 @@ class MakePathSafeAction(Highlander): try: sanitized_path = make_path_safe(path) except ValueError as e: - raise argparse.ArgumentError(self, e) + raise ArgumentError(self, e) if sanitized_path == ".": - raise argparse.ArgumentError(self, f"{path!r} is not a valid file name") + raise ArgumentError(self, f"{path!r} is not a valid file name") setattr(namespace, self.dest, sanitized_path) diff --git a/src/borg/patterns.py b/src/borg/patterns.py index 8c03987b7..e382e5314 100644 --- a/src/borg/patterns.py +++ b/src/borg/patterns.py @@ -1,4 +1,3 @@ -import argparse import fnmatch import posixpath import re @@ -8,6 +7,7 @@ from collections import namedtuple from enum import Enum from .helpers import clean_lines, shellpattern +from .helpers.jap_helpers import Action, ArgumentTypeError from .helpers.errors import Error @@ -36,7 +36,7 @@ def load_exclude_file(fileobj, patterns): patterns.append(parse_exclude_pattern(patternstr)) -class ArgparsePatternAction(argparse.Action): +class ArgparsePatternAction(Action): def __init__(self, nargs=1, **kw): super().__init__(nargs=nargs, **kw) @@ -44,7 +44,7 @@ class ArgparsePatternAction(argparse.Action): parse_patternfile_line(values[0], args.pattern_roots, args.patterns, ShellPattern) -class ArgparsePatternFileAction(argparse.Action): +class ArgparsePatternFileAction(Action): def __init__(self, nargs=1, **kw): super().__init__(nargs=nargs, **kw) @@ -357,16 +357,16 @@ def parse_inclexcl_command(cmd_line_str, fallback=ShellPattern): "p": IECommand.PatternStyle, } if not cmd_line_str: - raise argparse.ArgumentTypeError("A pattern/command must not be empty.") + raise ArgumentTypeError("A pattern/command must not be empty.") cmd = cmd_prefix_map.get(cmd_line_str[0]) if cmd is None: - raise argparse.ArgumentTypeError("A pattern/command must start with any of: %s" % ", ".join(cmd_prefix_map)) + raise ArgumentTypeError("A pattern/command must start with any of: %s" % ", ".join(cmd_prefix_map)) # remaining text on command-line following the command character remainder_str = cmd_line_str[1:].lstrip() if not remainder_str: - raise argparse.ArgumentTypeError("A pattern/command must have a value part.") + raise ArgumentTypeError("A pattern/command must have a value part.") if cmd is IECommand.RootPath: # TODO: validate string? @@ -376,7 +376,7 @@ def parse_inclexcl_command(cmd_line_str, fallback=ShellPattern): try: val = get_pattern_class(remainder_str) except ValueError: - raise argparse.ArgumentTypeError(f"Invalid pattern style: {remainder_str}") + raise ArgumentTypeError(f"Invalid pattern style: {remainder_str}") else: # determine recurse_dir based on command type recurse_dir = command_recurses_dir(cmd) diff --git a/src/borg/testsuite/archiver/argparsing_test.py b/src/borg/testsuite/archiver/argparsing_test.py index 58d09076d..afc706b2c 100644 --- a/src/borg/testsuite/archiver/argparsing_test.py +++ b/src/borg/testsuite/archiver/argparsing_test.py @@ -1,9 +1,7 @@ -import argparse import pytest -from jsonargparse import ArgumentParser - from . import Archiver, RK_ENCRYPTION, cmd +from ...helpers.jap_helpers import ArgumentParser, RawDescriptionHelpFormatter, flatten_namespace def test_bad_filters(archiver): @@ -118,14 +116,12 @@ class TestCommonOptions: @pytest.fixture def parse_vars_from_line(self, parser, subcommands, common_parser): - from ...helpers.jap_helpers import flatten_namespace - subparser = ArgumentParser( parents=[common_parser], add_help=False, description="foo", epilog="bar", - formatter_class=argparse.RawDescriptionHelpFormatter, + formatter_class=RawDescriptionHelpFormatter, ) subparser.add_argument("--foo-bar", dest="foo_bar", action="store_true") subcommands.add_subcommand("subcmd", subparser, help="baz") diff --git a/src/borg/testsuite/compress_test.py b/src/borg/testsuite/compress_test.py index 9ec9f1046..ea90d76dc 100644 --- a/src/borg/testsuite/compress_test.py +++ b/src/borg/testsuite/compress_test.py @@ -1,4 +1,3 @@ -import argparse import os import zlib @@ -6,6 +5,7 @@ import pytest from ..compress import get_compressor, Compressor, CompressionSpec, CNONE, ZLIB, LZ4, LZMA, ZSTD, Auto from ..constants import ROBJ_FILE_STREAM, ROBJ_ARCHIVE_META +from ..helpers.jap_helpers import ArgumentTypeError DATA = b"fooooooooobaaaaaaaar" * 10 params = dict(name="zlib", level=6) @@ -209,7 +209,7 @@ def test_specified_compression_level(c_type, c_name, c_levels): @pytest.mark.parametrize("invalid_spec", ["", "lzma,9,invalid", "invalid"]) def test_invalid_compression_level(invalid_spec): - with pytest.raises(argparse.ArgumentTypeError): + with pytest.raises(ArgumentTypeError): CompressionSpec(invalid_spec) diff --git a/src/borg/testsuite/helpers/parseformat_test.py b/src/borg/testsuite/helpers/parseformat_test.py index c90c55920..9c356fe26 100644 --- a/src/borg/testsuite/helpers/parseformat_test.py +++ b/src/borg/testsuite/helpers/parseformat_test.py @@ -1,11 +1,12 @@ import base64 import os -from argparse import ArgumentTypeError + from datetime import datetime, timezone import pytest from ...constants import * # NOQA +from ...helpers.jap_helpers import ArgumentTypeError from ...helpers.parseformat import ( bin_to_hex, binary_to_json, diff --git a/src/borg/testsuite/patterns_test.py b/src/borg/testsuite/patterns_test.py index f6fd602c5..b434e4540 100644 --- a/src/borg/testsuite/patterns_test.py +++ b/src/borg/testsuite/patterns_test.py @@ -1,10 +1,10 @@ -import argparse import io import os.path import sys import pytest +from ..helpers.jap_helpers import ArgumentTypeError from ..patterns import PathFullPattern, PathPrefixPattern, FnmatchPattern, ShellPattern, RegexPattern from ..patterns import load_exclude_file, load_pattern_file from ..patterns import parse_pattern, PatternMatcher @@ -491,7 +491,7 @@ def test_load_invalid_patterns_from_file(tmpdir, lines): with patternfile.open("wt") as fh: fh.write("\n".join(lines)) filename = str(patternfile) - with pytest.raises(argparse.ArgumentTypeError): + with pytest.raises(ArgumentTypeError): roots = [] inclexclpatterns = [] with open(filename) as f: