Merge pull request #7013 from ThomasWaldmann/replace-placeholders

refactor replace_placeholders, fixes #6966
This commit is contained in:
TW 2022-09-10 00:23:44 +02:00 committed by GitHub
commit 9ba03f0468
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 46 additions and 23 deletions

View file

@ -15,7 +15,7 @@ try:
import shlex
import signal
import time
from datetime import datetime
from datetime import datetime, timezone
from ..logger import create_logger, setup_logging
@ -27,6 +27,7 @@ try:
from ..helpers import Error, set_ec
from ..helpers import format_file_size
from ..helpers import remove_surrogates
from ..helpers import DatetimeWrapper, replace_placeholders
from ..helpers import check_python, check_extension_modules
from ..helpers import is_slow_msgpack, is_supported_msgpack, sysinfo
from ..helpers import signal_handler, raising_signal_handler, SigHup, SigTerm
@ -402,8 +403,18 @@ class Archiver(
}
if func not in bypass_allowed:
raise Error("Not allowed to bypass locking mechanism for chosen command")
# we can only have a complete knowledge of placeholder replacements we should do **after** arg parsing,
# e.g. due to options like --timestamp that override the current time.
# thus we have to initialize replace_placeholders here and process all args that need placeholder replacement.
if getattr(args, "timestamp", None):
replace_placeholders.override("now", DatetimeWrapper(args.timestamp))
replace_placeholders.override("utcnow", DatetimeWrapper(args.timestamp.astimezone(timezone.utc)))
args.location = args.location.with_timestamp(args.timestamp)
for name in "name", "other_name", "newname", "glob_archives", "comment":
value = getattr(args, name, None)
if value is not None:
setattr(args, name, replace_placeholders(value))
return args
def prerun_checks(self, logger, is_serve):

View file

@ -8,7 +8,7 @@ from ..archive import Archive
from ..constants import * # NOQA
from ..cache import Cache, assert_secure
from ..helpers import Error
from ..helpers import GlobSpec, SortBySpec, positive_int_validator, location_validator, Location
from ..helpers import SortBySpec, positive_int_validator, location_validator, Location
from ..helpers.nanorst import rst_to_terminal
from ..manifest import Manifest, AI_HUMAN_SORT_KEYS
from ..patterns import PatternMatcher
@ -363,7 +363,6 @@ def define_archive_filters_group(subparser, *, sort_by=True, first_last=True):
"--glob-archives",
metavar="GLOB",
dest="glob_archives",
type=GlobSpec,
action=Highlander,
help="only consider archive names matching the glob. " 'sh: rules apply, see "borg help patterns".',
)

View file

@ -17,7 +17,7 @@ from ..cache import Cache
from ..constants import * # NOQA
from ..compress import CompressionSpec
from ..helpers import ChunkerParams
from ..helpers import NameSpec, CommentSpec, FilesCacheMode
from ..helpers import NameSpec, FilesCacheMode
from ..helpers import eval_escapes
from ..helpers import timestamp
from ..helpers import get_cache_dir, os_stat
@ -806,12 +806,7 @@ class CreateMixIn:
archive_group = subparser.add_argument_group("Archive options")
archive_group.add_argument(
"--comment",
dest="comment",
metavar="COMMENT",
type=CommentSpec,
default="",
help="add a comment text to the archive",
"--comment", dest="comment", metavar="COMMENT", default="", help="add a comment text to the archive"
)
archive_group.add_argument(
"--timestamp",

View file

@ -6,7 +6,6 @@ from ..archive import ArchiveRecreater
from ..constants import * # NOQA
from ..compress import CompressionSpec
from ..helpers import archivename_validator, ChunkerParams
from ..helpers import CommentSpec
from ..helpers import timestamp
from ..manifest import Manifest
@ -162,12 +161,7 @@ class RecreateMixIn:
help="write checkpoint every SECONDS seconds (Default: 1800)",
)
archive_group.add_argument(
"--comment",
dest="comment",
metavar="COMMENT",
type=CommentSpec,
default=None,
help="add a comment text to the archive",
"--comment", dest="comment", metavar="COMMENT", default=None, help="add a comment text to the archive"
)
archive_group.add_argument(
"--timestamp",

View file

@ -24,7 +24,7 @@ from .parseformat import ChunkerParams, FilesCacheMode, partial_format, Datetime
from .parseformat import format_file_size, parse_file_size, FileSize, parse_storage_quota
from .parseformat import sizeof_fmt, sizeof_fmt_iec, sizeof_fmt_decimal
from .parseformat import format_line, replace_placeholders, PlaceholderError
from .parseformat import PrefixSpec, GlobSpec, CommentSpec, SortBySpec, NameSpec
from .parseformat import SortBySpec, NameSpec
from .parseformat import format_archive, parse_stringified_list, clean_lines
from .parseformat import Location, location_validator, archivename_validator
from .parseformat import BaseFormatter, ArchiveFormatter, ItemFormatter, file_status

View file

@ -185,7 +185,7 @@ def format_line(format, data):
raise PlaceholderError(format, data, e.__class__.__name__, str(e))
def replace_placeholders(text, overrides={}):
def _replace_placeholders(text, overrides={}):
"""Replace placeholders in text with their values."""
from ..platform import fqdn, hostname, getosusername
@ -208,13 +208,26 @@ def replace_placeholders(text, overrides={}):
return format_line(text, data)
PrefixSpec = replace_placeholders
class PlaceholderReplacer:
def __init__(self):
self.reset()
GlobSpec = replace_placeholders
def override(self, key, value):
self.overrides[key] = value
NameSpec = replace_placeholders
def reset(self):
self.overrides = {}
CommentSpec = replace_placeholders
def __call__(self, text, overrides=None):
ovr = {}
ovr.update(self.overrides)
ovr.update(overrides or {})
return _replace_placeholders(text, overrides=ovr)
replace_placeholders = PlaceholderReplacer()
NameSpec = str
def SortBySpec(text):

View file

@ -1393,6 +1393,16 @@ class ArchiverTestCase(ArchiverTestCaseBase):
self.cmd(f"--repo={self.repository_location}", "rinfo")
self.cmd(f"--repo={self.repository_location}", "check")
def test_create_archivename_with_placeholder(self):
self.create_test_files()
self.cmd(f"--repo={self.repository_location}", "rcreate", RK_ENCRYPTION)
ts = "1999-12-31T23:59:59"
name_given = "test-{now}" # placeholder in archive name gets replaced by borg
name_expected = f"test-{ts}" # placeholder in f-string gets replaced by python
self.cmd(f"--repo={self.repository_location}", "create", f"--timestamp={ts}", name_given, "input")
list_output = self.cmd(f"--repo={self.repository_location}", "rlist", "--short")
assert name_expected in list_output
def test_extract_pattern_opt(self):
self.cmd(f"--repo={self.repository_location}", "rcreate", RK_ENCRYPTION)
self.create_regular_file("file1", size=1024 * 80)

View file

@ -920,6 +920,7 @@ def test_format_line_erroneous():
def test_replace_placeholders():
replace_placeholders.reset() # avoid overrides are spoiled by previous tests
now = datetime.now()
assert " " not in replace_placeholders("{now}")
assert int(replace_placeholders("{now:%Y}")) == now.year