|
4 | 4 |
|
5 | 5 | import datetime
|
6 | 6 | import re
|
| 7 | +import sys |
7 | 8 |
|
8 | 9 | from urllib.parse import urlencode
|
9 | 10 |
|
|
25 | 26 |
|
26 | 27 | OMITTED_APPS_APIS = ["ietf.status"]
|
27 | 28 |
|
| 29 | +# Pre-py3.11, fromisoformat() does not handle Z or +HH tz offsets |
| 30 | +HAVE_BROKEN_FROMISOFORMAT = sys.version_info < (3, 11, 0, "", 0) |
| 31 | + |
28 | 32 | def populate_api_list():
|
29 | 33 | _module_dict = globals()
|
30 | 34 | for app_config in django_apps.get_app_configs():
|
@@ -58,6 +62,35 @@ def generate_cache_key(self, *args, **kwargs):
|
58 | 62 | # Use a list plus a ``.join()`` because it's faster than concatenation.
|
59 | 63 | return "%s:%s:%s:%s" % (self._meta.api_name, self._meta.resource_name, ':'.join(args), smooshed)
|
60 | 64 |
|
| 65 | + def _z_aware_fromisoformat(self, value): |
| 66 | + """datetime.datetie.fromisoformat replacement that works with python < 3.11""" |
| 67 | + if HAVE_BROKEN_FROMISOFORMAT: |
| 68 | + if value.upper().endswith("Z"): |
| 69 | + value = value[:-1] + "+00:00" # Z -> UTC |
| 70 | + elif re.match(r"[+-][0-9][0-9]$", value[-3:]): |
| 71 | + value = value + ":00" # -04 -> -04:00 |
| 72 | + return value |
| 73 | + |
| 74 | + def filter_value_to_python( |
| 75 | + self, value, field_name, filters, filter_expr, filter_type |
| 76 | + ): |
| 77 | + py_value = super().filter_value_to_python( |
| 78 | + value, field_name, filters, filter_expr, filter_type |
| 79 | + ) |
| 80 | + if isinstance( |
| 81 | + self.fields[field_name], tastypie.fields.DateTimeField |
| 82 | + ) and isinstance(py_value, str): |
| 83 | + # Ensure datetime values are TZ-aware, using UTC by default |
| 84 | + try: |
| 85 | + dt = self._z_aware_fromisoformat(py_value) |
| 86 | + except ValueError: |
| 87 | + pass # let tastypie deal with the original value |
| 88 | + else: |
| 89 | + if dt.tzinfo is None: |
| 90 | + dt = dt.replace(tzinfo=datetime.timezone.utc) |
| 91 | + py_value = dt.isoformat() |
| 92 | + return py_value |
| 93 | + |
61 | 94 |
|
62 | 95 | TIMEDELTA_REGEX = re.compile(r'^(?P<days>\d+d)?\s?(?P<hours>\d+h)?\s?(?P<minutes>\d+m)?\s?(?P<seconds>\d+s?)$')
|
63 | 96 |
|
|
0 commit comments