librus-python-api 1.6.2__py3-none-any.whl → 1.6.4__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.
- librus_python_api/__init__.py +1 -1
- librus_python_api/modern_body.py +33 -6
- {librus_python_api-1.6.2.dist-info → librus_python_api-1.6.4.dist-info}/METADATA +8 -8
- {librus_python_api-1.6.2.dist-info → librus_python_api-1.6.4.dist-info}/RECORD +6 -6
- {librus_python_api-1.6.2.dist-info → librus_python_api-1.6.4.dist-info}/WHEEL +0 -0
- {librus_python_api-1.6.2.dist-info → librus_python_api-1.6.4.dist-info}/licenses/LICENSE +0 -0
librus_python_api/__init__.py
CHANGED
|
@@ -138,7 +138,7 @@ from librus_python_api.models import (
|
|
|
138
138
|
from librus_python_api.sending import SendAttempt
|
|
139
139
|
from librus_python_api.service import AccountClient, LibrusService
|
|
140
140
|
|
|
141
|
-
__version__ = "1.6.
|
|
141
|
+
__version__ = "1.6.4"
|
|
142
142
|
|
|
143
143
|
__all__ = [
|
|
144
144
|
"ArchiveAbsences",
|
librus_python_api/modern_body.py
CHANGED
|
@@ -11,11 +11,8 @@ from librus_python_api.exceptions import ErrorKind, LibrusError
|
|
|
11
11
|
from librus_python_api.markup import text
|
|
12
12
|
from librus_python_api.parsers import parse_html_document
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
r"(?:<\?xml\b|<Message(?=[\s/>]))",
|
|
17
|
-
re.DOTALL,
|
|
18
|
-
)
|
|
14
|
+
_XML_START = re.compile(r"<\?xml\b|<Message(?=[\s/>])")
|
|
15
|
+
_XML_DECLARATION = re.compile(r"<\?xml\b")
|
|
19
16
|
|
|
20
17
|
|
|
21
18
|
def render_body(encoded: str) -> str:
|
|
@@ -34,7 +31,7 @@ def render_body(encoded: str) -> str:
|
|
|
34
31
|
decoded = decoded.removeprefix("\ufeff")
|
|
35
32
|
# XML comments/PIs and a UTF-8 BOM must not route a Message wrapper through
|
|
36
33
|
# the permissive HTML renderer, losing CDATA or bypassing Content checks.
|
|
37
|
-
if
|
|
34
|
+
if _xml_prefixed(decoded):
|
|
38
35
|
decoded = _xml_content(decoded)
|
|
39
36
|
if not decoded:
|
|
40
37
|
return ""
|
|
@@ -47,6 +44,36 @@ def render_body(encoded: str) -> str:
|
|
|
47
44
|
)
|
|
48
45
|
|
|
49
46
|
|
|
47
|
+
def _xml_prefixed(decoded: str) -> bool:
|
|
48
|
+
"""Whether whitespace, comments and non-declaration PIs precede an XML start.
|
|
49
|
+
|
|
50
|
+
A comment or PI may end at any later terminator, as the lazy pattern this
|
|
51
|
+
replaces allowed, but one left-to-right pass decides it: once an item has
|
|
52
|
+
opened, every later terminator closes it. A regex backtracks exponentially
|
|
53
|
+
over a run of items with no XML start after it.
|
|
54
|
+
"""
|
|
55
|
+
starts = bytearray(len(decoded) + 1)
|
|
56
|
+
starts[0] = 1
|
|
57
|
+
comment_from = pi_from = len(decoded) + 1
|
|
58
|
+
for index, char in enumerate(decoded):
|
|
59
|
+
if starts[index]:
|
|
60
|
+
if char.isspace():
|
|
61
|
+
starts[index + 1] = 1
|
|
62
|
+
elif _XML_START.match(decoded, index):
|
|
63
|
+
return True
|
|
64
|
+
elif decoded.startswith("<!--", index):
|
|
65
|
+
comment_from = min(comment_from, index + 4)
|
|
66
|
+
elif decoded.startswith("<?", index) and not _XML_DECLARATION.match(
|
|
67
|
+
decoded, index
|
|
68
|
+
):
|
|
69
|
+
pi_from = min(pi_from, index + 2)
|
|
70
|
+
if index >= comment_from and decoded.startswith("-->", index):
|
|
71
|
+
starts[index + 3] = 1
|
|
72
|
+
if index >= pi_from and decoded.startswith("?>", index):
|
|
73
|
+
starts[index + 2] = 1
|
|
74
|
+
return False
|
|
75
|
+
|
|
76
|
+
|
|
50
77
|
def _xml_content(decoded: str) -> str:
|
|
51
78
|
parser = etree.XMLParser(
|
|
52
79
|
no_network=True,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: librus-python-api
|
|
3
|
-
Version: 1.6.
|
|
3
|
+
Version: 1.6.4
|
|
4
4
|
Summary: Independent bounded async Librus Synergia client
|
|
5
5
|
Project-URL: Repository, https://github.com/krzysztofbury/librus-python-api
|
|
6
6
|
Project-URL: Issues, https://github.com/krzysztofbury/librus-python-api/issues
|
|
@@ -16,7 +16,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.14
|
|
17
17
|
Classifier: Typing :: Typed
|
|
18
18
|
Requires-Python: >=3.13
|
|
19
|
-
Requires-Dist: aiohttp<4,>=3.14.
|
|
19
|
+
Requires-Dist: aiohttp<4,>=3.14.4
|
|
20
20
|
Requires-Dist: lxml<7,>=6.1
|
|
21
21
|
Requires-Dist: pydantic<3,>=2.12
|
|
22
22
|
Requires-Dist: pywin32<400,>=312; sys_platform == 'win32'
|
|
@@ -42,8 +42,9 @@ private ACLs. Windows installations include `pywin32` and `tzdata` automatically
|
|
|
42
42
|
Network paths, reparse-point paths and unsafe storage permissions fail closed.
|
|
43
43
|
See [platform requirements](contracts/persistence.md#windows-disk-boundary-25).
|
|
44
44
|
|
|
45
|
-
**Status:** `1.6.
|
|
46
|
-
|
|
45
|
+
**Status:** `1.6.4` renders message bodies in linear time, so a crafted body can
|
|
46
|
+
no longer stall a client, and keeps the 1.6.2 completed-lesson and grade layouts,
|
|
47
|
+
including behaviour points, which are validated and not reported as grades.
|
|
47
48
|
Class free days, typed capability outcomes and the live-check freshness watchdog
|
|
48
49
|
remain available. See [verification](VERIFICATION.md).
|
|
49
50
|
The stable 1.x API does not guarantee every school layout. School features depend
|
|
@@ -55,7 +56,7 @@ See [limitations](#supported-features-and-limitations) below.
|
|
|
55
56
|
Install the exact release from PyPI after its gated publication completes:
|
|
56
57
|
|
|
57
58
|
```sh
|
|
58
|
-
python -m pip install librus-python-api==1.6.
|
|
59
|
+
python -m pip install librus-python-api==1.6.4
|
|
59
60
|
```
|
|
60
61
|
|
|
61
62
|
Pin the exact version qualified by your application. Before publication, use a
|
|
@@ -71,7 +72,7 @@ python -m pip install .
|
|
|
71
72
|
To install a locally built wheel instead:
|
|
72
73
|
|
|
73
74
|
```sh
|
|
74
|
-
python -m pip install ./dist/librus_python_api-1.6.
|
|
75
|
+
python -m pip install ./dist/librus_python_api-1.6.4-py3-none-any.whl
|
|
75
76
|
```
|
|
76
77
|
|
|
77
78
|
No CLI or background process is installed: import the library in your own program.
|
|
@@ -250,7 +251,7 @@ Completed lessons may be disabled by the school. Behaviour notes are not
|
|
|
250
251
|
implemented. Some recipient, archive and receipt layouts remain
|
|
251
252
|
unqualified; backend acceptance is not proof of delivery. Tests cover supported
|
|
252
253
|
contracts, not every school or role. Detailed coverage is in the
|
|
253
|
-
[verification log][verification]
|
|
254
|
+
[verification log][verification]; open work is tracked in [issues][issues].
|
|
254
255
|
|
|
255
256
|
## Development and support
|
|
256
257
|
|
|
@@ -265,7 +266,6 @@ MIT licensed. See [LICENSE][license].
|
|
|
265
266
|
[api]: https://github.com/krzysztofbury/librus-python-api/blob/main/API.md
|
|
266
267
|
[upgrade]: https://github.com/krzysztofbury/librus-python-api/blob/main/contracts/account-context.md
|
|
267
268
|
[verification]: https://github.com/krzysztofbury/librus-python-api/blob/main/VERIFICATION.md
|
|
268
|
-
[roadmap]: https://github.com/krzysztofbury/librus-python-api/blob/main/TODO.md
|
|
269
269
|
[contributing]: https://github.com/krzysztofbury/librus-python-api/blob/main/CONTRIBUTING.md
|
|
270
270
|
[issues]: https://github.com/krzysztofbury/librus-python-api/issues
|
|
271
271
|
[security]: https://github.com/krzysztofbury/librus-python-api/blob/main/SECURITY.md
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
librus_python_api/__init__.py,sha256=
|
|
1
|
+
librus_python_api/__init__.py,sha256=g55dpGqZUadHtjcfKr1_1nW8ESiC38xEDw2ZNqhr4fw,6513
|
|
2
2
|
librus_python_api/_notification_bootstrap.py,sha256=18nzH0doXBNaZXCz-_ckIvRrlHTWV5Mm1t5m2-5eYGI,4669
|
|
3
3
|
librus_python_api/_notification_codec.py,sha256=hU6G3zGJ5fR8wfPC6fV2OqBKs-7XpT9eRnKO3FGsTzY,13451
|
|
4
4
|
librus_python_api/_storage.py,sha256=M_6ZAzN4zs7laOdKJBRH1I2rvy3pgqYgC_MZtsd2xtM,15893
|
|
@@ -26,7 +26,7 @@ librus_python_api/markup.py,sha256=vd483NSUQbVYvSV2IaUmsyGcRhpWtbyDHMeEhbc7siA,5
|
|
|
26
26
|
librus_python_api/message_content.py,sha256=v_qoEAoaYS0_9MQBaj57i5XnC7pzv_HHWefgTt4CNyM,9043
|
|
27
27
|
librus_python_api/messages.py,sha256=K9F8XVs1SojfFpdxxLV8gx9E0g81YxEHjigDzLHmDCg,10230
|
|
28
28
|
librus_python_api/models.py,sha256=Z6eV-ftjaN8YGUW8SntVTLjGCqe1EWRpd0P1Va8EwDs,40991
|
|
29
|
-
librus_python_api/modern_body.py,sha256=
|
|
29
|
+
librus_python_api/modern_body.py,sha256=SocXJTyGm1Q_2owzh1PE1zcCB2kqZTz1KPJ2R4yMyoc,3895
|
|
30
30
|
librus_python_api/modern_mailbox.py,sha256=2jF9UnJZ5AF6dip-GrMcv_z2kSLpp87dIpVeXk9jMzA,20600
|
|
31
31
|
librus_python_api/modern_messages.py,sha256=qt2CXtrxCZQ-jTt0aQCD6ZaPAJQZXf0BxmvacPo9B0w,10653
|
|
32
32
|
librus_python_api/notification_models.py,sha256=4eLTAE9JzHhveoaWtZQwGBolmb87QUP5kOSW7Jjt7J0,4199
|
|
@@ -45,7 +45,7 @@ librus_python_api/sending.py,sha256=Nvvk2r-6D6Us53a2fH5GW9PcA6bWEfuOvy6c3M5QVcE,
|
|
|
45
45
|
librus_python_api/service.py,sha256=7ioLx-8C-I0Qla8YUyu5UqhjBPlcUvEFpQSYH-9yjzs,96232
|
|
46
46
|
librus_python_api/timetable.py,sha256=tX1QK9fQABxzfLaHHqTe8e6rAgdOkCoTOfAHdPwNmLE,9836
|
|
47
47
|
librus_python_api/transport.py,sha256=VslHio0FrOiTkISfLNDlGVV_MeGAYidHDKZnoVUIwrI,34310
|
|
48
|
-
librus_python_api-1.6.
|
|
49
|
-
librus_python_api-1.6.
|
|
50
|
-
librus_python_api-1.6.
|
|
51
|
-
librus_python_api-1.6.
|
|
48
|
+
librus_python_api-1.6.4.dist-info/METADATA,sha256=1FRSc28E3rv-XOlguzKk2FTBstg_DnhKN2lqZw1CLZw,12103
|
|
49
|
+
librus_python_api-1.6.4.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
50
|
+
librus_python_api-1.6.4.dist-info/licenses/LICENSE,sha256=IuerLb3cnZCsaB23znydjjvKqP-LmSJd3syKa9NGUts,1071
|
|
51
|
+
librus_python_api-1.6.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|