Updated aiohttp v3.12.12 -> v3.12.13 Updated attrs v23.1.0 -> v23.2.0 Updated blessed v1.19.1 -> v1.21.0 Updated cbor2 v4.0.1 -> v4.1.2 Updated compare-locales v9.0.1 -> v9.0.4 Updated distro v1.8.0 -> v1.9.0 Updated fluent-migrate v0.13.2 -> v0.13.3 Updated importlib-metadata v6.0.0 -> v6.11.0 Updated jsmin v3.0.0 -> v3.0.1 Updated json-e v4.5.3 -> v4.8.0 Updated looseversion v1.0.1 -> v1.3.0 Updated mozilla-taskgraph v3.3.1 -> v3.3.2 Updated multidict v6.4.4 -> v6.5.0 Updated pathspec v0.9.0 -> v0.12.1 Updated pyasn1 v0.4.8 -> v0.6.1 Updated pyasn1-modules v0.2.8 -> v0.4.2 Updated pylru v1.0.9 -> v1.2.1 Updated python-hglib v2.4 -> v2.6.2 Updated redo v2.0.3 -> v2.0.4 Updated requests-unixsocket v0.2.0 -> v0.4.1 Updated responses v0.10.6 -> v0.25.7 Updated rsa v4.9 -> v4.9.1 Updated sentry-sdk v1.14.0 -> v1.45.1 Updated six v1.16.0 -> v1.17.0 Updated tomlkit v0.12.3 -> v0.13.3 Updated tqdm v4.66.3 -> v4.67.1 Updated typing-extensions v4.12.2 -> v4.14.0 Updated voluptuous v0.12.1 -> v0.15.2 Updated yamllint v1.23.0 -> v1.37.1 Differential Revision: https://phabricator.services.mozilla.com/D254096
135 lines
3.6 KiB
Python
135 lines
3.6 KiB
Python
from __future__ import absolute_import, print_function, unicode_literals
|
|
|
|
import re
|
|
import datetime
|
|
|
|
|
|
class DeleteMarker:
|
|
pass
|
|
|
|
|
|
class JSONTemplateError(Exception):
|
|
def __init__(self, message):
|
|
super(JSONTemplateError, self).__init__(message)
|
|
self.location = []
|
|
|
|
def add_location(self, loc):
|
|
self.location.insert(0, loc)
|
|
|
|
def __str__(self):
|
|
location = " at template" + "".join(self.location)
|
|
return "{}{}: {}".format(
|
|
self.__class__.__name__, location if self.location else "", self.args[0]
|
|
)
|
|
|
|
|
|
class TemplateError(JSONTemplateError):
|
|
pass
|
|
|
|
|
|
class InterpreterError(JSONTemplateError):
|
|
pass
|
|
|
|
|
|
# Regular expression matching: X days Y hours Z minutes
|
|
FROMNOW_RE = re.compile(
|
|
"".join(
|
|
[
|
|
r"^(\s*(?P<years>\d+)\s*(years|year|yr|y))?",
|
|
r"(\s*(?P<months>\d+)\s*(months|month|mo))?",
|
|
r"(\s*(?P<weeks>\d+)\s*(weeks|week|wk|w))?",
|
|
r"(\s*(?P<days>\d+)\s*(days|day|d))?",
|
|
r"(\s*(?P<hours>\d+)\s*(hours|hour|hr|h))?",
|
|
r"(\s*(?P<minutes>\d+)\s*(minutes|minute|min|m))?",
|
|
r"(\s*(?P<seconds>\d+)\s*(seconds|second|sec|s))?\s*$",
|
|
]
|
|
)
|
|
)
|
|
|
|
|
|
def fromNow(offset, reference):
|
|
# copied from taskcluster-client.py
|
|
# We want to handle past dates as well as future
|
|
future = True
|
|
offset = offset.lstrip()
|
|
if offset.startswith("-"):
|
|
future = False
|
|
offset = offset[1:].lstrip()
|
|
if offset.startswith("+"):
|
|
offset = offset[1:].lstrip()
|
|
|
|
# Parse offset
|
|
m = FROMNOW_RE.match(offset)
|
|
if m is None:
|
|
raise TemplateError("offset string: '%s' does not parse" % offset)
|
|
|
|
# In order to calculate years and months we need to calculate how many days
|
|
# to offset the offset by, since timedelta only goes as high as weeks
|
|
days = 0
|
|
hours = 0
|
|
minutes = 0
|
|
seconds = 0
|
|
if m.group("years"):
|
|
# forget leap years, a year is 365 days
|
|
years = int(m.group("years"))
|
|
days += 365 * years
|
|
if m.group("months"):
|
|
# assume "month" means 30 days
|
|
months = int(m.group("months"))
|
|
days += 30 * months
|
|
days += int(m.group("days") or 0)
|
|
hours += int(m.group("hours") or 0)
|
|
minutes += int(m.group("minutes") or 0)
|
|
seconds += int(m.group("seconds") or 0)
|
|
|
|
# Offset datetime from utc
|
|
delta = datetime.timedelta(
|
|
weeks=int(m.group("weeks") or 0),
|
|
days=days,
|
|
hours=hours,
|
|
minutes=minutes,
|
|
seconds=seconds,
|
|
)
|
|
|
|
if isinstance(reference, string):
|
|
reference = datetime.datetime.strptime(reference, "%Y-%m-%dT%H:%M:%S.%fZ")
|
|
elif reference is None:
|
|
reference = datetime.datetime.utcnow()
|
|
return stringDate(reference + delta if future else reference - delta)
|
|
|
|
|
|
datefmt_re = re.compile(r"(\.[0-9]{3})[0-9]*(\+00:00)?")
|
|
|
|
|
|
def to_str(v):
|
|
if isinstance(v, bool):
|
|
return {True: "true", False: "false"}[v]
|
|
elif isinstance(v, list):
|
|
return ",".join(to_str(e) for e in v)
|
|
elif v is None:
|
|
return "null"
|
|
elif isinstance(v, string):
|
|
return v
|
|
else:
|
|
return str(v)
|
|
|
|
|
|
def stringDate(date):
|
|
# Convert to isoFormat
|
|
try:
|
|
string = date.isoformat(timespec="microseconds")
|
|
# py2.7 to py3.5 does not have timespec
|
|
except TypeError as e:
|
|
string = date.isoformat()
|
|
if string.find(".") == -1:
|
|
string += ".000"
|
|
string = datefmt_re.sub(r"\1Z", string)
|
|
return string
|
|
|
|
|
|
# the base class for strings, regardless of python version
|
|
try:
|
|
string = basestring
|
|
except NameError:
|
|
string = str
|