Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/borg/archiver.py
Original file line number Diff line number Diff line change
Expand Up @@ -4457,6 +4457,8 @@ def parse_args(self, args=None):
self.do_list, self.do_mount, self.do_umount}
if func not in bypass_allowed:
raise Error('Not allowed to bypass locking mechanism for chosen command')
if getattr(args, 'timestamp', None):
args.location = args.location.with_timestamp(args.timestamp)
return args

def prerun_checks(self, logger, is_serve):
Expand Down
21 changes: 14 additions & 7 deletions src/borg/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,7 @@ def timestamp(s):
try:
# is it pointing to a file / directory?
ts = safe_s(os.stat(s).st_mtime)
return datetime.utcfromtimestamp(ts)
return datetime.fromtimestamp(ts, tz=timezone.utc)
except OSError:
# didn't work, try parsing as timestamp. UTC, no TZ, no microsecs support.
for format in ('%Y-%m-%dT%H:%M:%SZ', '%Y-%m-%dT%H:%M:%S+00:00',
Expand All @@ -615,7 +615,7 @@ def timestamp(s):
'%Y-%m-%d', '%Y-%j',
):
try:
return datetime.strptime(s, format)
return datetime.strptime(s, format).replace(tzinfo=timezone.utc)
except ValueError:
continue
raise ValueError
Expand Down Expand Up @@ -718,7 +718,7 @@ def format_line(format, data):
raise PlaceholderError(format, data, e.__class__.__name__, str(e))


def replace_placeholders(text):
def replace_placeholders(text, overrides={}):
"""Replace placeholders in text with their values."""
from .platform import fqdn, hostname
current_time = datetime.now(timezone.utc)
Expand All @@ -735,6 +735,7 @@ def replace_placeholders(text):
'borgmajor': '%d' % borg_version_tuple[:1],
'borgminor': '%d.%d' % borg_version_tuple[:2],
'borgpatch': '%d.%d.%d' % borg_version_tuple[:3],
**overrides,
}
return format_line(text, data)

Expand Down Expand Up @@ -1103,13 +1104,13 @@ class Location:
| # or
""" + optional_archive_re, re.VERBOSE) # archive name (optional, may be empty)

def __init__(self, text=''):
if not self.parse(text):
def __init__(self, text='', overrides={}):
if not self.parse(text, overrides):
raise ValueError('Invalid location format: "%s"' % self.orig)

def parse(self, text):
def parse(self, text, overrides={}):
self.orig = text
text = replace_placeholders(text)
text = replace_placeholders(text, overrides)
valid = self._parse(text)
if valid:
return True
Expand Down Expand Up @@ -1202,6 +1203,12 @@ def canonical_path(self):
':{}'.format(self.port) if self.port else '',
path)

def with_timestamp(self, timestamp):
return Location(self.orig, overrides={
'now': DatetimeWrapper(timestamp.astimezone(None)),
'utcnow': DatetimeWrapper(timestamp),
})


def location_validator(archive=None, proto=None):
def validator(text):
Expand Down
8 changes: 8 additions & 0 deletions src/borg/testsuite/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,10 @@ def test_user_parsing(self):
assert repr(Location('ssh://host/path::2016-12-31@23:59:59')) == \
"Location(proto='ssh', user=None, host='host', port=None, path='/path', archive='2016-12-31@23:59:59')"

def test_with_timestamp(self):
assert repr(Location('path::archive-{utcnow}').with_timestamp(datetime(2002, 9, 19, tzinfo=timezone.utc))) == \
"Location(proto='file', user=None, host=None, port=None, path='path', archive='archive-2002-09-19T00:00:00')"

def test_underspecified(self, monkeypatch):
monkeypatch.delenv('BORG_REPO', raising=False)
with pytest.raises(ValueError):
Expand Down Expand Up @@ -894,6 +898,10 @@ def test_replace_placeholders():
assert int(replace_placeholders('{now:%Y}')) == now.year


def test_override_placeholders():
assert replace_placeholders('{uuid4}', overrides={'uuid4': "overridden"}) == "overridden"


def working_swidth():
return platform.swidth('선') == 2

Expand Down