Skip to content

Commit d1d4330

Browse files
WIP2
1 parent 822ab66 commit d1d4330

File tree

3 files changed

+57
-15
lines changed

3 files changed

+57
-15
lines changed

src/borg/archiver/_common.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@
1717
from ..repository import Repository
1818
from ..repoobj import RepoObj, RepoObj1
1919
from ..patterns import (
20-
ArgparseExcludePatternAction,
2120
ArgparsePatternAction,
2221
ArgparseExcludeFileAction,
2322
ArgparsePatternFileAction,
23+
parse_exclude_pattern,
2424
)
2525

2626

@@ -272,7 +272,8 @@ def define_exclude_and_patterns(add_option, *, tag_files=False, strip_components
272272
"--exclude",
273273
metavar="PATTERN",
274274
dest="patterns",
275-
action=ArgparseExcludePatternAction,
275+
type=parse_exclude_pattern,
276+
action="append",
276277
help="exclude paths matching PATTERN",
277278
)
278279
add_option(
@@ -302,6 +303,7 @@ def define_exclude_and_patterns(add_option, *, tag_files=False, strip_components
302303
"--exclude-if-present",
303304
metavar="NAME",
304305
dest="exclude_if_present",
306+
type=str,
305307
action="append",
306308
help="exclude directories that are tagged by containing a filesystem object with the given NAME",
307309
)

src/borg/helpers/jap_wrapper.py

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33
This module provides a compatibility layer between Borg's argparse patterns
44
and jsonargparse's API. Key adaptations:
55
6-
1. Namespace flattening: jsonargparse creates nested namespaces for subcommands
6+
1. type+action combination: jsonargparse forbids combining type= and action=
7+
in add_argument(). Our override keeps type= at call sites for readability
8+
and wraps both into a composite action class that jsonargparse accepts.
9+
10+
2. Namespace flattening: jsonargparse creates nested namespaces for subcommands
711
(args.create.name instead of args.name). flatten_namespace() merges these
812
into a flat namespace compatible with Borg's command handlers.
913
"""
@@ -14,13 +18,58 @@
1418
from jsonargparse._core import ArgumentGroup as _JAPArgumentGroup
1519

1620

17-
class ArgumentGroup(_JAPArgumentGroup):
18-
"""ArgumentGroup for Borg."""
21+
def _make_type_converting_action(base_action_name, type_fn):
22+
"""Create a custom action class that wraps a standard action and applies type conversion.
23+
24+
jsonargparse forbids type+action, so we strip both from kwargs and replace
25+
them with a single composite action class that does type conversion + action.
26+
"""
27+
_action_map = {"append": argparse._AppendAction, "store": argparse._StoreAction}
28+
base_cls = _action_map.get(base_action_name)
29+
if base_cls is None:
30+
return None
31+
32+
class TypeConvertingAction(base_cls):
33+
def __call__(self, parser, namespace, values, option_string=None):
34+
if type_fn is not None and isinstance(values, str):
35+
try:
36+
values = type_fn(values)
37+
except argparse.ArgumentTypeError as e:
38+
raise argparse.ArgumentError(self, str(e))
39+
super().__call__(parser, namespace, values, option_string)
40+
41+
TypeConvertingAction.__name__ = f"TypeConverting{base_action_name.title()}Action"
42+
return TypeConvertingAction
43+
44+
45+
class BorgAddArgumentMixin:
46+
"""Mixin that handles the type+action combination jsonargparse forbids.
47+
48+
Call sites can use both type= and action= naturally (e.g. type=parse_exclude_pattern,
49+
action="append"). This mixin intercepts the call, strips both, and creates a
50+
composite action class that jsonargparse accepts.
51+
"""
52+
53+
def add_argument(self, *args, **kwargs):
54+
action = kwargs.get("action")
55+
if action is not None and "type" in kwargs and isinstance(action, str):
56+
type_fn = kwargs.pop("type")
57+
wrapper = _make_type_converting_action(action, type_fn)
58+
if wrapper is not None:
59+
kwargs["action"] = wrapper
60+
else:
61+
# Unknown action string, put type back and hope for the best.
62+
kwargs["type"] = type_fn
63+
return super().add_argument(*args, **kwargs)
64+
65+
66+
class ArgumentGroup(BorgAddArgumentMixin, _JAPArgumentGroup):
67+
"""ArgumentGroup that supports Borg's add_argument patterns."""
1968

2069
pass
2170

2271

23-
class ArgumentParser(_JAPArgumentParser):
72+
class ArgumentParser(BorgAddArgumentMixin, _JAPArgumentParser):
2473
"""ArgumentParser bridging Borg's argparse patterns with jsonargparse."""
2574

2675
def __init__(self, *args, **kwargs):

src/borg/patterns.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -75,15 +75,6 @@ def parse(self, fobj, args):
7575
load_pattern_file(fobj, ArgparsePatternFileAction.roots_from_patterns, args.patterns)
7676

7777

78-
class ArgparseExcludePatternAction(argparse.Action):
79-
"""Action for --exclude that parses and appends an exclude pattern."""
80-
81-
def __call__(self, parser, args, values, option_string=None):
82-
if args.patterns is None:
83-
args.patterns = []
84-
args.patterns.append(parse_exclude_pattern(values))
85-
86-
8778
class ArgparseExcludeFileAction(ArgparsePatternFileAction):
8879
def parse(self, fobj, args):
8980
# jsonargparse may initialize list-like attributes to None instead of []

0 commit comments

Comments
 (0)