Pyjoyment 0.1.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.
Files changed (200) hide show
  1. Pyjo/App/ASGI.py +735 -0
  2. Pyjo/App/CGI.py +773 -0
  3. Pyjo/App/FastCGI.py +631 -0
  4. Pyjo/App/Lambda.py +987 -0
  5. Pyjo/App/WSGI.py +427 -0
  6. Pyjo/App/__init__.py +409 -0
  7. Pyjo/Asset/File.py +377 -0
  8. Pyjo/Asset/Memory.py +235 -0
  9. Pyjo/Asset/__init__.py +200 -0
  10. Pyjo/Base.py +215 -0
  11. Pyjo/Cache.py +119 -0
  12. Pyjo/Collection.py +570 -0
  13. Pyjo/Content/MultiPart.py +325 -0
  14. Pyjo/Content/Single.py +193 -0
  15. Pyjo/Content/__init__.py +806 -0
  16. Pyjo/Cookie/Request.py +90 -0
  17. Pyjo/Cookie/Response.py +294 -0
  18. Pyjo/Cookie/__init__.py +143 -0
  19. Pyjo/DOM/CSS.py +1019 -0
  20. Pyjo/DOM/HTML.py +550 -0
  21. Pyjo/DOM/__init__.py +1329 -0
  22. Pyjo/Date.py +294 -0
  23. Pyjo/EventEmitter.py +522 -0
  24. Pyjo/Exception.py +494 -0
  25. Pyjo/File.py +959 -0
  26. Pyjo/GetOpt.py +605 -0
  27. Pyjo/Headers.py +760 -0
  28. Pyjo/HelloWorld.py +77 -0
  29. Pyjo/Home.py +109 -0
  30. Pyjo/IOLoop/ChildProcess.py +591 -0
  31. Pyjo/IOLoop/Client.py +736 -0
  32. Pyjo/IOLoop/Datagram.py +982 -0
  33. Pyjo/IOLoop/Messages.py +664 -0
  34. Pyjo/IOLoop/Server.py +458 -0
  35. Pyjo/IOLoop/Stdio.py +446 -0
  36. Pyjo/IOLoop/Stream.py +644 -0
  37. Pyjo/IOLoop/Subprocess.py +419 -0
  38. Pyjo/IOLoop/TLS.py +360 -0
  39. Pyjo/IOLoop/__init__.py +1036 -0
  40. Pyjo/IOLoop/certs/README.md +41 -0
  41. Pyjo/IOLoop/certs/ca.crt +20 -0
  42. Pyjo/IOLoop/certs/server.crt +21 -0
  43. Pyjo/IOLoop/certs/server.key +28 -0
  44. Pyjo/JSON/Pointer.py +135 -0
  45. Pyjo/JSON/RPC.py +772 -0
  46. Pyjo/JSON/Schema.py +2506 -0
  47. Pyjo/JSON/__init__.py +573 -0
  48. Pyjo/Loader.py +342 -0
  49. Pyjo/Log.py +723 -0
  50. Pyjo/Message/Request.py +725 -0
  51. Pyjo/Message/Response.py +476 -0
  52. Pyjo/Message/__init__.py +830 -0
  53. Pyjo/Parameters.py +472 -0
  54. Pyjo/Path.py +419 -0
  55. Pyjo/Promise.py +894 -0
  56. Pyjo/Prove.py +1305 -0
  57. Pyjo/Reactor/Asyncio.py +854 -0
  58. Pyjo/Reactor/Poll.py +439 -0
  59. Pyjo/Reactor/__init__.py +525 -0
  60. Pyjo/SSE.py +119 -0
  61. Pyjo/Server/ASGI.py +486 -0
  62. Pyjo/Server/CGI.py +259 -0
  63. Pyjo/Server/Daemon.py +1007 -0
  64. Pyjo/Server/FastCGI.py +517 -0
  65. Pyjo/Server/Hot/__init__.py +687 -0
  66. Pyjo/Server/Hot/__main__.py +5 -0
  67. Pyjo/Server/Lambda.py +641 -0
  68. Pyjo/Server/Prefork.py +917 -0
  69. Pyjo/Server/Prethread.py +394 -0
  70. Pyjo/Server/Reload/Backend/Poll.py +114 -0
  71. Pyjo/Server/Reload/Backend/__init__.py +126 -0
  72. Pyjo/Server/Reload/__init__.py +482 -0
  73. Pyjo/Server/Reload/__main__.py +5 -0
  74. Pyjo/Server/Stdio.py +228 -0
  75. Pyjo/Server/WSGI.py +349 -0
  76. Pyjo/Server/__init__.py +396 -0
  77. Pyjo/String/Bytes.py +532 -0
  78. Pyjo/String/HTML.py +210 -0
  79. Pyjo/String/Mixin.py +333 -0
  80. Pyjo/String/Template.py +470 -0
  81. Pyjo/String/Unicode.py +553 -0
  82. Pyjo/String/__init__.py +0 -0
  83. Pyjo/TOML.py +232 -0
  84. Pyjo/Template.py +1290 -0
  85. Pyjo/Test/Pyjo.py +1454 -0
  86. Pyjo/Test/__init__.py +1233 -0
  87. Pyjo/Test/pytest_plugin.py +305 -0
  88. Pyjo/Transaction/HTTP.py +374 -0
  89. Pyjo/Transaction/WebSocket.py +709 -0
  90. Pyjo/Transaction/__init__.py +429 -0
  91. Pyjo/URL/Template.py +459 -0
  92. Pyjo/URL/__init__.py +651 -0
  93. Pyjo/Upload.py +139 -0
  94. Pyjo/UserAgent/CookieJar.py +391 -0
  95. Pyjo/UserAgent/Proxy.py +138 -0
  96. Pyjo/UserAgent/Server.py +239 -0
  97. Pyjo/UserAgent/Transactor.py +766 -0
  98. Pyjo/UserAgent/__init__.py +1243 -0
  99. Pyjo/Util.py +3567 -0
  100. Pyjo/WebSocket.py +332 -0
  101. Pyjo/YAML.py +1167 -0
  102. Pyjo/__init__.py +441 -0
  103. Pyjo/__main__.py +35 -0
  104. Pyjo/py.typed +0 -0
  105. Pyjoyment/Command/Author/__init__.py +20 -0
  106. Pyjoyment/Command/Author/generate/__init__.py +106 -0
  107. Pyjoyment/Command/Author/generate/app.py +299 -0
  108. Pyjoyment/Command/Author/generate/dockerfile.py +281 -0
  109. Pyjoyment/Command/Author/generate/kubernetes.py +514 -0
  110. Pyjoyment/Command/Author/generate/lambda_zip.py +372 -0
  111. Pyjoyment/Command/Author/generate/lite_app.py +113 -0
  112. Pyjoyment/Command/Author/generate/plugin.py +267 -0
  113. Pyjoyment/Command/Author/generate/pyproject.py +123 -0
  114. Pyjoyment/Command/Author/inflate.py +101 -0
  115. Pyjoyment/Command/__init__.py +332 -0
  116. Pyjoyment/Command/asgi.py +92 -0
  117. Pyjoyment/Command/cgi.py +90 -0
  118. Pyjoyment/Command/daemon.py +160 -0
  119. Pyjoyment/Command/eval.py +128 -0
  120. Pyjoyment/Command/fastcgi.py +99 -0
  121. Pyjoyment/Command/get.py +305 -0
  122. Pyjoyment/Command/lambda.py +117 -0
  123. Pyjoyment/Command/prefork.py +181 -0
  124. Pyjoyment/Command/prethread.py +126 -0
  125. Pyjoyment/Command/routes.py +133 -0
  126. Pyjoyment/Command/stdio.py +92 -0
  127. Pyjoyment/Command/version.py +141 -0
  128. Pyjoyment/Command/wsgi.py +93 -0
  129. Pyjoyment/Commands.py +546 -0
  130. Pyjoyment/Controller.py +1307 -0
  131. Pyjoyment/HelloWorld.py +56 -0
  132. Pyjoyment/Lite.py +494 -0
  133. Pyjoyment/Plugin/AllowedHosts.py +205 -0
  134. Pyjoyment/Plugin/Config.py +248 -0
  135. Pyjoyment/Plugin/DefaultHelpers.py +1202 -0
  136. Pyjoyment/Plugin/EPLRenderer.py +134 -0
  137. Pyjoyment/Plugin/EPRenderer.py +212 -0
  138. Pyjoyment/Plugin/HeaderCondition.py +98 -0
  139. Pyjoyment/Plugin/Health.py +325 -0
  140. Pyjoyment/Plugin/JSONConfig.py +172 -0
  141. Pyjoyment/Plugin/Mount.py +223 -0
  142. Pyjoyment/Plugin/PyRenderer.py +209 -0
  143. Pyjoyment/Plugin/TOMLConfig.py +141 -0
  144. Pyjoyment/Plugin/TagHelpers.py +959 -0
  145. Pyjoyment/Plugin/YAMLConfig.py +178 -0
  146. Pyjoyment/Plugin/__init__.py +80 -0
  147. Pyjoyment/Plugins.py +215 -0
  148. Pyjoyment/Renderer.py +673 -0
  149. Pyjoyment/Routes/Match.py +251 -0
  150. Pyjoyment/Routes/Pattern.py +443 -0
  151. Pyjoyment/Routes/Route.py +827 -0
  152. Pyjoyment/Routes/__init__.py +419 -0
  153. Pyjoyment/Sessions.py +255 -0
  154. Pyjoyment/Static.py +421 -0
  155. Pyjoyment/Types.py +238 -0
  156. Pyjoyment/Validator/Validation.py +395 -0
  157. Pyjoyment/Validator/__init__.py +304 -0
  158. Pyjoyment/__init__.py +1083 -0
  159. Pyjoyment/__main__.py +5 -0
  160. Pyjoyment/py.typed +0 -0
  161. Pyjoyment/resources/public/favicon.ico +0 -0
  162. Pyjoyment/resources/public/favicon.svg +345 -0
  163. Pyjoyment/resources/public/pyjo/bootstrap/LICENSE +21 -0
  164. Pyjoyment/resources/public/pyjo/bootstrap/bootstrap.bundle.min.js.map +1 -0
  165. Pyjoyment/resources/public/pyjo/bootstrap/bootstrap.css +6 -0
  166. Pyjoyment/resources/public/pyjo/bootstrap/bootstrap.js +7 -0
  167. Pyjoyment/resources/public/pyjo/bootstrap/bootstrap.min.css.map +1 -0
  168. Pyjoyment/resources/public/pyjo/failunicat.png +0 -0
  169. Pyjoyment/resources/public/pyjo/failunicat.svg +50 -0
  170. Pyjoyment/resources/public/pyjo/highlight.js/LICENSE +29 -0
  171. Pyjoyment/resources/public/pyjo/highlight.js/highlight-mojo-dark.css +56 -0
  172. Pyjoyment/resources/public/pyjo/highlight.js/highlight.min.js +1262 -0
  173. Pyjoyment/resources/public/pyjo/highlight.js/pyjoyment.min.js +1 -0
  174. Pyjoyment/resources/public/pyjo/logo-black-4x.png +0 -0
  175. Pyjoyment/resources/public/pyjo/logo-black-4x.svg +346 -0
  176. Pyjoyment/resources/public/pyjo/logo-white-2x.png +0 -0
  177. Pyjoyment/resources/public/pyjo/logo-white-2x.svg +346 -0
  178. Pyjoyment/resources/public/pyjo/logo-white-4x.png +0 -0
  179. Pyjoyment/resources/public/pyjo/logo-white-4x.svg +346 -0
  180. Pyjoyment/resources/public/pyjo/logo-white.png +0 -0
  181. Pyjoyment/resources/public/pyjo/logo-white.svg +346 -0
  182. Pyjoyment/resources/public/pyjo/logo.png +0 -0
  183. Pyjoyment/resources/public/pyjo/logo.svg +346 -0
  184. Pyjoyment/resources/public/pyjo/notfound.png +0 -0
  185. Pyjoyment/resources/public/pyjo/notfound.svg +3 -0
  186. Pyjoyment/resources/public/pyjo/nounicat.png +0 -0
  187. Pyjoyment/resources/public/pyjo/nounicat.svg +5 -0
  188. Pyjoyment/resources/public/pyjo/pinstripe-dark.png +0 -0
  189. Pyjoyment/resources/public/pyjo/pinstripe-dark.svg +4 -0
  190. Pyjoyment/resources/public/pyjo/pinstripe-light.png +0 -0
  191. Pyjoyment/resources/public/pyjo/pinstripe-light.svg +4 -0
  192. Pyjoyment/resources/public/pyjo/pyjo.css +204 -0
  193. Pyjoyment/resources/templates/pyjo/debug.html.ep +346 -0
  194. Pyjoyment/resources/templates/pyjo/exception.html.ep +32 -0
  195. Pyjoyment/resources/templates/pyjo/not_found.html.ep +45 -0
  196. pyjoyment-0.1.0.dist-info/METADATA +376 -0
  197. pyjoyment-0.1.0.dist-info/RECORD +200 -0
  198. pyjoyment-0.1.0.dist-info/WHEEL +4 -0
  199. pyjoyment-0.1.0.dist-info/entry_points.txt +8 -0
  200. pyjoyment-0.1.0.dist-info/licenses/LICENSE +201 -0
Pyjo/Date.py ADDED
@@ -0,0 +1,294 @@
1
+ """
2
+ Pyjo.Date - HTTP date
3
+ =====================
4
+ ::
5
+
6
+ from Pyjo.Date import Date
7
+
8
+ # Parse
9
+ date = Date('Sun, 06 Nov 1994 08:49:37 GMT')
10
+ print(date.epoch)
11
+
12
+ # Or import the module, as in Google Python Style Guide
13
+
14
+ import Pyjo.Date
15
+
16
+ import time
17
+
18
+ # Parse
19
+ date = Pyjo.Date.new('Sun, 06 Nov 1994 08:49:37 GMT')
20
+ print(date.epoch)
21
+
22
+ # Build
23
+ date = Pyjo.Date.new(time.time() + 60)
24
+ print(date)
25
+
26
+ :mod:`Pyjo.Date` implements HTTP date and time functions, based on :rfc:`7230`,
27
+ :rfc:`7231` and :rfc:`3339`.
28
+
29
+ See also
30
+ --------
31
+
32
+ :mod:`Pyjoyment`, https://mojolicious.org.
33
+
34
+ Ported from `Mojo::Date <https://github.com/mojolicious/mojo/blob/v9.49/lib/Mojo/Date.pm>`_
35
+ of Mojolicious 9.49.
36
+
37
+ Classes
38
+ -------
39
+ """
40
+
41
+ import Pyjo.Base
42
+ import Pyjo.String.Mixin
43
+
44
+ import calendar
45
+ import re
46
+ import time
47
+
48
+ from datetime import UTC, datetime
49
+ from typing import Self
50
+
51
+
52
+ RFC3339_RE = re.compile(
53
+ r"""
54
+ ^(\d+)-(\d+)-(\d+)\D+(\d+):(\d+):(\d+(?:\.\d+)?) # Date and time
55
+ (?:Z|([+-])(\d+):(\d+))?$ # Offset
56
+ """,
57
+ re.X | re.I,
58
+ )
59
+
60
+ DAYS = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat')
61
+ MONTHS = ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec')
62
+ MONTH_NUMBERS = {name: number for number, name in enumerate(MONTHS)}
63
+
64
+
65
+ class Pyjo_Date(Pyjo.Base.object, Pyjo.String.Mixin.object):
66
+ """
67
+ :mod:`Pyjo.Date` inherits all methods from :mod:`Pyjo.Base` and
68
+ :mod:`Pyjo.String.Mixin` and implements the following new ones.
69
+ """
70
+
71
+ epoch: int | float | None
72
+
73
+ def __init__(self, date: str | float | datetime | None = None) -> None:
74
+ """::
75
+
76
+ date = Pyjo.Date.new()
77
+ date = Pyjo.Date.new('Sun Nov 6 08:49:37 1994')
78
+ date = Pyjo.Date.new(datetime.now(UTC))
79
+
80
+ Construct a new :mod:`Pyjo.Date` object and :meth:`parse` date if necessary.
81
+ """
82
+
83
+ self.epoch = int(time.time())
84
+ """::
85
+
86
+ epoch = date.epoch
87
+ date.epoch = 784111777
88
+
89
+ Epoch seconds, defaults to the current time.
90
+ """
91
+
92
+ if date is not None:
93
+ self.parse(date)
94
+
95
+ def __bool__(self) -> bool:
96
+ """::
97
+
98
+ boolean = bool(date)
99
+
100
+ Always true.
101
+ """
102
+ return True
103
+
104
+ # Not in Mojolicious: the epoch for the standard library
105
+ def __float__(self) -> float:
106
+ """::
107
+
108
+ number = float(date)
109
+
110
+ The epoch, for the standard library. Before Python 3.15,
111
+ :meth:`datetime.datetime.fromtimestamp` and :func:`time.gmtime` take only an
112
+ ``int`` or a ``float``, not an object that converts to one, so they get
113
+ ``float(date)``. ::
114
+
115
+ # datetime.datetime(1994, 11, 6, 8, 49, 37, tzinfo=datetime.timezone.utc)
116
+ datetime.fromtimestamp(float(Pyjo.Date.new(784111777)), UTC)
117
+ """
118
+ return float(self.epoch or 0)
119
+
120
+ # Not in Mojolicious: the epoch for the standard library
121
+ def __int__(self) -> int:
122
+ """::
123
+
124
+ number = int(date)
125
+
126
+ The epoch in whole seconds, for the standard library.
127
+ """
128
+ return int(self.epoch or 0)
129
+
130
+ def __str__(self) -> str:
131
+ """::
132
+
133
+ string = str(date)
134
+
135
+ Alias for :meth:`to_str`.
136
+ """
137
+ return self.to_str()
138
+
139
+ def parse(self, date: str | float | datetime) -> Self:
140
+ """::
141
+
142
+ date = date.parse('Sun Nov 6 08:49:37 1994')
143
+ date = date.parse(datetime.now(UTC))
144
+
145
+ Parse date. A :class:`datetime.datetime` gives its timestamp, so a naive
146
+ one is local time, as for :meth:`datetime.datetime.timestamp`. ::
147
+
148
+ # Epoch
149
+ print(Pyjo.Date.new('784111777').epoch)
150
+ print(Pyjo.Date.new('784111777.21').epoch)
151
+
152
+ # RFC 822/1123
153
+ print(Pyjo.Date.new('Sun, 06 Nov 1994 08:49:37 GMT').epoch)
154
+
155
+ # RFC 850/1036
156
+ print(Pyjo.Date.new('Sunday, 06-Nov-94 08:49:37 GMT').epoch)
157
+
158
+ # Ansi C asctime()
159
+ print(Pyjo.Date.new('Sun Nov 6 08:49:37 1994').epoch)
160
+
161
+ # RFC 3339
162
+ print(Pyjo.Date.new('1994-11-06T08:49:37Z').epoch)
163
+ print(Pyjo.Date.new('1994-11-06T08:49:37').epoch)
164
+ print(Pyjo.Date.new('1994-11-06T08:49:37.21Z').epoch)
165
+ print(Pyjo.Date.new('1994-11-06T08:49:37+01:00').epoch)
166
+ print(Pyjo.Date.new('1994-11-06T08:49:37-01:00').epoch)
167
+ """
168
+ # Not in Mojolicious: datetime of the standard library
169
+ if isinstance(date, datetime):
170
+ timestamp = date.timestamp()
171
+ self.epoch = int(timestamp) if timestamp.is_integer() else timestamp
172
+ return self
173
+
174
+ date = str(date)
175
+
176
+ # epoch (784111777)
177
+ if re.search(r'^\d+$|^\d+\.\d+$', date):
178
+ self.epoch = float(date) if '.' in date else int(date)
179
+ return self
180
+
181
+ # RFC 822/1123 (Sun, 06 Nov 1994 08:49:37 GMT)
182
+ # RFC 850/1036 (Sunday, 06-Nov-94 08:49:37 GMT)
183
+ offset = 0
184
+ if m := re.search(r'^\w+\W+(\d+)\W+(\w+)\W+(\d+)\W+(\d+):(\d+):(\d+)\W*\w+$', date):
185
+ day, month, year, h, mi, s = m[1], MONTH_NUMBERS.get(m[2]), m[3], m[4], m[5], m[6]
186
+
187
+ # RFC 3339 (1994-11-06T08:49:37Z)
188
+ elif m := RFC3339_RE.search(date):
189
+ year, month, day, h, mi, s = m[1], int(m[2]) - 1, m[3], m[4], m[5], m[6]
190
+ if m[7]:
191
+ offset = ((int(m[8]) * 3600) + (int(m[9]) * 60)) * (-1 if m[7] == '+' else 1)
192
+
193
+ # ANSI C asctime() (Sun Nov 6 08:49:37 1994)
194
+ elif m := re.search(r'^\w+\s+(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+(\d+)$', date):
195
+ month, day, h, mi, s, year = MONTH_NUMBERS.get(m[1]), m[2], m[3], m[4], m[5], m[6]
196
+
197
+ # Invalid
198
+ else:
199
+ self.epoch = None
200
+ return self
201
+
202
+ epoch = _timegm(float(s), int(mi), int(h), int(day), month, int(year))
203
+ self.epoch = epoch + offset if epoch is not None and epoch + offset >= 0 else None
204
+ return self
205
+
206
+ # Not in Mojolicious: datetime of the standard library
207
+ def to_datetime(self) -> datetime:
208
+ """::
209
+
210
+ dt = date.to_datetime()
211
+
212
+ Turn date into a :class:`datetime.datetime` in UTC, for the standard library. ::
213
+
214
+ # datetime.datetime(1994, 11, 6, 8, 49, 37, tzinfo=datetime.timezone.utc)
215
+ Pyjo.Date.new(784111777).to_datetime()
216
+ """
217
+ return datetime.fromtimestamp(self.epoch or 0, UTC)
218
+
219
+ # Renamed from Mojolicious: to_datetime, the name of the conversion to the
220
+ # datetime of the standard library here
221
+ def to_rfc3339(self) -> str:
222
+ """::
223
+
224
+ string = date.to_rfc3339()
225
+
226
+ Render :rfc:`3339` date and time. ::
227
+
228
+ # "1994-11-06T08:49:37Z"
229
+ Pyjo.Date.new(784111777).to_rfc3339()
230
+
231
+ # "1994-11-06T08:49:37.21Z"
232
+ Pyjo.Date.new(784111777.21).to_rfc3339()
233
+ """
234
+ # RFC 3339 (1994-11-06T08:49:37Z)
235
+ epoch = self.epoch or 0
236
+ t = time.gmtime(epoch)
237
+ string = f'{t.tm_year:04d}-{t.tm_mon:02d}-{t.tm_mday:02d}T{t.tm_hour:02d}:{t.tm_min:02d}:{t.tm_sec:02d}'
238
+ m = re.search(r'(\.\d+)$', _number(epoch))
239
+ return string + (m[1] if m else '') + 'Z'
240
+
241
+ def to_str(self) -> str:
242
+ """::
243
+
244
+ string = date.to_str()
245
+
246
+ Render date suitable for HTTP messages. ::
247
+
248
+ # "Sun, 06 Nov 1994 08:49:37 GMT"
249
+ Pyjo.Date.new(784111777).to_str()
250
+ """
251
+ # RFC 7231 (Sun, 06 Nov 1994 08:49:37 GMT)
252
+ t = time.gmtime(self.epoch or 0)
253
+ # Python counts the days of the week from Monday, Perl from Sunday
254
+ day, month = DAYS[(t.tm_wday + 1) % 7], MONTHS[t.tm_mon - 1]
255
+ return f'{day}, {t.tm_mday:02d} {month} {t.tm_year:04d} {t.tm_hour:02d}:{t.tm_min:02d}:{t.tm_sec:02d} GMT'
256
+
257
+
258
+ def _number(number: int | float) -> str:
259
+ # A number as Perl stringifies it: no ".0" for a whole float
260
+ return repr(number) if isinstance(number, float) and not number.is_integer() else str(int(number))
261
+
262
+
263
+ def _timegm(s: float, mi: int, h: int, day: int, month: int | None, year: int) -> int | float | None:
264
+ # Time::Local's timegm, with None instead of an exception for a value out of
265
+ # range
266
+
267
+ # Years 0 to 99 are within 50 years of the current one, 100 to 999 count
268
+ # from 1900
269
+ if year < 100:
270
+ this_year = time.localtime().tm_year
271
+ year += this_year - this_year % 100
272
+ if year > this_year + 50:
273
+ year -= 100
274
+ elif year <= this_year - 50:
275
+ year += 100
276
+ elif year < 1000:
277
+ year += 1900
278
+
279
+ # Unlike Mojolicious: an unknown month name is invalid, where Perl takes undef
280
+ # as 0, January, with a warning; and a year after 9999 is invalid, where
281
+ # datetime, which calendar.timegm uses, ends
282
+ if month is None or not 0 <= month <= 11 or year > 9999:
283
+ return None
284
+ month_days = calendar.monthrange(2000 if calendar.isleap(year) else 2001, month + 1)[1]
285
+ if not (1 <= day <= month_days and 0 <= h <= 23 and 0 <= mi <= 59 and 0 <= s < 60):
286
+ return None
287
+
288
+ epoch = calendar.timegm((year, month + 1, day, h, mi, int(s), 0, 0, 0))
289
+ return epoch + (s - int(s)) if s != int(s) else epoch
290
+
291
+
292
+ new = Pyjo_Date.new
293
+ object = Pyjo_Date
294
+ Date = Pyjo_Date