csu 2.4.2__tar.gz → 2.5.0__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.
Files changed (108) hide show
  1. {csu-2.4.2 → csu-2.5.0}/.bumpversion.cfg +1 -1
  2. {csu-2.4.2 → csu-2.5.0}/.cookiecutterrc +1 -1
  3. {csu-2.4.2 → csu-2.5.0}/.pre-commit-config.yaml +3 -3
  4. {csu-2.4.2 → csu-2.5.0}/PKG-INFO +3 -3
  5. {csu-2.4.2 → csu-2.5.0}/README.rst +2 -2
  6. {csu-2.4.2 → csu-2.5.0}/pyproject.toml +1 -1
  7. {csu-2.4.2 → csu-2.5.0}/src/csu/__init__.py +1 -1
  8. {csu-2.4.2 → csu-2.5.0}/src/csu/conf.py +7 -5
  9. {csu-2.4.2 → csu-2.5.0}/src/csu/forms/fields.py +1 -6
  10. {csu-2.4.2 → csu-2.5.0}/src/csu/logging.py +23 -5
  11. {csu-2.4.2 → csu-2.5.0}/src/csu/service.py +19 -13
  12. {csu-2.4.2 → csu-2.5.0}/src/csu/xml.py +2 -2
  13. {csu-2.4.2 → csu-2.5.0}/src/csu.egg-info/PKG-INFO +3 -3
  14. {csu-2.4.2 → csu-2.5.0}/tests/test_service.py +12 -12
  15. {csu-2.4.2 → csu-2.5.0}/tests/testproject/settings.py +3 -2
  16. {csu-2.4.2 → csu-2.5.0}/.coveragerc +0 -0
  17. {csu-2.4.2 → csu-2.5.0}/.editorconfig +0 -0
  18. {csu-2.4.2 → csu-2.5.0}/.github/workflows/github-actions.yml +0 -0
  19. {csu-2.4.2 → csu-2.5.0}/.tombi.toml +0 -0
  20. {csu-2.4.2 → csu-2.5.0}/AUTHORS.rst +0 -0
  21. {csu-2.4.2 → csu-2.5.0}/CHANGELOG.rst +0 -0
  22. {csu-2.4.2 → csu-2.5.0}/CONTRIBUTING.rst +0 -0
  23. {csu-2.4.2 → csu-2.5.0}/LICENSE +0 -0
  24. {csu-2.4.2 → csu-2.5.0}/MANIFEST.in +0 -0
  25. {csu-2.4.2 → csu-2.5.0}/ci/bootstrap.py +0 -0
  26. {csu-2.4.2 → csu-2.5.0}/ci/requirements.txt +0 -0
  27. {csu-2.4.2 → csu-2.5.0}/ci/templates/.github/workflows/github-actions.yml +0 -0
  28. {csu-2.4.2 → csu-2.5.0}/pytest.ini +0 -0
  29. {csu-2.4.2 → csu-2.5.0}/setup.cfg +0 -0
  30. {csu-2.4.2 → csu-2.5.0}/src/csu/admin.py +0 -0
  31. {csu-2.4.2 → csu-2.5.0}/src/csu/consts.py +0 -0
  32. {csu-2.4.2 → csu-2.5.0}/src/csu/drf/__init__.py +0 -0
  33. {csu-2.4.2 → csu-2.5.0}/src/csu/drf/auth.py +0 -0
  34. {csu-2.4.2 → csu-2.5.0}/src/csu/drf/fields.py +0 -0
  35. {csu-2.4.2 → csu-2.5.0}/src/csu/drf/forms.py +0 -0
  36. {csu-2.4.2 → csu-2.5.0}/src/csu/drf/perms.py +0 -0
  37. {csu-2.4.2 → csu-2.5.0}/src/csu/drf/phonenumber.py +0 -0
  38. {csu-2.4.2 → csu-2.5.0}/src/csu/drf/serializers.py +0 -0
  39. {csu-2.4.2 → csu-2.5.0}/src/csu/drf/test_auth.py +0 -0
  40. {csu-2.4.2 → csu-2.5.0}/src/csu/drf/test_fields.py +0 -0
  41. {csu-2.4.2 → csu-2.5.0}/src/csu/drf/test_forms.py +0 -0
  42. {csu-2.4.2 → csu-2.5.0}/src/csu/drf/test_phonenumber.py +0 -0
  43. {csu-2.4.2 → csu-2.5.0}/src/csu/drf/test_serializers.py +0 -0
  44. {csu-2.4.2 → csu-2.5.0}/src/csu/drf/views.py +0 -0
  45. {csu-2.4.2 → csu-2.5.0}/src/csu/enums.py +0 -0
  46. {csu-2.4.2 → csu-2.5.0}/src/csu/env.py +0 -0
  47. {csu-2.4.2 → csu-2.5.0}/src/csu/exceptions.py +0 -0
  48. {csu-2.4.2 → csu-2.5.0}/src/csu/export.py +0 -0
  49. {csu-2.4.2 → csu-2.5.0}/src/csu/fixups.py +0 -0
  50. {csu-2.4.2 → csu-2.5.0}/src/csu/forms/__init__.py +0 -0
  51. {csu-2.4.2 → csu-2.5.0}/src/csu/forms/crispy.py +0 -0
  52. {csu-2.4.2 → csu-2.5.0}/src/csu/gettext.py +0 -0
  53. {csu-2.4.2 → csu-2.5.0}/src/csu/gettext_lazy.py +0 -0
  54. {csu-2.4.2 → csu-2.5.0}/src/csu/locale/ro/LC_MESSAGES/django.mo +0 -0
  55. {csu-2.4.2 → csu-2.5.0}/src/csu/locale/ro/LC_MESSAGES/django.po +0 -0
  56. {csu-2.4.2 → csu-2.5.0}/src/csu/management.py +0 -0
  57. {csu-2.4.2 → csu-2.5.0}/src/csu/models.py +0 -0
  58. {csu-2.4.2 → csu-2.5.0}/src/csu/query.py +0 -0
  59. {csu-2.4.2 → csu-2.5.0}/src/csu/routers.py +0 -0
  60. {csu-2.4.2 → csu-2.5.0}/src/csu/templates/api_exception.html +0 -0
  61. {csu-2.4.2 → csu-2.5.0}/src/csu/templates/forms/widgets/opaquewidget.html +0 -0
  62. {csu-2.4.2 → csu-2.5.0}/src/csu/test_consts.py +0 -0
  63. {csu-2.4.2 → csu-2.5.0}/src/csu/test_env.py +0 -0
  64. {csu-2.4.2 → csu-2.5.0}/src/csu/test_exceptions.py +0 -0
  65. {csu-2.4.2 → csu-2.5.0}/src/csu/test_logging.py +0 -0
  66. {csu-2.4.2 → csu-2.5.0}/src/csu/test_management.py +0 -0
  67. {csu-2.4.2 → csu-2.5.0}/src/csu/test_service.py +0 -0
  68. {csu-2.4.2 → csu-2.5.0}/src/csu/test_timezones.py +0 -0
  69. {csu-2.4.2 → csu-2.5.0}/src/csu/test_utils.py +0 -0
  70. {csu-2.4.2 → csu-2.5.0}/src/csu/test_xml.py +0 -0
  71. {csu-2.4.2 → csu-2.5.0}/src/csu/timezones.py +0 -0
  72. {csu-2.4.2 → csu-2.5.0}/src/csu/utils.py +0 -0
  73. {csu-2.4.2 → csu-2.5.0}/src/csu/views.py +0 -0
  74. {csu-2.4.2 → csu-2.5.0}/src/csu/worker/__init__.py +0 -0
  75. {csu-2.4.2 → csu-2.5.0}/src/csu/worker/admin.py +0 -0
  76. {csu-2.4.2 → csu-2.5.0}/src/csu/worker/asgi.py +0 -0
  77. {csu-2.4.2 → csu-2.5.0}/src/csu/worker/engine.py +0 -0
  78. {csu-2.4.2 → csu-2.5.0}/src/csu/worker/enums.py +0 -0
  79. {csu-2.4.2 → csu-2.5.0}/src/csu/worker/job.py +0 -0
  80. {csu-2.4.2 → csu-2.5.0}/src/csu/worker/models.py +0 -0
  81. {csu-2.4.2 → csu-2.5.0}/src/csu/worker/registry.py +0 -0
  82. {csu-2.4.2 → csu-2.5.0}/src/csu/wsgi.py +0 -0
  83. {csu-2.4.2 → csu-2.5.0}/src/csu.egg-info/SOURCES.txt +0 -0
  84. {csu-2.4.2 → csu-2.5.0}/src/csu.egg-info/dependency_links.txt +0 -0
  85. {csu-2.4.2 → csu-2.5.0}/src/csu.egg-info/requires.txt +0 -0
  86. {csu-2.4.2 → csu-2.5.0}/src/csu.egg-info/top_level.txt +0 -0
  87. {csu-2.4.2 → csu-2.5.0}/tests/cassettes/test_service/test_custom_context.yaml +0 -0
  88. {csu-2.4.2 → csu-2.5.0}/tests/cassettes/test_service/test_custom_context_0_retries.yaml +0 -0
  89. {csu-2.4.2 → csu-2.5.0}/tests/cassettes/test_service/test_decoding.yaml +0 -0
  90. {csu-2.4.2 → csu-2.5.0}/tests/cassettes/test_service/test_error.yaml +0 -0
  91. {csu-2.4.2 → csu-2.5.0}/tests/cassettes/test_service/test_logging.yaml +0 -0
  92. {csu-2.4.2 → csu-2.5.0}/tests/cassettes/test_service/test_redirects.yaml +0 -0
  93. {csu-2.4.2 → csu-2.5.0}/tests/cassettes/test_service_auth/test_async.yaml +0 -0
  94. {csu-2.4.2 → csu-2.5.0}/tests/cassettes/test_service_auth/test_async_auth.yaml +0 -0
  95. {csu-2.4.2 → csu-2.5.0}/tests/cassettes/test_service_auth/test_sync.yaml +0 -0
  96. {csu-2.4.2 → csu-2.5.0}/tests/cassettes/test_service_auth/test_sync_auth.yaml +0 -0
  97. {csu-2.4.2 → csu-2.5.0}/tests/conftest.py +0 -0
  98. {csu-2.4.2 → csu-2.5.0}/tests/exampleworker.py +0 -0
  99. {csu-2.4.2 → csu-2.5.0}/tests/test_exampleworker.py +0 -0
  100. {csu-2.4.2 → csu-2.5.0}/tests/test_models.py +0 -0
  101. {csu-2.4.2 → csu-2.5.0}/tests/test_service_auth.py +0 -0
  102. {csu-2.4.2 → csu-2.5.0}/tests/test_views.py +0 -0
  103. {csu-2.4.2 → csu-2.5.0}/tests/test_worker.py +0 -0
  104. {csu-2.4.2 → csu-2.5.0}/tests/testproject/__init__.py +0 -0
  105. {csu-2.4.2 → csu-2.5.0}/tests/testproject/fixtures/testuser.json +0 -0
  106. {csu-2.4.2 → csu-2.5.0}/tests/testproject/models.py +0 -0
  107. {csu-2.4.2 → csu-2.5.0}/tests/testproject/urls.py +0 -0
  108. {csu-2.4.2 → csu-2.5.0}/tox.ini +0 -0
@@ -1,5 +1,5 @@
1
1
  [bumpversion]
2
- current_version = 2.4.2
2
+ current_version = 2.5.0
3
3
  commit = True
4
4
  tag = True
5
5
 
@@ -41,7 +41,7 @@ default_context:
41
41
  sphinx_theme: furo
42
42
  test_matrix_separate_coverage: false
43
43
  tests_inside_package: true
44
- version: 2.4.2
44
+ version: 2.5.0
45
45
  version_manager: bump2version
46
46
  website: https://blog.ionelmc.ro
47
47
  year_from: '2024'
@@ -6,19 +6,19 @@ exclude: '^(\.tox|ci/templates|\.bumpversion\.cfg)(/|$)'
6
6
  # Note the order is intentional to avoid multiple passes of the hooks
7
7
  repos:
8
8
  - repo: https://github.com/adamchainz/django-upgrade
9
- rev: 1.30.0
9
+ rev: 1.32.0
10
10
  hooks:
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.17
14
+ rev: v0.16.6
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: v1.1.2
21
+ rev: v1.5.1
22
22
  hooks:
23
23
  - id: tombi-format
24
24
  # args: [ "--offline" ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: csu
3
- Version: 2.4.2
3
+ Version: 2.5.0
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.4.2.svg
72
+ .. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-csu/v2.5.0.svg
73
73
  :alt: Commits since latest release
74
- :target: https://github.com/ionelmc/python-csu/compare/v2.4.2...main
74
+ :target: https://github.com/ionelmc/python-csu/compare/v2.5.0...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.4.2.svg
37
+ .. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-csu/v2.5.0.svg
38
38
  :alt: Commits since latest release
39
- :target: https://github.com/ionelmc/python-csu/compare/v2.4.2...main
39
+ :target: https://github.com/ionelmc/python-csu/compare/v2.5.0...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/
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "csu"
3
- version = "2.4.2"
3
+ version = "2.5.0"
4
4
  description = "Clean Slate Utils - bunch of utility code, mostly Django/DRF specific."
5
5
  requires-python = ">=3.14"
6
6
  license = "BSD-2-Clause"
@@ -2,4 +2,4 @@
2
2
  a.k.a Clean Slate Utils
3
3
  """
4
4
 
5
- __version__ = "2.4.2"
5
+ __version__ = "2.5.0"
@@ -25,10 +25,12 @@ LOGGING_AUTH_INFO_FIELDS = getattr(settings, "CSU_LOGGING_AUTH_INFO_FIELDS", ("_
25
25
  assert isinstance(LOGGING_AUTH_INFO_FIELDS, list | tuple), f"Expected CSU_LOGGING_AUTH_INFO_FIELDS={LOGGING_AUTH_INFO_FIELDS!r} to be a list or tuple."
26
26
  LOGGING_TB_LIMIT = getattr(settings, "CSU_LOGGING_TB_LIMIT", 5)
27
27
  LOGGING_REQUEST_BODY_LIMIT = getattr(settings, "CSU_LOGGING_REQUEST_BODY_LIMIT", 102400)
28
- LOGGING_RESPONSE_BODY_LIMIT = getattr(settings, "CSU_LOGGING_RESPONSE_BODY_LIMIT", 102400)
28
+ LOGGING_RESPONSE_TEXT_LIMIT = getattr(settings, "LOGGING_RESPONSE_TEXT_LIMIT", 102400)
29
+ LOGGING_RESPONSE_BINARY_LIMIT = getattr(settings, "LOGGING_RESPONSE_BINARY_LIMIT", 64)
29
30
  LOGGING_SHOW_HEADERS = getattr(settings, "CSU_LOGGING_SHOW_HEADERS", False)
30
31
 
31
- SERVICE_REQUEST_CONTENT_LOG_LIMIT = getattr(settings, "CSU_SERVICE_REQUEST_CONTENT_LOG_LIMIT", None)
32
- SERVICE_REQUEST_SHOW_HEADERS = getattr(settings, "CSU_SERVICE_REQUEST_SHOW_HEADERS", False)
33
- SERVICE_RESPONSE_CONTENT_LOG_LIMIT = getattr(settings, "CSU_SERVICE_RESPONSE_CONTENT_LOG_LIMIT", 10240)
34
- SERVICE_RESPONSE_SHOW_HEADERS = getattr(settings, "CSU_SERVICE_RESPONSE_SHOW_HEADERS", False)
32
+ SERVICE_LOGGING_REQUEST_BODY_LIMIT = getattr(settings, "CSU_SERVICE_LOGGING_REQUEST_BODY_LIMIT", None)
33
+ SERVICE_LOGGING_REQUEST_SHOW_HEADERS = getattr(settings, "CSU_SERVICE_LOGGING_REQUEST_SHOW_HEADERS", False)
34
+ SERVICE_LOGGING_RESPONSE_TEXT_LIMIT = getattr(settings, "CSU_SERVICE_LOGGING_RESPONSE_TEXT_LIMIT", 102400)
35
+ SERVICE_LOGGING_RESPONSE_BINARY_LIMIT = getattr(settings, "CSU_SERVICE_LOGGING_RESPONSE_BINARY_LIMIT", 64)
36
+ SERVICE_LOGGING_RESPONSE_SHOW_HEADERS = getattr(settings, "CSU_SERVICE_LOGGING_RESPONSE_SHOW_HEADERS", False)
@@ -34,17 +34,12 @@ class OpaqueField(forms.MultiValueField, forms.FileField):
34
34
  value_widget: type
35
35
  value_field: type
36
36
 
37
- def __init__(self, required=UNSPECIFIED, widget=UNSPECIFIED, disabled=UNSPECIFIED, **kwargs):
38
- if not (required is UNSPECIFIED or required):
39
- raise TypeError("Cannot make this field not mandatory.")
37
+ def __init__(self, widget=UNSPECIFIED, **kwargs):
40
38
  if widget is not UNSPECIFIED:
41
39
  raise TypeError("Cannot override widget.")
42
- if not (disabled is UNSPECIFIED or not disabled):
43
- raise TypeError("Cannot disable this field.")
44
40
  super().__init__(
45
41
  fields=[forms.CharField(required=False), self.value_field(required=False)],
46
42
  widget=OpaqueWidget(self.value_widget, self.display_value),
47
- required=False,
48
43
  require_all_fields=False,
49
44
  **kwargs,
50
45
  )
@@ -11,7 +11,8 @@ from rest_framework.request import Request
11
11
  from rest_framework.response import Response
12
12
 
13
13
  from .conf import LOGGING_REQUEST_BODY_LIMIT
14
- from .conf import LOGGING_RESPONSE_BODY_LIMIT
14
+ from .conf import LOGGING_RESPONSE_BINARY_LIMIT
15
+ from .conf import LOGGING_RESPONSE_TEXT_LIMIT
15
16
  from .conf import LOGGING_SHOW_HEADERS
16
17
  from .consts import LINE_LENGTH
17
18
  from .consts import REQUEST_DATA_LINE
@@ -24,6 +25,12 @@ from .consts import THICK_LINE
24
25
  default_logger = getLogger("csu")
25
26
 
26
27
 
28
+ def is_text_content_type(content_type: str) -> bool:
29
+ return (
30
+ content_type.startswith("text/") or content_type.endswith(("json", "plain", "xml")) or any(part in content_type for part in ("json", "script", "xml"))
31
+ )
32
+
33
+
27
34
  def get_remote_addresses(environ, placeholder=None):
28
35
  remote_addr = [environ.get("REMOTE_ADDR", placeholder)]
29
36
  forwarded_for = environ.get("HTTP_X_FORWARDED_FOR", "").split(",")
@@ -63,7 +70,7 @@ def get_request_line(environ, status_code, auth=None, *extra_auth):
63
70
  return format, arguments
64
71
 
65
72
 
66
- def fa_append_repr(format: list[str], arguments: list, value, limit: None | int):
73
+ def fa_append_repr(format: list[str], arguments: list, value, limit: int | None):
67
74
  value = repr(value)
68
75
  if limit and (size := len(value)) > limit:
69
76
  format.append("%s\n ... %s more characters")
@@ -111,19 +118,30 @@ def get_content_lines(request: Request | HttpRequest | ASGIRequest, response: Re
111
118
  "\n ".join(f"{name}: {value}" for name, value in request.headers.items()),
112
119
  )
113
120
  if response:
121
+ if response.headers:
122
+ format.append("\n%s\n %s")
123
+ arguments += (
124
+ " response headers ".center(LINE_LENGTH, "-"),
125
+ "\n ".join(f"{name}: {value}" for name, value in response.headers.items()),
126
+ )
127
+ content_type = response.headers.get("content-type")
128
+ else:
129
+ content_type = None
130
+
131
+ is_text = is_text_content_type(content_type or "text/")
114
132
  try:
115
133
  if getattr(response, "is_rendered", True) and response.content:
116
134
  format.append("\n%s\n ")
117
135
  arguments.append(RESPONSE_CONTENT_LINE)
118
- fa_append_repr(format, arguments, response.content, LOGGING_RESPONSE_BODY_LIMIT)
136
+ fa_append_repr(format, arguments, response.content, LOGGING_RESPONSE_TEXT_LIMIT if is_text else LOGGING_RESPONSE_BINARY_LIMIT)
119
137
  elif hasattr(response, "data") and response.data is not None:
120
138
  format.append("\n%s\n ")
121
139
  arguments.append(RESPONSE_DATA_LINE)
122
- fa_append_repr(format, arguments, response.data, LOGGING_RESPONSE_BODY_LIMIT)
140
+ fa_append_repr(format, arguments, response.data, LOGGING_RESPONSE_TEXT_LIMIT if is_text else LOGGING_RESPONSE_BINARY_LIMIT)
123
141
  else:
124
142
  format.append("\n%s\n ")
125
143
  arguments.append(RESPONSE_LINE)
126
- fa_append_repr(format, arguments, response, LOGGING_RESPONSE_BODY_LIMIT)
144
+ fa_append_repr(format, arguments, response, LOGGING_RESPONSE_TEXT_LIMIT if is_text else LOGGING_RESPONSE_BINARY_LIMIT)
127
145
  except Exception as exc:
128
146
  format.append("\n%s\n Failed to get response: %s")
129
147
  arguments += (
@@ -19,10 +19,11 @@ from niquests.exceptions import ConnectionError
19
19
  from niquests.typing import TLSClientCertType
20
20
  from niquests.typing import TLSVerifyType
21
21
 
22
- from .conf import SERVICE_REQUEST_CONTENT_LOG_LIMIT
23
- from .conf import SERVICE_REQUEST_SHOW_HEADERS
24
- from .conf import SERVICE_RESPONSE_CONTENT_LOG_LIMIT
25
- from .conf import SERVICE_RESPONSE_SHOW_HEADERS
22
+ from .conf import SERVICE_LOGGING_REQUEST_BODY_LIMIT
23
+ from .conf import SERVICE_LOGGING_REQUEST_SHOW_HEADERS
24
+ from .conf import SERVICE_LOGGING_RESPONSE_BINARY_LIMIT
25
+ from .conf import SERVICE_LOGGING_RESPONSE_SHOW_HEADERS
26
+ from .conf import SERVICE_LOGGING_RESPONSE_TEXT_LIMIT
26
27
  from .consts import LINE_LENGTH
27
28
  from .consts import THICK_LINE
28
29
  from .exceptions import DecodeError
@@ -33,13 +34,14 @@ from .exceptions import RetryableError
33
34
  from .exceptions import RetryableStatusError
34
35
  from .exceptions import UnexpectedStatusError
35
36
  from .exceptions import get_event_id
37
+ from .logging import is_text_content_type
36
38
 
37
39
 
38
40
  class HTTPServiceResponse:
39
41
  status: int
40
42
  http_response: Response | AsyncResponse
41
43
  event_id: str
42
- json: None | list | dict = None
44
+ json: list | dict | None = None
43
45
  text: str
44
46
  content: bytes
45
47
 
@@ -89,8 +91,11 @@ class HTTPServiceContext[RTYPE = HTTPServiceResponse]:
89
91
  client_class = Session
90
92
  client: Session
91
93
  logger: Logger
92
- request_content_log_limit = SERVICE_REQUEST_CONTENT_LOG_LIMIT
93
- response_content_log_limit = SERVICE_RESPONSE_CONTENT_LOG_LIMIT
94
+ log_request_body_limit = SERVICE_LOGGING_REQUEST_BODY_LIMIT
95
+ log_request_show_headers = SERVICE_LOGGING_REQUEST_SHOW_HEADERS
96
+ log_response_text_limit = SERVICE_LOGGING_RESPONSE_TEXT_LIMIT
97
+ log_response_binary_limit = SERVICE_LOGGING_RESPONSE_BINARY_LIMIT
98
+ log_response_show_headers = SERVICE_LOGGING_RESPONSE_SHOW_HEADERS
94
99
 
95
100
  def __init__(self, service: HTTPService[Self], event_id=None, auth=None, proxy=None, **kwargs):
96
101
  if auth is None:
@@ -123,14 +128,14 @@ class HTTPServiceContext[RTYPE = HTTPServiceResponse]:
123
128
  self.handle_log_request(request)
124
129
 
125
130
  def handle_log_request(self, request: PreparedRequest):
126
- if SERVICE_REQUEST_SHOW_HEADERS:
131
+ if self.log_request_show_headers:
127
132
  header = " request headers ".center(LINE_LENGTH, "-"), "\n ".join(f"{k}: {v!r}" for k, v in sorted(request.headers.items()))
128
133
  else:
129
134
  header = (request.headers.get("authorization", ""),)
130
135
 
131
136
  if request.body:
132
137
  size = len(request.body)
133
- limit = self.request_content_log_limit
138
+ limit = self.log_request_body_limit
134
139
  if limit and size > limit:
135
140
  content = f"{request.body!r:.{limit}}\n ... {size - limit} more bytes"
136
141
  else:
@@ -142,12 +147,12 @@ class HTTPServiceContext[RTYPE = HTTPServiceResponse]:
142
147
  content = request_line = None
143
148
 
144
149
  if content is None:
145
- if SERVICE_REQUEST_SHOW_HEADERS:
150
+ if self.log_request_show_headers:
146
151
  self.logger.info("[%s] %s %s\n%s\n %s", self.short_id, request.method, request.url, *header)
147
152
  else:
148
153
  self.logger.info("[%s] %s %s +[%s]", self.short_id, request.method, request.url, *header)
149
154
  else:
150
- if SERVICE_REQUEST_SHOW_HEADERS:
155
+ if self.log_request_show_headers:
151
156
  self.logger.info(
152
157
  "[%s] %s %s\n%s\n %s\n%s\n %s\n%s",
153
158
  self.short_id,
@@ -176,14 +181,15 @@ class HTTPServiceContext[RTYPE = HTTPServiceResponse]:
176
181
  def handle_log_response(self, response: Response | AsyncResponse, content):
177
182
  size = len(content) if content else 0
178
183
  content_type = response.headers.get("content-type")
184
+ is_text = is_text_content_type(content_type or "text/")
179
185
  content_type = "no content-type" if content_type is None else repr(content_type)
180
186
  response_line = f" response body ({content_type} - {size} bytes) ".center(LINE_LENGTH, "-")
181
- limit = self.response_content_log_limit
187
+ limit = self.log_response_text_limit if is_text else self.log_response_binary_limit
182
188
  if limit and size > limit:
183
189
  content = f"{content!r:.{limit}}\n ... {size - limit} more bytes"
184
190
  else:
185
191
  content = f"{content!r}"
186
- if SERVICE_RESPONSE_SHOW_HEADERS:
192
+ if self.log_response_show_headers:
187
193
  headers_line = " response headers ".center(LINE_LENGTH, "-")
188
194
  headers = "\n ".join(f"{k}: {v!r}" for k, v in sorted(response.headers.items()))
189
195
  self.logger.info(
@@ -91,13 +91,13 @@ def replace_ns(value: str, replacement: tuple[str, str]) -> str:
91
91
  class XML:
92
92
  element: Element
93
93
 
94
- def __init__(self, element, namespaces: None | dict = None):
94
+ def __init__(self, element, namespaces: dict | None = None):
95
95
  assert isinstance(element, Element), "Expected an Element instance. Perhaps you wanted to use XML.fromstring?"
96
96
  self.element = element
97
97
  self.namespaces = namespaces or {}
98
98
 
99
99
  @classmethod
100
- def fromstring(cls, xml: str, namespaces: None | dict = None) -> Self:
100
+ def fromstring(cls, xml: str, namespaces: dict | None = None) -> Self:
101
101
  try:
102
102
  return cls(
103
103
  fromstring(xml, forbid_dtd=True, forbid_entities=True, forbid_external=True),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: csu
3
- Version: 2.4.2
3
+ Version: 2.5.0
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.4.2.svg
72
+ .. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-csu/v2.5.0.svg
73
73
  :alt: Commits since latest release
74
- :target: https://github.com/ionelmc/python-csu/compare/v2.4.2...main
74
+ :target: https://github.com/ionelmc/python-csu/compare/v2.5.0...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/
@@ -260,7 +260,7 @@ json.decoder.JSONDecodeError: Expecting value: line 2 column 5 (char 5)
260
260
  During handling of the above exception, another exception occurred:
261
261
 
262
262
  Traceback (most recent call last):
263
- File "[&SERVICE_PATH]", line 230, in handle_process_response
263
+ File "[&SERVICE_PATH]", line 236, in handle_process_response
264
264
  json = response.json(parse_float=Decimal)
265
265
  File "[&MODELS_PATH]", line 1529, in json
266
266
  raise RequestsJSONDecodeError(e.msg, e.doc, e.pos)
@@ -275,10 +275,10 @@ Traceback (most recent call last):
275
275
  File "[&TEST_PATH]", line 78, in json
276
276
  return ctx.request("GET", "status/418", accept_statuses=[418]).json
277
277
  ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
278
- File "[&SERVICE_PATH]", line 308, in request
278
+ File "[&SERVICE_PATH]", line 314, in request
279
279
  return self.handle_process_response(response, expect_json=expect_json)
280
280
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
281
- File "[&SERVICE_PATH]", line 232, in handle_process_response
281
+ File "[&SERVICE_PATH]", line 238, in handle_process_response
282
282
  raise DecodeError(response, exc, event_id=self.event_id) from exc
283
283
  csu.exceptions.DecodeError: DecodeError(JSONDecodeError('Expecting value: line 2 column 5 (char 5)'), response=<Response Dummy [418]>)
284
284
  (418, b'\\n -=[ teapot ]=-\\n\\n _...._\\n .\\' _ _ `.\\n | ."` ^ `". _,\\n \\\\_;`"---"`|//\\n | ;/\\n \\\\_ _/\\n `"""`\\n')
@@ -340,7 +340,7 @@ Traceback (most recent call last):
340
340
  File "[&TEST_PATH]", line 47, in error
341
341
  return ctx.request("GET", "status/599", expect_json=False)
342
342
  ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
343
- File "[&SERVICE_PATH]", line 307, in request
343
+ File "[&SERVICE_PATH]", line 313, in request
344
344
  raise UnexpectedStatusError(response, accept_statuses, event_id=self.event_id)
345
345
  csu.exceptions.UnexpectedStatusError: UnexpectedStatusError(<Response Dummy [599]>, accept_statuses=[200])
346
346
  (599, b'')
@@ -540,7 +540,7 @@ INFO | [01:000000.00] POST [&HTTPBIN]/anything?foo=bar&accept_statuses=%7B999
540
540
  """\
541
541
  ERROR | [01:000000.00] POST [&HTTPBIN]/anything?foo=bar&accept_statuses=%7B999%7D&expect_json=true&follow_redirects=false&retries_left=3&retry_count=0&retry_statuses=%7B200%7D !> RetryableStatusError('200 not in [999] but in [200]', 200, b'{"args":{"accept_statuses":"{999}","expect_json":"true","follow_redirects":"false","foo":"bar","retries_left":"3","retry_count":"0","retry_statuses":"{200}"},"data":"config={\\'custom\\': \\'config\\'}","files":{},"form":{},"headers":{"Accept":"*/*","Accept-Encoding":"gzip, deflate, br","Connection":"keep-alive","Content-Length":"27","Host":"127.0.0.1:12345","User-Agent":"python-httpx/0.X.X"},"json":null,"method":"POST","origin":"127.0.0.1","url":"http://127.0.0.1:12345/anything?foo=bar&accept_statuses={999}&expect_json=true&follow_redirects=false&retries_left=3&retry_count=0&retry_statuses={200}"}\\n', event_id=241212:01:000000.00) (will retry 3 more times)
542
542
  Traceback (most recent call last):
543
- File "[&SERVICE_PATH]", line 305, in request
543
+ File "[&SERVICE_PATH]", line 311, in request
544
544
  raise RetryableStatusError(response, accept_statuses, retry_statuses, event_id=self.event_id)
545
545
  csu.exceptions.RetryableStatusError: RetryableStatusError(<Response Dummy [200]>, accept_statuses=[999], retry_statuses=[200])
546
546
  (200, b'{"args":{"accept_statuses":"{999}","expect_json":"true","follow_redirects":"false","foo":"bar","retries_left":"3","retry_count":"0","retry_statuses":"{200}"},"data":"config={\\'custom\\': \\'config\\'}","files":{},"form":{},"headers":{"Accept":"*/*","Accept-Encoding":"gzip, deflate, br","Connection":"keep-alive","Content-Length":"27","Host":"127.0.0.1:12345","User-Agent":"python-httpx/0.X.X"},"json":null,"method":"POST","origin":"127.0.0.1","url":"http://127.0.0.1:12345/anything?foo=bar&accept_statuses={999}&expect_json=true&follow_redirects=false&retries_left=3&retry_count=0&retry_statuses={200}"}\\n')
@@ -574,7 +574,7 @@ INFO | [01:000000.00] POST [&HTTPBIN]/anything?foo=bar&accept_statuses=%7B999
574
574
  """\
575
575
  ERROR | [01:000000.00] POST [&HTTPBIN]/anything?foo=bar&accept_statuses=%7B999%7D&expect_json=true&follow_redirects=false&retries_left=2&retry_count=1&retry_statuses=%7B200%7D !> RetryableStatusError('200 not in [999] but in [200]', 200, b'{"args":{"accept_statuses":"{999}","expect_json":"true","follow_redirects":"false","foo":"bar","retries_left":"2","retry_count":"1","retry_statuses":"{200}"},"data":"config={\\'custom\\': \\'config\\'}","files":{},"form":{},"headers":{"Accept":"*/*","Accept-Encoding":"gzip, deflate, br","Connection":"keep-alive","Content-Length":"27","Host":"127.0.0.1:12345","User-Agent":"python-httpx/0.X.X"},"json":null,"method":"POST","origin":"127.0.0.1","url":"http://127.0.0.1:12345/anything?foo=bar&accept_statuses={999}&expect_json=true&follow_redirects=false&retries_left=2&retry_count=1&retry_statuses={200}"}\\n', event_id=241212:01:000000.00) (will retry 2 more times)
576
576
  Traceback (most recent call last):
577
- File "[&SERVICE_PATH]", line 305, in request
577
+ File "[&SERVICE_PATH]", line 311, in request
578
578
  raise RetryableStatusError(response, accept_statuses, retry_statuses, event_id=self.event_id)
579
579
  csu.exceptions.RetryableStatusError: RetryableStatusError(<Response Dummy [200]>, accept_statuses=[999], retry_statuses=[200])
580
580
  (200, b'{"args":{"accept_statuses":"{999}","expect_json":"true","follow_redirects":"false","foo":"bar","retries_left":"2","retry_count":"1","retry_statuses":"{200}"},"data":"config={\\'custom\\': \\'config\\'}","files":{},"form":{},"headers":{"Accept":"*/*","Accept-Encoding":"gzip, deflate, br","Connection":"keep-alive","Content-Length":"27","Host":"127.0.0.1:12345","User-Agent":"python-httpx/0.X.X"},"json":null,"method":"POST","origin":"127.0.0.1","url":"http://127.0.0.1:12345/anything?foo=bar&accept_statuses={999}&expect_json=true&follow_redirects=false&retries_left=2&retry_count=1&retry_statuses={200}"}\\n')
@@ -608,7 +608,7 @@ INFO | [01:000000.00] POST [&HTTPBIN]/anything?foo=bar&accept_statuses=%7B999
608
608
  """\
609
609
  ERROR | [01:000000.00] POST [&HTTPBIN]/anything?foo=bar&accept_statuses=%7B999%7D&expect_json=true&follow_redirects=false&retries_left=1&retry_count=2&retry_statuses=%7B200%7D !> RetryableStatusError('200 not in [999] but in [200]', 200, b'{"args":{"accept_statuses":"{999}","expect_json":"true","follow_redirects":"false","foo":"bar","retries_left":"1","retry_count":"2","retry_statuses":"{200}"},"data":"config={\\'custom\\': \\'config\\'}","files":{},"form":{},"headers":{"Accept":"*/*","Accept-Encoding":"gzip, deflate, br","Connection":"keep-alive","Content-Length":"27","Host":"127.0.0.1:12345","User-Agent":"python-httpx/0.X.X"},"json":null,"method":"POST","origin":"127.0.0.1","url":"http://127.0.0.1:12345/anything?foo=bar&accept_statuses={999}&expect_json=true&follow_redirects=false&retries_left=1&retry_count=2&retry_statuses={200}"}\\n', event_id=241212:01:000000.00) (will retry 1 more times)
610
610
  Traceback (most recent call last):
611
- File "[&SERVICE_PATH]", line 305, in request
611
+ File "[&SERVICE_PATH]", line 311, in request
612
612
  raise RetryableStatusError(response, accept_statuses, retry_statuses, event_id=self.event_id)
613
613
  csu.exceptions.RetryableStatusError: RetryableStatusError(<Response Dummy [200]>, accept_statuses=[999], retry_statuses=[200])
614
614
  (200, b'{"args":{"accept_statuses":"{999}","expect_json":"true","follow_redirects":"false","foo":"bar","retries_left":"1","retry_count":"2","retry_statuses":"{200}"},"data":"config={\\'custom\\': \\'config\\'}","files":{},"form":{},"headers":{"Accept":"*/*","Accept-Encoding":"gzip, deflate, br","Connection":"keep-alive","Content-Length":"27","Host":"127.0.0.1:12345","User-Agent":"python-httpx/0.X.X"},"json":null,"method":"POST","origin":"127.0.0.1","url":"http://127.0.0.1:12345/anything?foo=bar&accept_statuses={999}&expect_json=true&follow_redirects=false&retries_left=1&retry_count=2&retry_statuses={200}"}\\n')
@@ -656,31 +656,31 @@ ERROR | (renderer: NoneType)
656
656
  | ...<6 lines>...
657
657
  | accept_statuses=[999],
658
658
  | )
659
- | File "[&SERVICE_PATH]", line 319, in request
659
+ | File "[&SERVICE_PATH]", line 325, in request
660
660
  | raise ExhaustedRetriesError(retry_budget, retry_failures, event_id=self.event_id)
661
661
  | csu.exceptions.ExhaustedRetriesError: ExhaustedRetriesError(3)
662
662
  | ([RetryableStatusError('200 not in [999] but in [200]', 200, b'{"args":{"accept_statuses":"{999}","expect_json":"true","follow_redirects":"false","foo":"bar","retries_left":"3","retry_count":"0","retry_statuses":"{200}"},"data":"config={\\'custom\\': \\'config\\'}","files":{},"form":{},"headers":{"Accept":"*/*","Accept-Encoding":"gzip, deflate, br","Connection":"keep-alive","Content-Length":"27","Host":"127.0.0.1:12345","User-Agent":"python-httpx/0.X.X"},"json":null,"method":"POST","origin":"127.0.0.1","url":"http://127.0.0.1:12345/anything?foo=bar&accept_statuses={999}&expect_json=true&follow_redirects=false&retries_left=3&retry_count=0&retry_statuses={200}"}\\n', event_id=241212:01:000000.00), RetryableStatusError('200 not in [999] but in [200]', 200, b'{"args":{"accept_statuses":"{999}","expect_json":"true","follow_redirects":"false","foo":"bar","retries_left":"2","retry_count":"1","retry_statuses":"{200}"},"data":"config={\\'custom\\': \\'config\\'}","files":{},"form":{},"headers":{"Accept":"*/*","Accept-Encoding":"gzip, deflate, br","Connection":"keep-alive","Content-Length":"27","Host":"127.0.0.1:12345","User-Agent":"python-httpx/0.X.X"},"json":null,"method":"POST","origin":"127.0.0.1","url":"http://127.0.0.1:12345/anything?foo=bar&accept_statuses={999}&expect_json=true&follow_redirects=false&retries_left=2&retry_count=1&retry_statuses={200}"}\\n', event_id=241212:01:000000.00), RetryableStatusError('200 not in [999] but in [200]', 200, b'{"args":{"accept_statuses":"{999}","expect_json":"true","follow_redirects":"false","foo":"bar","retries_left":"1","retry_count":"2","retry_statuses":"{200}"},"data":"config={\\'custom\\': \\'config\\'}","files":{},"form":{},"headers":{"Accept":"*/*","Accept-Encoding":"gzip, deflate, br","Connection":"keep-alive","Content-Length":"27","Host":"127.0.0.1:12345","User-Agent":"python-httpx/0.X.X"},"json":null,"method":"POST","origin":"127.0.0.1","url":"http://127.0.0.1:12345/anything?foo=bar&accept_statuses={999}&expect_json=true&follow_redirects=false&retries_left=1&retry_count=2&retry_statuses={200}"}\\n', event_id=241212:01:000000.00), RetryableStatusError('200 not in [999] but in [200]', 200, b'{"args":{"accept_statuses":"{999}","expect_json":"true","follow_redirects":"false","foo":"bar","retries_left":"0","retry_count":"3","retry_statuses":"{200}"},"data":"config={\\'custom\\': \\'config\\'}","files":{},"form":{},"headers":{"Accept":"*/*","Accept-Encoding":"gzip, deflate, br","Connection":"keep-alive","Content-Length":"27","Host":"127.0.0.1:12345","User-Agent":"python-httpx/0.X.X"},"json":null,"method":"POST","origin":"127.0.0.1","url":"http://127.0.0.1:12345/anything?foo=bar&accept_statuses={999}&expect_json=true&follow_redirects=false&retries_left=0&retry_count=3&retry_statuses={200}"}\\n', event_id=241212:01:000000.00)],)
663
663
  +-+---------------- 1 ----------------
664
664
  | Traceback (most recent call last):
665
- | File "[&SERVICE_PATH]", line 305, in request
665
+ | File "[&SERVICE_PATH]", line 311, in request
666
666
  | raise RetryableStatusError(response, accept_statuses, retry_statuses, event_id=self.event_id)
667
667
  | csu.exceptions.RetryableStatusError: RetryableStatusError(<Response Dummy [200]>, accept_statuses=[999], retry_statuses=[200])
668
668
  | (200, b'{"args":{"accept_statuses":"{999}","expect_json":"true","follow_redirects":"false","foo":"bar","retries_left":"3","retry_count":"0","retry_statuses":"{200}"},"data":"config={\\'custom\\': \\'config\\'}","files":{},"form":{},"headers":{"Accept":"*/*","Accept-Encoding":"gzip, deflate, br","Connection":"keep-alive","Content-Length":"27","Host":"127.0.0.1:12345","User-Agent":"python-httpx/0.X.X"},"json":null,"method":"POST","origin":"127.0.0.1","url":"http://127.0.0.1:12345/anything?foo=bar&accept_statuses={999}&expect_json=true&follow_redirects=false&retries_left=3&retry_count=0&retry_statuses={200}"}\\n')
669
669
  +---------------- 2 ----------------
670
670
  | Traceback (most recent call last):
671
- | File "[&SERVICE_PATH]", line 305, in request
671
+ | File "[&SERVICE_PATH]", line 311, in request
672
672
  | raise RetryableStatusError(response, accept_statuses, retry_statuses, event_id=self.event_id)
673
673
  | csu.exceptions.RetryableStatusError: RetryableStatusError(<Response Dummy [200]>, accept_statuses=[999], retry_statuses=[200])
674
674
  | (200, b'{"args":{"accept_statuses":"{999}","expect_json":"true","follow_redirects":"false","foo":"bar","retries_left":"2","retry_count":"1","retry_statuses":"{200}"},"data":"config={\\'custom\\': \\'config\\'}","files":{},"form":{},"headers":{"Accept":"*/*","Accept-Encoding":"gzip, deflate, br","Connection":"keep-alive","Content-Length":"27","Host":"127.0.0.1:12345","User-Agent":"python-httpx/0.X.X"},"json":null,"method":"POST","origin":"127.0.0.1","url":"http://127.0.0.1:12345/anything?foo=bar&accept_statuses={999}&expect_json=true&follow_redirects=false&retries_left=2&retry_count=1&retry_statuses={200}"}\\n')
675
675
  +---------------- 3 ----------------
676
676
  | Traceback (most recent call last):
677
- | File "[&SERVICE_PATH]", line 305, in request
677
+ | File "[&SERVICE_PATH]", line 311, in request
678
678
  | raise RetryableStatusError(response, accept_statuses, retry_statuses, event_id=self.event_id)
679
679
  | csu.exceptions.RetryableStatusError: RetryableStatusError(<Response Dummy [200]>, accept_statuses=[999], retry_statuses=[200])
680
680
  | (200, b'{"args":{"accept_statuses":"{999}","expect_json":"true","follow_redirects":"false","foo":"bar","retries_left":"1","retry_count":"2","retry_statuses":"{200}"},"data":"config={\\'custom\\': \\'config\\'}","files":{},"form":{},"headers":{"Accept":"*/*","Accept-Encoding":"gzip, deflate, br","Connection":"keep-alive","Content-Length":"27","Host":"127.0.0.1:12345","User-Agent":"python-httpx/0.X.X"},"json":null,"method":"POST","origin":"127.0.0.1","url":"http://127.0.0.1:12345/anything?foo=bar&accept_statuses={999}&expect_json=true&follow_redirects=false&retries_left=1&retry_count=2&retry_statuses={200}"}\\n')
681
681
  +---------------- 4 ----------------
682
682
  | Traceback (most recent call last):
683
- | File "[&SERVICE_PATH]", line 305, in request
683
+ | File "[&SERVICE_PATH]", line 311, in request
684
684
  | raise RetryableStatusError(response, accept_statuses, retry_statuses, event_id=self.event_id)
685
685
  | csu.exceptions.RetryableStatusError: RetryableStatusError(<Response Dummy [200]>, accept_statuses=[999], retry_statuses=[200])
686
686
  | (200, b'{"args":{"accept_statuses":"{999}","expect_json":"true","follow_redirects":"false","foo":"bar","retries_left":"0","retry_count":"3","retry_statuses":"{200}"},"data":"config={\\'custom\\': \\'config\\'}","files":{},"form":{},"headers":{"Accept":"*/*","Accept-Encoding":"gzip, deflate, br","Connection":"keep-alive","Content-Length":"27","Host":"127.0.0.1:12345","User-Agent":"python-httpx/0.X.X"},"json":null,"method":"POST","origin":"127.0.0.1","url":"http://127.0.0.1:12345/anything?foo=bar&accept_statuses={999}&expect_json=true&follow_redirects=false&retries_left=0&retry_count=3&retry_statuses={200}"}\\n')
@@ -1,5 +1,6 @@
1
- CSU_SERVICE_RESPONSE_SHOW_HEADERS = True
2
- CSU_SERVICE_REQUEST_SHOW_HEADERS = True
1
+ CSU_LOGGING_SHOW_HEADERS = True
2
+ CSU_SERVICE_LOGGING_REQUEST_SHOW_HEADERS = True
3
+ CSU_SERVICE_LOGGING_RESPONSE_SHOW_HEADERS = True
3
4
  ALLOWED_HOSTS = ["localhost", "127.0.0.1"]
4
5
  DATABASES = {
5
6
  "default": {
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