plain 0.14.1__py3-none-any.whl → 0.16.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- plain/internal/middleware/headers.py +6 -0
- plain/utils/timesince.py +79 -42
- {plain-0.14.1.dist-info → plain-0.16.0.dist-info}/METADATA +1 -1
- {plain-0.14.1.dist-info → plain-0.16.0.dist-info}/RECORD +7 -7
- {plain-0.14.1.dist-info → plain-0.16.0.dist-info}/WHEEL +0 -0
- {plain-0.14.1.dist-info → plain-0.16.0.dist-info}/entry_points.txt +0 -0
- {plain-0.14.1.dist-info → plain-0.16.0.dist-info}/licenses/LICENSE +0 -0
@@ -9,6 +9,12 @@ class DefaultHeadersMiddleware:
|
|
9
9
|
response = self.get_response(request)
|
10
10
|
|
11
11
|
for header, value in settings.DEFAULT_RESPONSE_HEADERS.items():
|
12
|
+
# Since we don't have a good way to *remote* default response headers,
|
13
|
+
# use allow users to set them to an empty string to indicate they should be removed.
|
14
|
+
if header in response.headers and response.headers[header] == "":
|
15
|
+
del response.headers[header]
|
16
|
+
continue
|
17
|
+
|
12
18
|
response.headers.setdefault(header, value)
|
13
19
|
|
14
20
|
# Add the Content-Length header to non-streaming responses if not
|
plain/utils/timesince.py
CHANGED
@@ -4,59 +4,93 @@ from plain.utils.html import avoid_wrapping
|
|
4
4
|
from plain.utils.text import pluralize_lazy
|
5
5
|
from plain.utils.timezone import is_aware
|
6
6
|
|
7
|
-
TIME_STRINGS = {
|
8
|
-
"year": pluralize_lazy("%(num)d year", "%(num)d years", "num"),
|
9
|
-
"month": pluralize_lazy("%(num)d month", "%(num)d months", "num"),
|
10
|
-
"week": pluralize_lazy("%(num)d week", "%(num)d weeks", "num"),
|
11
|
-
"day": pluralize_lazy("%(num)d day", "%(num)d days", "num"),
|
12
|
-
"hour": pluralize_lazy("%(num)d hour", "%(num)d hours", "num"),
|
13
|
-
"minute": pluralize_lazy("%(num)d minute", "%(num)d minutes", "num"),
|
14
|
-
}
|
15
7
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
MONTHS_DAYS = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
|
26
|
-
|
27
|
-
|
28
|
-
def timesince(d, now=None, reversed=False, time_strings=None, depth=2):
|
8
|
+
def timesince(
|
9
|
+
d: datetime.datetime,
|
10
|
+
*,
|
11
|
+
now: datetime.datetime | None = None,
|
12
|
+
reversed: bool = False,
|
13
|
+
format: str | dict[str, str] = "verbose",
|
14
|
+
depth: int = 2,
|
15
|
+
) -> str:
|
29
16
|
"""
|
30
17
|
Take two datetime objects and return the time between d and now as a nicely
|
31
|
-
formatted string, e.g
|
32
|
-
|
18
|
+
formatted string, e.g., "10 minutes" or "10m" (depending on the format).
|
19
|
+
|
20
|
+
`format` can be:
|
21
|
+
- "verbose": e.g., "1 year, 2 months"
|
22
|
+
- "short": e.g., "1y 2m"
|
23
|
+
- A custom dictionary defining time unit formats.
|
33
24
|
|
34
25
|
Units used are years, months, weeks, days, hours, and minutes.
|
35
26
|
Seconds and microseconds are ignored.
|
36
27
|
|
37
28
|
The algorithm takes into account the varying duration of years and months.
|
38
|
-
|
39
|
-
but also between 2007/08/10 and 2008/09/10 despite the delta
|
40
|
-
in the former case and 397 in the latter.
|
29
|
+
For example, there is exactly "1 year, 1 month" between 2013/02/10 and
|
30
|
+
2014/03/10, but also between 2007/08/10 and 2008/09/10 despite the delta
|
31
|
+
being 393 days in the former case and 397 in the latter.
|
41
32
|
|
42
|
-
Up to `depth` adjacent units will be displayed.
|
33
|
+
Up to `depth` adjacent units will be displayed. For example,
|
43
34
|
"2 weeks, 3 days" and "1 year, 3 months" are possible outputs, but
|
44
35
|
"2 weeks, 3 hours" and "1 year, 5 days" are not.
|
45
36
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
37
|
+
Arguments:
|
38
|
+
d: A datetime object representing the starting time.
|
39
|
+
now: A datetime object representing the current time. Defaults to the
|
40
|
+
current time if not provided.
|
41
|
+
reversed: If True, calculates time until `d` rather than since `d`.
|
42
|
+
format: The output format, either "verbose", "short", or a custom
|
43
|
+
dictionary of time unit formats.
|
44
|
+
depth: An integer specifying the number of adjacent time units to display.
|
45
|
+
|
46
|
+
Returns:
|
47
|
+
A string representing the time difference, formatted according to the
|
48
|
+
specified format.
|
49
|
+
|
50
|
+
Raises:
|
51
|
+
ValueError: If depth is less than 1 or if format is invalid.
|
55
52
|
"""
|
56
|
-
|
57
|
-
|
53
|
+
TIME_CHUNKS = [
|
54
|
+
60 * 60 * 24 * 7, # week
|
55
|
+
60 * 60 * 24, # day
|
56
|
+
60 * 60, # hour
|
57
|
+
60, # minute
|
58
|
+
]
|
59
|
+
MONTHS_DAYS = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
|
60
|
+
TIME_STRINGS_KEYS = ["year", "month", "week", "day", "hour", "minute"]
|
61
|
+
|
62
|
+
VERBOSE_TIME_STRINGS = {
|
63
|
+
"year": pluralize_lazy("%(num)d year", "%(num)d years", "num"),
|
64
|
+
"month": pluralize_lazy("%(num)d month", "%(num)d months", "num"),
|
65
|
+
"week": pluralize_lazy("%(num)d week", "%(num)d weeks", "num"),
|
66
|
+
"day": pluralize_lazy("%(num)d day", "%(num)d days", "num"),
|
67
|
+
"hour": pluralize_lazy("%(num)d hour", "%(num)d hours", "num"),
|
68
|
+
"minute": pluralize_lazy("%(num)d minute", "%(num)d minutes", "num"),
|
69
|
+
}
|
70
|
+
SHORT_TIME_STRINGS = {
|
71
|
+
"year": "%(num)dy",
|
72
|
+
"month": "%(num)dmo",
|
73
|
+
"week": "%(num)dw",
|
74
|
+
"day": "%(num)dd",
|
75
|
+
"hour": "%(num)dh",
|
76
|
+
"minute": "%(num)dm",
|
77
|
+
}
|
78
|
+
|
79
|
+
# Determine time_strings based on format
|
80
|
+
if format == "verbose":
|
81
|
+
time_strings = VERBOSE_TIME_STRINGS
|
82
|
+
elif format == "short":
|
83
|
+
time_strings = SHORT_TIME_STRINGS
|
84
|
+
elif isinstance(format, dict):
|
85
|
+
time_strings = format
|
86
|
+
else:
|
87
|
+
raise ValueError(
|
88
|
+
"format must be 'verbose', 'short', or a custom dictionary of formats."
|
89
|
+
)
|
90
|
+
|
58
91
|
if depth <= 0:
|
59
92
|
raise ValueError("depth must be greater than 0.")
|
93
|
+
|
60
94
|
# Convert datetime.date to datetime.datetime for comparison.
|
61
95
|
if not isinstance(d, datetime.datetime):
|
62
96
|
d = datetime.datetime(d.year, d.month, d.day)
|
@@ -82,8 +116,6 @@ def timesince(d, now=None, reversed=False, time_strings=None, depth=2):
|
|
82
116
|
years, months = divmod(total_months, 12)
|
83
117
|
|
84
118
|
# Calculate the remaining time.
|
85
|
-
# Create a "pivot" datetime shifted from d by years and months, then use
|
86
|
-
# that to determine the other parts.
|
87
119
|
if years or months:
|
88
120
|
pivot_year = d.year + years
|
89
121
|
pivot_month = d.month + months
|
@@ -131,8 +163,13 @@ def timesince(d, now=None, reversed=False, time_strings=None, depth=2):
|
|
131
163
|
return ", ".join(result)
|
132
164
|
|
133
165
|
|
134
|
-
def timeuntil(
|
166
|
+
def timeuntil(
|
167
|
+
d: datetime.datetime,
|
168
|
+
now: datetime.datetime | None = None,
|
169
|
+
format: str | dict[str, str] = "verbose",
|
170
|
+
depth: int = 2,
|
171
|
+
) -> str:
|
135
172
|
"""
|
136
173
|
Like timesince, but return a string measuring the time until the given time.
|
137
174
|
"""
|
138
|
-
return timesince(d, now, reversed=True,
|
175
|
+
return timesince(d, now=now, reversed=True, format=format, depth=depth)
|
@@ -51,7 +51,7 @@ plain/internal/handlers/base.py,sha256=I6549DBZUb-anqox7arzL9wpypH4bUCxZkitO76hl
|
|
51
51
|
plain/internal/handlers/exception.py,sha256=DH9gh1FyqgetFpMaB8yLIVE6phBTVPKQLA1GIn9MOeI,4555
|
52
52
|
plain/internal/handlers/wsgi.py,sha256=EWoH9EeetfgiL0BtoAe2Aof0VWaBrxl74Y9EP4xx0wc,7553
|
53
53
|
plain/internal/middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
54
|
-
plain/internal/middleware/headers.py,sha256=
|
54
|
+
plain/internal/middleware/headers.py,sha256=naYsFK0phMGFf1DlRj5fAYz78R5cXToCJfuqm0cR0nI,965
|
55
55
|
plain/internal/middleware/https.py,sha256=XpuQK8HicYX1jNanQHqNgyQ9rqe4NLUOZO3ZzKdsP8k,1203
|
56
56
|
plain/internal/middleware/slash.py,sha256=1rZsSSzXAA-vBb-dc2RlZaKW9gTT8YM8fJzwYGL4swA,2575
|
57
57
|
plain/logs/README.md,sha256=H6uVXdInYlasq0Z1WnhWnPmNwYQoZ1MSLPDQ4ZE7u4A,492
|
@@ -125,7 +125,7 @@ plain/utils/module_loading.py,sha256=CWl7Shoax9Zkevf1pM9PpS_0V69J5Cukjyj078UPCAw
|
|
125
125
|
plain/utils/regex_helper.py,sha256=pAdh_xG52BOyXLsiuIMPFgduUAoWOEje1ZpjhcefxiA,12769
|
126
126
|
plain/utils/safestring.py,sha256=SHGhpbX6FFDKSYOY9zYAgAQX0g0exzRba7dM2bJalWs,1873
|
127
127
|
plain/utils/text.py,sha256=qX7vECGH4Xk96qZRH9A1IyZA-mrJ-j62j3kDcLTdWK0,16586
|
128
|
-
plain/utils/timesince.py,sha256=
|
128
|
+
plain/utils/timesince.py,sha256=a_-ZoPK_s3Pt998CW4rWp0clZ1XyK2x04hCqak2giII,5928
|
129
129
|
plain/utils/timezone.py,sha256=6u0sE-9RVp0_OCe0Y1KiYYQoq5THWLokZFQYY8jf78g,6221
|
130
130
|
plain/utils/tree.py,sha256=wdWzmfsgc26YDF2wxhAY3yVxXTixQYqYDKE9mL3L3ZY,4383
|
131
131
|
plain/views/README.md,sha256=qndsXKyNMnipPlLaAvgQeGxqXknNQwlFh31Yxk8rHp8,5994
|
@@ -138,8 +138,8 @@ plain/views/forms.py,sha256=RhlaUcZCkeqokY_fvv-NOS-kgZAG4XhDLOPbf9K_Zlc,2691
|
|
138
138
|
plain/views/objects.py,sha256=fRfS6KNehIGqkbPw4nSafj8HStxYExHmbggolBbzcxs,7921
|
139
139
|
plain/views/redirect.py,sha256=KLnlktzK6ZNMTlaEiZpMKQMEP5zeTgGLJ9BIkIJfwBo,1733
|
140
140
|
plain/views/templates.py,sha256=nF9CcdhhjAyp3LB0RrSYnBaHpHzMfPSw719RCdcXk7o,2007
|
141
|
-
plain-0.
|
142
|
-
plain-0.
|
143
|
-
plain-0.
|
144
|
-
plain-0.
|
145
|
-
plain-0.
|
141
|
+
plain-0.16.0.dist-info/METADATA,sha256=QYjt6bNbKLzOHA2ER7Mch3hy9f0nrZm03kXJM56hQXs,2518
|
142
|
+
plain-0.16.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
143
|
+
plain-0.16.0.dist-info/entry_points.txt,sha256=DHHprvufgd7xypiBiqMANYRnpJ9xPPYhYbnPGwOkWqE,40
|
144
|
+
plain-0.16.0.dist-info/licenses/LICENSE,sha256=m0D5O7QoH9l5Vz_rrX_9r-C8d9UNr_ciK6Qwac7o6yo,3175
|
145
|
+
plain-0.16.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|