csu 2.3.4__tar.gz → 2.3.6__tar.gz
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.
- {csu-2.3.4 → csu-2.3.6}/.bumpversion.cfg +1 -1
- {csu-2.3.4 → csu-2.3.6}/.cookiecutterrc +1 -1
- {csu-2.3.4 → csu-2.3.6}/.pre-commit-config.yaml +2 -2
- {csu-2.3.4 → csu-2.3.6}/PKG-INFO +3 -3
- {csu-2.3.4 → csu-2.3.6}/README.rst +2 -2
- {csu-2.3.4 → csu-2.3.6}/pyproject.toml +1 -1
- {csu-2.3.4 → csu-2.3.6}/src/csu/__init__.py +1 -1
- {csu-2.3.4 → csu-2.3.6}/src/csu/service.py +3 -3
- {csu-2.3.4 → csu-2.3.6}/src/csu.egg-info/PKG-INFO +3 -3
- csu-2.3.6/tests/conftest.py +186 -0
- {csu-2.3.4 → csu-2.3.6}/tests/test_service.py +9 -9
- {csu-2.3.4 → csu-2.3.6}/tests/test_service_auth.py +14 -12
- {csu-2.3.4 → csu-2.3.6}/tox.ini +1 -0
- csu-2.3.4/tests/conftest.py +0 -56
- {csu-2.3.4 → csu-2.3.6}/.coveragerc +0 -0
- {csu-2.3.4 → csu-2.3.6}/.editorconfig +0 -0
- {csu-2.3.4 → csu-2.3.6}/.github/workflows/github-actions.yml +0 -0
- {csu-2.3.4 → csu-2.3.6}/.tombi.toml +0 -0
- {csu-2.3.4 → csu-2.3.6}/AUTHORS.rst +0 -0
- {csu-2.3.4 → csu-2.3.6}/CHANGELOG.rst +0 -0
- {csu-2.3.4 → csu-2.3.6}/CONTRIBUTING.rst +0 -0
- {csu-2.3.4 → csu-2.3.6}/LICENSE +0 -0
- {csu-2.3.4 → csu-2.3.6}/MANIFEST.in +0 -0
- {csu-2.3.4 → csu-2.3.6}/ci/bootstrap.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/ci/requirements.txt +0 -0
- {csu-2.3.4 → csu-2.3.6}/ci/templates/.github/workflows/github-actions.yml +0 -0
- {csu-2.3.4 → csu-2.3.6}/pytest.ini +0 -0
- {csu-2.3.4 → csu-2.3.6}/setup.cfg +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/admin.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/conf.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/consts.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/drf/__init__.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/drf/auth.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/drf/fields.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/drf/forms.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/drf/perms.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/drf/phonenumber.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/drf/serializers.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/drf/test_auth.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/drf/test_fields.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/drf/test_forms.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/drf/test_phonenumber.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/drf/test_serializers.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/drf/views.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/enums.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/env.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/exceptions.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/export.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/fixups.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/forms/__init__.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/forms/crispy.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/forms/fields.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/gettext.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/gettext_lazy.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/locale/ro/LC_MESSAGES/django.mo +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/locale/ro/LC_MESSAGES/django.po +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/logging.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/management.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/models.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/query.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/routers.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/templates/api_exception.html +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/templates/forms/widgets/opaquewidget.html +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/test_consts.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/test_env.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/test_exceptions.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/test_logging.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/test_management.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/test_service.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/test_timezones.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/test_utils.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/test_xml.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/timezones.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/utils.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/views.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/worker/__init__.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/worker/admin.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/worker/asgi.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/worker/engine.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/worker/enums.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/worker/job.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/worker/models.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/worker/registry.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/wsgi.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu/xml.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu.egg-info/SOURCES.txt +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu.egg-info/dependency_links.txt +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu.egg-info/requires.txt +0 -0
- {csu-2.3.4 → csu-2.3.6}/src/csu.egg-info/top_level.txt +0 -0
- {csu-2.3.4 → csu-2.3.6}/tests/cassettes/test_service/test_custom_context.yaml +0 -0
- {csu-2.3.4 → csu-2.3.6}/tests/cassettes/test_service/test_custom_context_0_retries.yaml +0 -0
- {csu-2.3.4 → csu-2.3.6}/tests/cassettes/test_service/test_decoding.yaml +0 -0
- {csu-2.3.4 → csu-2.3.6}/tests/cassettes/test_service/test_error.yaml +0 -0
- {csu-2.3.4 → csu-2.3.6}/tests/cassettes/test_service/test_logging.yaml +0 -0
- {csu-2.3.4 → csu-2.3.6}/tests/cassettes/test_service/test_redirects.yaml +0 -0
- {csu-2.3.4 → csu-2.3.6}/tests/cassettes/test_service_auth/test_async.yaml +0 -0
- {csu-2.3.4 → csu-2.3.6}/tests/cassettes/test_service_auth/test_sync.yaml +0 -0
- {csu-2.3.4 → csu-2.3.6}/tests/exampleworker.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/tests/test_exampleworker.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/tests/test_models.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/tests/test_views.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/tests/test_worker.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/tests/testproject/__init__.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/tests/testproject/fixtures/testuser.json +0 -0
- {csu-2.3.4 → csu-2.3.6}/tests/testproject/models.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/tests/testproject/settings.py +0 -0
- {csu-2.3.4 → csu-2.3.6}/tests/testproject/urls.py +0 -0
|
@@ -11,14 +11,14 @@ repos:
|
|
|
11
11
|
- id: django-upgrade
|
|
12
12
|
args: [ --target-version, "5.2" ]
|
|
13
13
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
14
|
-
rev: v0.15.
|
|
14
|
+
rev: v0.15.17
|
|
15
15
|
hooks:
|
|
16
16
|
- id: ruff
|
|
17
17
|
# Using --unsafe-fixes can change code behavior
|
|
18
18
|
args: [--fix, --exit-non-zero-on-fix, --show-fixes, --unsafe-fixes]
|
|
19
19
|
- id: ruff-format
|
|
20
20
|
- repo: https://github.com/tombi-toml/tombi-pre-commit
|
|
21
|
-
rev:
|
|
21
|
+
rev: v1.1.2
|
|
22
22
|
hooks:
|
|
23
23
|
- id: tombi-format
|
|
24
24
|
# args: [ "--offline" ]
|
{csu-2.3.4 → csu-2.3.6}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: csu
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.6
|
|
4
4
|
Summary: Clean Slate Utils - bunch of utility code, mostly Django/DRF specific.
|
|
5
5
|
Author-email: Ionel Cristian Mărieș <contact@ionelmc.ro>
|
|
6
6
|
License-Expression: BSD-2-Clause
|
|
@@ -69,9 +69,9 @@ Overview
|
|
|
69
69
|
.. |supported-implementations| image:: https://img.shields.io/pypi/implementation/csu.svg
|
|
70
70
|
:alt: Supported implementations
|
|
71
71
|
:target: https://pypi.org/project/csu
|
|
72
|
-
.. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-csu/v2.3.
|
|
72
|
+
.. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-csu/v2.3.6.svg
|
|
73
73
|
:alt: Commits since latest release
|
|
74
|
-
:target: https://github.com/ionelmc/python-csu/compare/v2.3.
|
|
74
|
+
:target: https://github.com/ionelmc/python-csu/compare/v2.3.6...main
|
|
75
75
|
.. |scrutinizer| image:: https://img.shields.io/scrutinizer/quality/g/ionelmc/python-csu/main.svg
|
|
76
76
|
:alt: Scrutinizer Status
|
|
77
77
|
:target: https://scrutinizer-ci.com/g/ionelmc/python-csu/
|
|
@@ -34,9 +34,9 @@ Overview
|
|
|
34
34
|
.. |supported-implementations| image:: https://img.shields.io/pypi/implementation/csu.svg
|
|
35
35
|
:alt: Supported implementations
|
|
36
36
|
:target: https://pypi.org/project/csu
|
|
37
|
-
.. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-csu/v2.3.
|
|
37
|
+
.. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-csu/v2.3.6.svg
|
|
38
38
|
:alt: Commits since latest release
|
|
39
|
-
:target: https://github.com/ionelmc/python-csu/compare/v2.3.
|
|
39
|
+
:target: https://github.com/ionelmc/python-csu/compare/v2.3.6...main
|
|
40
40
|
.. |scrutinizer| image:: https://img.shields.io/scrutinizer/quality/g/ionelmc/python-csu/main.svg
|
|
41
41
|
:alt: Scrutinizer Status
|
|
42
42
|
:target: https://scrutinizer-ci.com/g/ionelmc/python-csu/
|
|
@@ -161,11 +161,11 @@ class HTTPServiceContext[RTYPE = HTTPServiceResponse]:
|
|
|
161
161
|
THICK_LINE,
|
|
162
162
|
)
|
|
163
163
|
|
|
164
|
-
def log_response(self, response: Response):
|
|
164
|
+
def log_response(self, response: Response, **kwargs):
|
|
165
165
|
self.handle_log_response(response, response.content)
|
|
166
166
|
|
|
167
167
|
def handle_log_response(self, response: Response | AsyncResponse, content):
|
|
168
|
-
size = len(content)
|
|
168
|
+
size = len(content) if content else 0
|
|
169
169
|
content_type = response.headers.get("content-type")
|
|
170
170
|
content_type = "no content-type" if content_type is None else repr(content_type)
|
|
171
171
|
response_line = f" response body ({content_type} - {size} bytes) ".center(LINE_LENGTH, "-")
|
|
@@ -334,7 +334,7 @@ class AsyncHTTPServiceContext[RT = HTTPServiceResponse](HTTPServiceContext[RT]):
|
|
|
334
334
|
async def log_request(self, request: PreparedRequest):
|
|
335
335
|
self.handle_log_request(request)
|
|
336
336
|
|
|
337
|
-
async def log_response(self, response: AsyncResponse | Response):
|
|
337
|
+
async def log_response(self, response: AsyncResponse | Response, **kwargs):
|
|
338
338
|
if isinstance(response, AsyncResponse):
|
|
339
339
|
content = await response.content
|
|
340
340
|
else:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: csu
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.6
|
|
4
4
|
Summary: Clean Slate Utils - bunch of utility code, mostly Django/DRF specific.
|
|
5
5
|
Author-email: Ionel Cristian Mărieș <contact@ionelmc.ro>
|
|
6
6
|
License-Expression: BSD-2-Clause
|
|
@@ -69,9 +69,9 @@ Overview
|
|
|
69
69
|
.. |supported-implementations| image:: https://img.shields.io/pypi/implementation/csu.svg
|
|
70
70
|
:alt: Supported implementations
|
|
71
71
|
:target: https://pypi.org/project/csu
|
|
72
|
-
.. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-csu/v2.3.
|
|
72
|
+
.. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-csu/v2.3.6.svg
|
|
73
73
|
:alt: Commits since latest release
|
|
74
|
-
:target: https://github.com/ionelmc/python-csu/compare/v2.3.
|
|
74
|
+
:target: https://github.com/ionelmc/python-csu/compare/v2.3.6...main
|
|
75
75
|
.. |scrutinizer| image:: https://img.shields.io/scrutinizer/quality/g/ionelmc/python-csu/main.svg
|
|
76
76
|
:alt: Scrutinizer Status
|
|
77
77
|
:target: https://scrutinizer-ci.com/g/ionelmc/python-csu/
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import itertools
|
|
3
|
+
import sys
|
|
4
|
+
import time
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from io import BytesIO
|
|
7
|
+
from logging import INFO
|
|
8
|
+
from logging import getLogger
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from types import SimpleNamespace
|
|
11
|
+
from unittest import mock
|
|
12
|
+
|
|
13
|
+
import pytest
|
|
14
|
+
from niquests import adapters
|
|
15
|
+
from niquests import async_session
|
|
16
|
+
from niquests import sessions
|
|
17
|
+
from urllib3 import AsyncHTTPConnectionPool
|
|
18
|
+
from urllib3 import AsyncHTTPResponse
|
|
19
|
+
from urllib3 import HTTPHeaderDict
|
|
20
|
+
from vcr.errors import CannotOverwriteExistingCassetteException
|
|
21
|
+
from vcr.patch import CassettePatcherBuilder
|
|
22
|
+
from vcr.request import Request
|
|
23
|
+
|
|
24
|
+
from csu import exceptions
|
|
25
|
+
|
|
26
|
+
logger = getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
_original_async_urlopen = AsyncHTTPConnectionPool.urlopen
|
|
29
|
+
_original_build = CassettePatcherBuilder.build
|
|
30
|
+
|
|
31
|
+
_DEFAULT_PORTS = {"https": 443, "http": 80}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@pytest.fixture(scope="session")
|
|
35
|
+
def vcr_config():
|
|
36
|
+
return {
|
|
37
|
+
"decode_compressed_response": True,
|
|
38
|
+
"match_on": ("method", "scheme", "host", "path", "query"), # no port because it's random
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@pytest.fixture
|
|
43
|
+
def fake_accident_id(monkeypatch):
|
|
44
|
+
calls = []
|
|
45
|
+
|
|
46
|
+
def fake_urandom(_):
|
|
47
|
+
frame = sys._getframe().f_back.f_back
|
|
48
|
+
filename = Path(frame.f_code.co_filename).name
|
|
49
|
+
calls.append(f"{filename}:{frame.f_lineno}")
|
|
50
|
+
return bytes([len(calls)])
|
|
51
|
+
|
|
52
|
+
monkeypatch.setattr(exceptions, "now", lambda: datetime(2024, 12, 12)) # noqa: DTZ001
|
|
53
|
+
monkeypatch.setattr(exceptions, "urandom", fake_urandom)
|
|
54
|
+
monkeypatch.setattr(time, "perf_counter", lambda: 0)
|
|
55
|
+
monkeypatch.setattr(sessions, "preferred_clock", lambda: 0)
|
|
56
|
+
|
|
57
|
+
return SimpleNamespace(
|
|
58
|
+
calls=calls,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def replace(text, replacements: dict[str, str]):
|
|
63
|
+
for alias, value in replacements.items():
|
|
64
|
+
text = text.replace(value, f"[&{alias}]")
|
|
65
|
+
return text
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@pytest.fixture
|
|
69
|
+
def caplogs(caplog):
|
|
70
|
+
def get_logs(**replacements):
|
|
71
|
+
return [replace(f"{record.levelname:7} | {record.message}", replacements) for record in caplog.records]
|
|
72
|
+
|
|
73
|
+
with caplog.at_level(INFO, logger="service"):
|
|
74
|
+
yield SimpleNamespace(
|
|
75
|
+
get=get_logs,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _serialize_headers(headers) -> dict[str, list[str]]:
|
|
80
|
+
out: dict[str, list[str]] = {}
|
|
81
|
+
for key, val in headers.items():
|
|
82
|
+
out.setdefault(key, []).append(val)
|
|
83
|
+
return out
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _deserialize_headers(headers: dict[str, list[str]]) -> HTTPHeaderDict:
|
|
87
|
+
hd = HTTPHeaderDict()
|
|
88
|
+
for key, values in headers.items():
|
|
89
|
+
if isinstance(values, list):
|
|
90
|
+
for v in values:
|
|
91
|
+
hd.add(key, v)
|
|
92
|
+
else:
|
|
93
|
+
hd.add(key, values)
|
|
94
|
+
return hd
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _reconstruct_uri(pool, url: str) -> str:
|
|
98
|
+
if url.startswith(("http://", "https://")):
|
|
99
|
+
return url
|
|
100
|
+
scheme = pool.scheme or "http"
|
|
101
|
+
host = pool.host
|
|
102
|
+
port = pool.port
|
|
103
|
+
port_postfix = f":{port}" if port and port != _DEFAULT_PORTS.get(scheme) else ""
|
|
104
|
+
return f"{scheme}://{host}{port_postfix}{url}"
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _build_async_response(vcr_response) -> AsyncHTTPResponse:
|
|
108
|
+
headers = dict(vcr_response.get("headers", {}))
|
|
109
|
+
for k in [h for h in headers if h.lower() == "transfer-encoding"]:
|
|
110
|
+
del headers[k]
|
|
111
|
+
body = vcr_response.get("body", {}).get("string", b"")
|
|
112
|
+
if isinstance(body, str):
|
|
113
|
+
body = body.encode("utf-8")
|
|
114
|
+
return AsyncHTTPResponse(
|
|
115
|
+
body=BytesIO(body),
|
|
116
|
+
status=vcr_response["status"]["code"],
|
|
117
|
+
reason=vcr_response["status"]["message"],
|
|
118
|
+
headers=_deserialize_headers(headers),
|
|
119
|
+
preload_content=True,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _make_async_urlopen(cassette):
|
|
124
|
+
@functools.wraps(_original_async_urlopen)
|
|
125
|
+
async def vcr_async_urlopen(self, method, url, body=None, headers=None, **kw):
|
|
126
|
+
if headers is None:
|
|
127
|
+
headers = {}
|
|
128
|
+
uri = _reconstruct_uri(self, url)
|
|
129
|
+
vcr_request = Request(method, uri, body, _serialize_headers(headers))
|
|
130
|
+
|
|
131
|
+
if cassette.can_play_response_for(vcr_request):
|
|
132
|
+
logger.info("Playing response for %s from cassette", vcr_request)
|
|
133
|
+
return _build_async_response(cassette.play_response(vcr_request))
|
|
134
|
+
|
|
135
|
+
if cassette.write_protected and cassette.filter_request(vcr_request):
|
|
136
|
+
raise CannotOverwriteExistingCassetteException(
|
|
137
|
+
cassette=cassette,
|
|
138
|
+
failed_request=vcr_request,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
logger.info("%s not in cassette, sending to real server", vcr_request)
|
|
142
|
+
response = await _original_async_urlopen(self, method, url, body=body, headers=headers, **kw)
|
|
143
|
+
resp_body = response._body
|
|
144
|
+
if resp_body is None:
|
|
145
|
+
resp_body = await response.data
|
|
146
|
+
if isinstance(resp_body, str):
|
|
147
|
+
resp_body = resp_body.encode("utf-8")
|
|
148
|
+
cassette.append(
|
|
149
|
+
vcr_request,
|
|
150
|
+
{
|
|
151
|
+
"status": {"code": response.status, "message": response.reason or ""},
|
|
152
|
+
"headers": _serialize_headers(response.headers),
|
|
153
|
+
"body": {"string": resp_body or b""},
|
|
154
|
+
},
|
|
155
|
+
)
|
|
156
|
+
return response
|
|
157
|
+
|
|
158
|
+
return vcr_async_urlopen
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _patched_build(self):
|
|
162
|
+
return itertools.chain(
|
|
163
|
+
_original_build(self),
|
|
164
|
+
(
|
|
165
|
+
mock.patch.object(
|
|
166
|
+
AsyncHTTPConnectionPool,
|
|
167
|
+
"urlopen",
|
|
168
|
+
_make_async_urlopen(self._cassette),
|
|
169
|
+
),
|
|
170
|
+
),
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
@pytest.fixture(scope="session", autouse=True)
|
|
175
|
+
def _vcr_async_urllib3():
|
|
176
|
+
"""Monkey-patch vcrpy so cassettes also cover async urllib3."""
|
|
177
|
+
CassettePatcherBuilder.build = _patched_build
|
|
178
|
+
yield
|
|
179
|
+
CassettePatcherBuilder.build = _original_build
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
@pytest.fixture(autouse=True)
|
|
183
|
+
def vcr_fix(monkeypatch):
|
|
184
|
+
monkeypatch.setattr(adapters, "preferred_clock", lambda: 0)
|
|
185
|
+
monkeypatch.setattr(sessions, "preferred_clock", lambda: 0)
|
|
186
|
+
monkeypatch.setattr(async_session, "preferred_clock", lambda: 0)
|
|
@@ -174,7 +174,7 @@ INFO | [01:000000.00] GET [&HTTPBIN]/status/599
|
|
|
174
174
|
Accept: '*/*'
|
|
175
175
|
Accept-Encoding: 'gzip, deflate, br'
|
|
176
176
|
Connection: 'keep-alive'
|
|
177
|
-
User-Agent: 'niquests/3.18.
|
|
177
|
+
User-Agent: 'niquests/3.18.6'\
|
|
178
178
|
""",
|
|
179
179
|
"""\
|
|
180
180
|
INFO | [01:000000.00] GET [&HTTPBIN]/status/599 => 599 in 0.0000s (HNone)
|
|
@@ -221,7 +221,7 @@ INFO | [01:000000.00] GET [&HTTPBIN]/status/418
|
|
|
221
221
|
Accept: '*/*'
|
|
222
222
|
Accept-Encoding: 'gzip, deflate, br'
|
|
223
223
|
Connection: 'keep-alive'
|
|
224
|
-
User-Agent: 'niquests/3.18.
|
|
224
|
+
User-Agent: 'niquests/3.18.6'\
|
|
225
225
|
""",
|
|
226
226
|
'''\
|
|
227
227
|
INFO | [01:000000.00] GET [&HTTPBIN]/status/418 => 418 in 0.0000s (HNone)
|
|
@@ -245,7 +245,7 @@ ERROR | (renderer: NoneType)
|
|
|
245
245
|
{}
|
|
246
246
|
---------------------------------------------------------------- traceback -----------------------------------------------------------------
|
|
247
247
|
Traceback (most recent call last):
|
|
248
|
-
File "[&MODELS_PATH]", line
|
|
248
|
+
File "[&MODELS_PATH]", line 1527, in json
|
|
249
249
|
return _json.loads(str(encoding_guess), **kwargs)
|
|
250
250
|
~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
251
251
|
File "[&JSON_PATH]", line 365, in loads
|
|
@@ -263,7 +263,7 @@ During handling of the above exception, another exception occurred:
|
|
|
263
263
|
Traceback (most recent call last):
|
|
264
264
|
File "[&SERVICE_PATH]", line 305, in request
|
|
265
265
|
json = response.json(parse_float=Decimal)
|
|
266
|
-
File "[&MODELS_PATH]", line
|
|
266
|
+
File "[&MODELS_PATH]", line 1529, in json
|
|
267
267
|
raise RequestsJSONDecodeError(e.msg, e.doc, e.pos)
|
|
268
268
|
niquests.exceptions.JSONDecodeError: Expecting value: line 2 column 5 (char 5)
|
|
269
269
|
|
|
@@ -312,7 +312,7 @@ INFO | [01:000000.00] GET [&HTTPBIN]/status/599
|
|
|
312
312
|
Accept: '*/*'
|
|
313
313
|
Accept-Encoding: 'gzip, deflate, br'
|
|
314
314
|
Connection: 'keep-alive'
|
|
315
|
-
User-Agent: 'niquests/3.18.
|
|
315
|
+
User-Agent: 'niquests/3.18.6'\
|
|
316
316
|
""",
|
|
317
317
|
"""\
|
|
318
318
|
INFO | [01:000000.00] GET [&HTTPBIN]/status/599 => 599 in 0.0000s (HNone)
|
|
@@ -520,7 +520,7 @@ INFO | [01:000000.00] POST [&HTTPBIN]/anything?foo=bar&accept_statuses=%7B999
|
|
|
520
520
|
Accept-Encoding: 'gzip, deflate, br'
|
|
521
521
|
Connection: 'keep-alive'
|
|
522
522
|
Content-Length: '27'
|
|
523
|
-
User-Agent: 'niquests/3.18.
|
|
523
|
+
User-Agent: 'niquests/3.18.6'
|
|
524
524
|
------------------------------------------------ request body (no content-type - 27 bytes) -------------------------------------------------
|
|
525
525
|
"config={'custom': 'config'}"
|
|
526
526
|
============================================================================================================================================\
|
|
@@ -557,7 +557,7 @@ INFO | [01:000000.00] POST [&HTTPBIN]/anything?foo=bar&accept_statuses=%7B999
|
|
|
557
557
|
Accept-Encoding: 'gzip, deflate, br'
|
|
558
558
|
Connection: 'keep-alive'
|
|
559
559
|
Content-Length: '27'
|
|
560
|
-
User-Agent: 'niquests/3.18.
|
|
560
|
+
User-Agent: 'niquests/3.18.6'
|
|
561
561
|
------------------------------------------------ request body (no content-type - 27 bytes) -------------------------------------------------
|
|
562
562
|
"config={'custom': 'config'}"
|
|
563
563
|
============================================================================================================================================\
|
|
@@ -594,7 +594,7 @@ INFO | [01:000000.00] POST [&HTTPBIN]/anything?foo=bar&accept_statuses=%7B999
|
|
|
594
594
|
Accept-Encoding: 'gzip, deflate, br'
|
|
595
595
|
Connection: 'keep-alive'
|
|
596
596
|
Content-Length: '27'
|
|
597
|
-
User-Agent: 'niquests/3.18.
|
|
597
|
+
User-Agent: 'niquests/3.18.6'
|
|
598
598
|
------------------------------------------------ request body (no content-type - 27 bytes) -------------------------------------------------
|
|
599
599
|
"config={'custom': 'config'}"
|
|
600
600
|
============================================================================================================================================\
|
|
@@ -631,7 +631,7 @@ INFO | [01:000000.00] POST [&HTTPBIN]/anything?foo=bar&accept_statuses=%7B999
|
|
|
631
631
|
Accept-Encoding: 'gzip, deflate, br'
|
|
632
632
|
Connection: 'keep-alive'
|
|
633
633
|
Content-Length: '27'
|
|
634
|
-
User-Agent: 'niquests/3.18.
|
|
634
|
+
User-Agent: 'niquests/3.18.6'
|
|
635
635
|
------------------------------------------------ request body (no content-type - 27 bytes) -------------------------------------------------
|
|
636
636
|
"config={'custom': 'config'}"
|
|
637
637
|
============================================================================================================================================\
|
|
@@ -59,7 +59,7 @@ INFO | [01:000000.00] POST http://[&HTTPBIN]/post
|
|
|
59
59
|
Connection: 'keep-alive'
|
|
60
60
|
Content-Length: '15'
|
|
61
61
|
Content-Type: 'application/json;charset=utf-8'
|
|
62
|
-
User-Agent: 'niquests/3.18.
|
|
62
|
+
User-Agent: 'niquests/3.18.6'
|
|
63
63
|
---------------------------------------- request body ('application/json;charset=utf-8' - 15 bytes) ----------------------------------------
|
|
64
64
|
b'{"foobar": 123}'
|
|
65
65
|
============================================================================================================================================\
|
|
@@ -84,7 +84,7 @@ INFO | [01:000000.00] GET http://[&HTTPBIN]/json
|
|
|
84
84
|
Accept: '*/*'
|
|
85
85
|
Accept-Encoding: 'gzip, deflate, br'
|
|
86
86
|
Connection: 'keep-alive'
|
|
87
|
-
User-Agent: 'niquests/3.18.
|
|
87
|
+
User-Agent: 'niquests/3.18.6'
|
|
88
88
|
X-Auth: '123'\
|
|
89
89
|
""",
|
|
90
90
|
"""\
|
|
@@ -124,23 +124,24 @@ INFO | [01:000000.00] POST [&HTTPBIN]/post
|
|
|
124
124
|
Connection: 'keep-alive'
|
|
125
125
|
Content-Length: '15'
|
|
126
126
|
Content-Type: 'application/json;charset=utf-8'
|
|
127
|
-
User-Agent: 'niquests/3.18.
|
|
128
|
-
|
|
127
|
+
User-Agent: 'niquests/3.18.6'
|
|
128
|
+
---------------------------------------- request body ('application/json;charset=utf-8' - 15 bytes) ----------------------------------------
|
|
129
129
|
b'{"foobar": 123}'
|
|
130
130
|
============================================================================================================================================\
|
|
131
131
|
""",
|
|
132
|
+
"INFO | Playing response for <Request (POST) [&HTTPBIN]/post> from cassette",
|
|
132
133
|
"""\
|
|
133
|
-
INFO | [01:000000.00] POST [&HTTPBIN]/post => 200 in 0.
|
|
134
|
+
INFO | [01:000000.00] POST [&HTTPBIN]/post => 200 in 0.0000s (H0)
|
|
134
135
|
------------------------------------------------------------- response headers -------------------------------------------------------------
|
|
135
136
|
Access-Control-Allow-Credentials: 'true'
|
|
136
137
|
Access-Control-Allow-Origin: '*'
|
|
137
138
|
Connection: 'Close'
|
|
138
139
|
Content-Length: '356'
|
|
139
140
|
Content-Type: 'application/json'
|
|
140
|
-
Date: 'Thu, 12 Mar 2026 20:
|
|
141
|
+
Date: 'Thu, 12 Mar 2026 20:21:07 GMT'
|
|
141
142
|
Server: 'Pytest-HTTPBIN/0.1.0'
|
|
142
|
-
---------------------------------------------- response body ('application/json' - 356
|
|
143
|
-
b'{"args":{},"data":"{\\\\"foobar\\\\": 123}","files":{},"form":{},"headers":{"Accept":"*/*","Accept-Encoding":"gzip, deflate, br","Connection":"keep-alive","Content-Length":"15","Content-Type":"application/json;charset=utf-8","Host":"127.0.0.1:
|
|
143
|
+
---------------------------------------------- response body ('application/json' - 356 bytes) ----------------------------------------------
|
|
144
|
+
b'{"args":{},"data":"{\\\\"foobar\\\\": 123}","files":{},"form":{},"headers":{"Accept":"*/*","Accept-Encoding":"gzip, deflate, br","Connection":"keep-alive","Content-Length":"15","Content-Type":"application/json;charset=utf-8","Host":"127.0.0.1:44101","User-Agent":"niquests/3.18.1"},"json":{"foobar":123},"origin":"127.0.0.1","url":"http://127.0.0.1:44101/post"}\\n'
|
|
144
145
|
============================================================================================================================================\
|
|
145
146
|
""",
|
|
146
147
|
"""\
|
|
@@ -149,20 +150,21 @@ INFO | [01:000000.00] GET [&HTTPBIN]/json
|
|
|
149
150
|
Accept: '*/*'
|
|
150
151
|
Accept-Encoding: 'gzip, deflate, br'
|
|
151
152
|
Connection: 'keep-alive'
|
|
152
|
-
User-Agent: 'niquests/3.18.
|
|
153
|
+
User-Agent: 'niquests/3.18.6'
|
|
153
154
|
X-Auth: '123'\
|
|
154
155
|
""",
|
|
156
|
+
"INFO | Playing response for <Request (GET) [&HTTPBIN]/json> from cassette",
|
|
155
157
|
"""\
|
|
156
|
-
INFO | [01:000000.00] GET [&HTTPBIN]/json => 200 in 0.
|
|
158
|
+
INFO | [01:000000.00] GET [&HTTPBIN]/json => 200 in 0.0000s (H0)
|
|
157
159
|
------------------------------------------------------------- response headers -------------------------------------------------------------
|
|
158
160
|
Access-Control-Allow-Credentials: 'true'
|
|
159
161
|
Access-Control-Allow-Origin: '*'
|
|
160
162
|
Connection: 'Close'
|
|
161
163
|
Content-Length: '275'
|
|
162
164
|
Content-Type: 'application/json'
|
|
163
|
-
Date: 'Thu, 12 Mar 2026 20:
|
|
165
|
+
Date: 'Thu, 12 Mar 2026 20:21:07 GMT'
|
|
164
166
|
Server: 'Pytest-HTTPBIN/0.1.0'
|
|
165
|
-
---------------------------------------------- response body ('application/json' - 275
|
|
167
|
+
---------------------------------------------- response body ('application/json' - 275 bytes) ----------------------------------------------
|
|
166
168
|
b'{"slideshow":{"author":"Yours Truly","date":"date of publication","slides":[{"title":"Wake up to WonderWidgets!","type":"all"},{"items":["Why <em>WonderWidgets</em> are great","Who <em>buys</em> WonderWidgets"],"title":"Overview","type":"all"}],"title":"Sample Slide Show"}}\\n'
|
|
167
169
|
============================================================================================================================================\
|
|
168
170
|
""",
|
{csu-2.3.4 → csu-2.3.6}/tox.ini
RENAMED
csu-2.3.4/tests/conftest.py
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import sys
|
|
3
|
-
import time
|
|
4
|
-
from datetime import datetime
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
from types import SimpleNamespace
|
|
7
|
-
|
|
8
|
-
import pytest
|
|
9
|
-
from niquests import sessions
|
|
10
|
-
|
|
11
|
-
from csu import exceptions
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
@pytest.fixture(scope="session")
|
|
15
|
-
def vcr_config():
|
|
16
|
-
return {
|
|
17
|
-
"decode_compressed_response": True,
|
|
18
|
-
"match_on": ("method", "scheme", "host", "path", "query"), # no port because it's random
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
@pytest.fixture
|
|
23
|
-
def fake_accident_id(monkeypatch):
|
|
24
|
-
calls = []
|
|
25
|
-
|
|
26
|
-
def fake_urandom(_):
|
|
27
|
-
frame = sys._getframe().f_back.f_back
|
|
28
|
-
filename = Path(frame.f_code.co_filename).name
|
|
29
|
-
calls.append(f"{filename}:{frame.f_lineno}")
|
|
30
|
-
return bytes([len(calls)])
|
|
31
|
-
|
|
32
|
-
monkeypatch.setattr(exceptions, "now", lambda: datetime(2024, 12, 12)) # noqa: DTZ001
|
|
33
|
-
monkeypatch.setattr(exceptions, "urandom", fake_urandom)
|
|
34
|
-
monkeypatch.setattr(time, "perf_counter", lambda: 0)
|
|
35
|
-
monkeypatch.setattr(sessions, "preferred_clock", lambda: 0)
|
|
36
|
-
|
|
37
|
-
return SimpleNamespace(
|
|
38
|
-
calls=calls,
|
|
39
|
-
)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def replace(text, replacements: dict[str, str]):
|
|
43
|
-
for alias, value in replacements.items():
|
|
44
|
-
text = text.replace(value, f"[&{alias}]")
|
|
45
|
-
return text
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
@pytest.fixture
|
|
49
|
-
def caplogs(caplog):
|
|
50
|
-
def get_logs(**replacements):
|
|
51
|
-
return [replace(f"{record.levelname:7} | {record.message}", replacements) for record in caplog.records]
|
|
52
|
-
|
|
53
|
-
with caplog.at_level(logging.INFO, logger="service"):
|
|
54
|
-
yield SimpleNamespace(
|
|
55
|
-
get=get_logs,
|
|
56
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{csu-2.3.4 → csu-2.3.6}/LICENSE
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|