django-esi 8.0.0a2__py3-none-any.whl → 8.0.0a3__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.
Potentially problematic release.
This version of django-esi might be problematic. Click here for more details.
- {django_esi-8.0.0a2.dist-info → django_esi-8.0.0a3.dist-info}/METADATA +1 -1
- {django_esi-8.0.0a2.dist-info → django_esi-8.0.0a3.dist-info}/RECORD +12 -11
- esi/__init__.py +2 -2
- esi/aiopenapi3/plugins.py +21 -0
- esi/exceptions.py +29 -0
- esi/locale/en/LC_MESSAGES/django.po +3 -3
- esi/locale/ru/LC_MESSAGES/django.po +8 -7
- esi/openapi_clients.py +89 -29
- esi/stubs.pyi +42 -0
- esi/tests/test_openapi.py +14 -0
- {django_esi-8.0.0a2.dist-info → django_esi-8.0.0a3.dist-info}/WHEEL +0 -0
- {django_esi-8.0.0a2.dist-info → django_esi-8.0.0a3.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
esi/__init__.py,sha256=
|
|
1
|
+
esi/__init__.py,sha256=h7UNGlHMW_Mr3hZ1uc6-nP6J4YJU0GnnNvq1WRgx1Eo,199
|
|
2
2
|
esi/admin.py,sha256=9i68WwW_gR0zsGhJKTWK2yMEi44EkhpAaOW0LDpVfM8,1176
|
|
3
3
|
esi/app_settings.py,sha256=2Jp_1myzKCL8A20RYvwQnQd04Ds_CBGsCqwDtsrYf0M,4289
|
|
4
4
|
esi/apps.py,sha256=HIu1niTkOXYmCzMVAjYcaFhHrrXeBbHvui6I44OCHXw,280
|
|
@@ -6,23 +6,23 @@ esi/checks.py,sha256=31puQdsrpRUJB-kedp2k7Evo0x2knTWQPZsCUUrJ3dY,1912
|
|
|
6
6
|
esi/clients.py,sha256=WB5YseJnfZdTeJ9C1Eze-iWXEqIpo5i_8uiQ6w0VkCw,23503
|
|
7
7
|
esi/decorators.py,sha256=2RmPdkrIAwbxOV5Ls8-RIWd_VhmXEDzIG6g5-bs6WZc,9093
|
|
8
8
|
esi/errors.py,sha256=KfFtgX8Mys4uMoQtco0n_pQeaM83yVrRSyW6StXuUjo,308
|
|
9
|
-
esi/exceptions.py,sha256=
|
|
9
|
+
esi/exceptions.py,sha256=nR-Z0BXVt5xSkgUL56E97pqtTyEu3MFr8_P0uCCCQ-E,1448
|
|
10
10
|
esi/helpers.py,sha256=-lYojtcWYWtlC_olSItWR3O3zJpjMYTQpNvsax81us8,672
|
|
11
11
|
esi/managers.py,sha256=zdri1aSJrSX9W_kwzbXq44CAAhdufikoOViaJntA41A,11443
|
|
12
12
|
esi/managers.pyi,sha256=BxRNX2yOT3domdrxsuGp4G0TrP_bWmf8VVwvM03eHQE,1977
|
|
13
13
|
esi/models.py,sha256=v33dvnWoiDwhcwc5Y4CvNOtAJocNd2qh5JtyNTtu-P8,11483
|
|
14
|
-
esi/openapi_clients.py,sha256=
|
|
14
|
+
esi/openapi_clients.py,sha256=ORYRPjW4pqmmMFmaK6oiIHsCpVlLL-s7LTKcXA3xhSM,36874
|
|
15
15
|
esi/rate_limiting.py,sha256=fUzso1YCHlBes8SVrEAsP58eRWcFNuO2XEkW6RQLhUE,2600
|
|
16
16
|
esi/stubs.py,sha256=UakysEAq5V454oKbxyEUewncr5sCFZCgwHFh0SJvU3Y,30
|
|
17
|
-
esi/stubs.pyi,sha256=
|
|
17
|
+
esi/stubs.pyi,sha256=rmWy-cM_qcZyEIAQcAbNJ2jMtgPU4Ai-SOncFmuBHSA,233649
|
|
18
18
|
esi/tasks.py,sha256=TaE_Q03oLk1ohkPw8KknXpIK-nY47C-oaGUUu4nr8pw,2658
|
|
19
19
|
esi/urls.py,sha256=B4EnmT-MZzs4F8a9ORK1ejAHbpGRN_V4TKks8ww3JGE,151
|
|
20
20
|
esi/views.py,sha256=cq2JkqSAwx-Nqi3amRUA9CKLu3ZLQYHSAH8c8g7xIQ4,3917
|
|
21
|
-
esi/aiopenapi3/plugins.py,sha256=
|
|
21
|
+
esi/aiopenapi3/plugins.py,sha256=1JGDhsca2vO_R_J5Gi4b5_lK0VDQ_AoyMHHyvzYoLrM,4673
|
|
22
22
|
esi/locale/de/LC_MESSAGES/django.mo,sha256=9_Fc_R8oZpT_CojH8gBxdSqj2K84EMEMn4wkvmOr7Dg,820
|
|
23
23
|
esi/locale/de/LC_MESSAGES/django.po,sha256=2NQVGYzLroHPPrWgmACSnb2d3nwlkMBLxA1uWU3K0FA,1503
|
|
24
24
|
esi/locale/en/LC_MESSAGES/django.mo,sha256=N1pb17IfLd0ASiKO8d68-B4ygSpDkhKOCs8YTzMXQo0,380
|
|
25
|
-
esi/locale/en/LC_MESSAGES/django.po,sha256
|
|
25
|
+
esi/locale/en/LC_MESSAGES/django.po,sha256=Ex11J8RaZM4KuP8xGtRi-TrSSAG05jU-KiemYeG84yc,1305
|
|
26
26
|
esi/locale/es/LC_MESSAGES/django.mo,sha256=dtCivRk6vD0mT2Wx9kcBRtMhU41bdqERHkBYPLoI5qg,737
|
|
27
27
|
esi/locale/es/LC_MESSAGES/django.po,sha256=KvCk4FLdPl_SMEcEWL6KAr870ScmsL5jnyCJvpglOB4,1504
|
|
28
28
|
esi/locale/fr_FR/LC_MESSAGES/django.mo,sha256=CG7EL6JGYoSVaAggmIvwerXuzf55b6VWmcOSK_1okik,643
|
|
@@ -34,7 +34,7 @@ esi/locale/ja/LC_MESSAGES/django.po,sha256=uWDpo_f8YyTn6VIH1S87PbGvQnsCiKe_i9LYm
|
|
|
34
34
|
esi/locale/ko_KR/LC_MESSAGES/django.mo,sha256=-GXsBq-bhlghnrxsuNfS2rirCJn9sPQi1whTvRCvGV4,746
|
|
35
35
|
esi/locale/ko_KR/LC_MESSAGES/django.po,sha256=gjPHlrraWxVBS-H4n88y-5a2n7PbwvwZzeaZdP5aquc,1511
|
|
36
36
|
esi/locale/ru/LC_MESSAGES/django.mo,sha256=WbKkLTav0kXvfTadQ6fccwh0T7iI_1wmPVu_LjQaYMo,915
|
|
37
|
-
esi/locale/ru/LC_MESSAGES/django.po,sha256=
|
|
37
|
+
esi/locale/ru/LC_MESSAGES/django.po,sha256=zQm-QOcIIu3vfU60bG2zFXv1sstsEV39EcN0VJErHrs,1812
|
|
38
38
|
esi/locale/zh_Hans/LC_MESSAGES/django.mo,sha256=hPSO7wlU9cN8f32Qo1Dztq6AHUm2hZfL1hHIDW1R1F4,681
|
|
39
39
|
esi/locale/zh_Hans/LC_MESSAGES/django.po,sha256=fZhif8jOUvY22xurl7eBYsHNKoqf7obtkW5L3894hk4,1451
|
|
40
40
|
esi/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -73,13 +73,14 @@ esi/tests/test_decorators.py,sha256=I5MhcLKhN5xD4PxgEQqOWQ2kiXquDqQIEve9dbTOsho,
|
|
|
73
73
|
esi/tests/test_management_command.py,sha256=mtxfBtG6CHP1bTy1tJEO-djX4SQwMT3T_mqGnUIM6qs,9863
|
|
74
74
|
esi/tests/test_managers.py,sha256=CEpjXSXyVY6xxgrs0f9FnEh-KY9JPaKjKYiutXLLShE,24309
|
|
75
75
|
esi/tests/test_models.py,sha256=lDj5IcYgXHeOFTHXTsWGg13CSooFaxD0c51kJn_ytAc,13748
|
|
76
|
+
esi/tests/test_openapi.py,sha256=UJB6g8Olpb71OenLmuoQEok4RNWo2F3-37wL-HtV-RI,459
|
|
76
77
|
esi/tests/test_swagger.json,sha256=HOrPgbvwm5N521QNcE3baWcZJkSjmuN_VWrR06wEQoo,17241
|
|
77
78
|
esi/tests/test_swagger_full.json,sha256=JCEAZNMFhkdZhquTx4lDhrqGgCrzzzlYU64HdbEv8E4,2548369
|
|
78
79
|
esi/tests/test_tasks.py,sha256=nIvfXax_8nQCxNQeT-4TJ84i0__t1qWe155z5rnsktQ,4399
|
|
79
80
|
esi/tests/test_templatetags.py,sha256=b68JWE3HvOlr2aUisJHsTsDS4e7IMjDeqTuzMqC7Re4,517
|
|
80
81
|
esi/tests/test_views.py,sha256=Kj_f2yIpmPG0kx-lAX_sfkaHlIpgbkm02ieA1V3o-k4,13073
|
|
81
82
|
esi/tests/threading_pilot.py,sha256=ax_dEdnTNibA-UQHqbZle_2dh_3jcHKRyrYSOKuE_6U,1931
|
|
82
|
-
django_esi-8.0.
|
|
83
|
-
django_esi-8.0.
|
|
84
|
-
django_esi-8.0.
|
|
85
|
-
django_esi-8.0.
|
|
83
|
+
django_esi-8.0.0a3.dist-info/licenses/LICENSE,sha256=WJ7YI-moTFb-uVrFjnzzhGJrnL9P2iqQe8NuED3hutI,35141
|
|
84
|
+
django_esi-8.0.0a3.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
|
85
|
+
django_esi-8.0.0a3.dist-info/METADATA,sha256=Q_uxmC2AihrvgYtxiVATgX0MFhM3bmrAk81axnHUyP4,4738
|
|
86
|
+
django_esi-8.0.0a3.dist-info/RECORD,,
|
esi/__init__.py
CHANGED
esi/aiopenapi3/plugins.py
CHANGED
|
@@ -19,6 +19,27 @@ class Trim204ContentType(Document):
|
|
|
19
19
|
return ctx
|
|
20
20
|
|
|
21
21
|
|
|
22
|
+
class Add304ContentType(Document):
|
|
23
|
+
"""
|
|
24
|
+
Adds 304 content-type to responses
|
|
25
|
+
A 304 never has content. ESI defualt has application/json
|
|
26
|
+
This is a hack for now
|
|
27
|
+
"""
|
|
28
|
+
def parsed(self, ctx: Document.Context) -> Document.Context:
|
|
29
|
+
spec = ctx.document
|
|
30
|
+
# Patch all paths
|
|
31
|
+
for path_item in spec.get("paths", {}).values():
|
|
32
|
+
for method_name in ("get", "post", "put", "delete", "patch", "options", "head"):
|
|
33
|
+
method = path_item.get(method_name)
|
|
34
|
+
if not method:
|
|
35
|
+
continue
|
|
36
|
+
if "304" not in method['responses']:
|
|
37
|
+
method['responses']["304"]={
|
|
38
|
+
"description": "Not Modified"
|
|
39
|
+
}
|
|
40
|
+
return ctx
|
|
41
|
+
|
|
42
|
+
|
|
22
43
|
class RemoveSecurityParameter(Document):
|
|
23
44
|
"""
|
|
24
45
|
Removes the whole OAuth2 securityScheme
|
esi/exceptions.py
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
|
|
3
|
+
from aiopenapi3.errors import HTTPServerError as base_HTTPServerError
|
|
4
|
+
from aiopenapi3.errors import HTTPClientError as base_HTTPClientError
|
|
5
|
+
from aiopenapi3.errors import HTTPError
|
|
6
|
+
|
|
1
7
|
class ESIErrorLimitException(Exception):
|
|
2
8
|
"""ESI Global Error Limit Exceeded
|
|
3
9
|
https://developers.eveonline.com/docs/services/esi/best-practices/#error-limit
|
|
@@ -16,3 +22,26 @@ class ESIBucketLimitException(Exception):
|
|
|
16
22
|
self.bucket = bucket
|
|
17
23
|
msg = kwargs.get("message") or f"ESI bucket limit reached for {bucket}."
|
|
18
24
|
super().__init__(msg, *args)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclasses.dataclass(repr=False)
|
|
28
|
+
class HTTPNotModified(HTTPError):
|
|
29
|
+
"""The HTTP Status is 304"""
|
|
30
|
+
|
|
31
|
+
status_code: int
|
|
32
|
+
headers: dict[str, str]
|
|
33
|
+
|
|
34
|
+
def __str__(self):
|
|
35
|
+
return f"""<{self.__class__.__name__} {self.status_code} {self.headers}>"""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclasses.dataclass(repr=False)
|
|
39
|
+
class HTTPClientError(base_HTTPClientError):
|
|
40
|
+
"""response code 4xx"""
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclasses.dataclass(repr=False)
|
|
45
|
+
class HTTPServerError(base_HTTPServerError):
|
|
46
|
+
"""response code 5xx"""
|
|
47
|
+
pass
|
|
@@ -8,7 +8,7 @@ msgid ""
|
|
|
8
8
|
msgstr ""
|
|
9
9
|
"Project-Id-Version: PACKAGE VERSION\n"
|
|
10
10
|
"Report-Msgid-Bugs-To: \n"
|
|
11
|
-
"POT-Creation-Date: 2025-
|
|
11
|
+
"POT-Creation-Date: 2025-09-04 13:41+1000\n"
|
|
12
12
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
|
13
13
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
|
14
14
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
|
@@ -18,11 +18,11 @@ msgstr ""
|
|
|
18
18
|
"Content-Transfer-Encoding: 8bit\n"
|
|
19
19
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
|
20
20
|
|
|
21
|
-
#: esi/models.py:
|
|
21
|
+
#: esi/models.py:66
|
|
22
22
|
msgid "Character"
|
|
23
23
|
msgstr ""
|
|
24
24
|
|
|
25
|
-
#: esi/models.py:
|
|
25
|
+
#: esi/models.py:67
|
|
26
26
|
msgid "Corporation"
|
|
27
27
|
msgstr ""
|
|
28
28
|
|
|
@@ -5,15 +5,16 @@
|
|
|
5
5
|
#
|
|
6
6
|
# Translators:
|
|
7
7
|
# Filipp Chertiev <f@fzfx.ru>, 2023
|
|
8
|
+
# Gnevich <and.vareba81@gmail.com>, 2025
|
|
8
9
|
#
|
|
9
10
|
#, fuzzy
|
|
10
11
|
msgid ""
|
|
11
12
|
msgstr ""
|
|
12
13
|
"Project-Id-Version: PACKAGE VERSION\n"
|
|
13
14
|
"Report-Msgid-Bugs-To: \n"
|
|
14
|
-
"POT-Creation-Date:
|
|
15
|
+
"POT-Creation-Date: 2025-06-30 10:57+1000\n"
|
|
15
16
|
"PO-Revision-Date: 2023-10-25 11:04+0000\n"
|
|
16
|
-
"Last-Translator:
|
|
17
|
+
"Last-Translator: Gnevich <and.vareba81@gmail.com>, 2025\n"
|
|
17
18
|
"Language-Team: Russian (https://app.transifex.com/alliance-auth/teams/107430/ru/)\n"
|
|
18
19
|
"MIME-Version: 1.0\n"
|
|
19
20
|
"Content-Type: text/plain; charset=UTF-8\n"
|
|
@@ -21,13 +22,13 @@ msgstr ""
|
|
|
21
22
|
"Language: ru\n"
|
|
22
23
|
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n"
|
|
23
24
|
|
|
24
|
-
#: esi/models.py:
|
|
25
|
+
#: esi/models.py:73
|
|
25
26
|
msgid "Character"
|
|
26
|
-
msgstr ""
|
|
27
|
+
msgstr "Персонаж"
|
|
27
28
|
|
|
28
|
-
#: esi/models.py:
|
|
29
|
+
#: esi/models.py:74
|
|
29
30
|
msgid "Corporation"
|
|
30
|
-
msgstr ""
|
|
31
|
+
msgstr "Корпорация"
|
|
31
32
|
|
|
32
33
|
#: esi/templates/esi/select_token.html:13
|
|
33
34
|
msgid "ESI Token Selection"
|
|
@@ -49,7 +50,7 @@ msgstr "Новый персонаж"
|
|
|
49
50
|
#: esi/templates/esi/select_token.html:71
|
|
50
51
|
#: esi/templates/esi/select_token.html:107
|
|
51
52
|
msgid "Add Token"
|
|
52
|
-
msgstr ""
|
|
53
|
+
msgstr "Добавить токен"
|
|
53
54
|
|
|
54
55
|
#: esi/templates/esi/select_token.html:88
|
|
55
56
|
#: esi/templates/esi/select_token.html:89
|
esi/openapi_clients.py
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import warnings
|
|
3
|
-
from datetime import datetime
|
|
3
|
+
from datetime import datetime, date
|
|
4
4
|
from hashlib import md5
|
|
5
5
|
from typing import Any
|
|
6
6
|
|
|
7
7
|
from aiopenapi3 import OpenAPI
|
|
8
8
|
from aiopenapi3._types import ResponseDataType, ResponseHeadersType
|
|
9
|
+
from aiopenapi3.errors import HTTPServerError as base_HTTPServerError
|
|
10
|
+
from aiopenapi3.errors import HTTPClientError as base_HTTPClientError
|
|
9
11
|
from aiopenapi3.request import OperationIndex, RequestBase
|
|
10
12
|
from httpx import (
|
|
11
13
|
AsyncClient, Client, HTTPStatusError, RequestError, Response, Timeout,
|
|
@@ -18,7 +20,8 @@ from tenacity import (
|
|
|
18
20
|
from django.core.cache import cache
|
|
19
21
|
|
|
20
22
|
from esi import app_settings
|
|
21
|
-
from esi.
|
|
23
|
+
from esi.exceptions import HTTPClientError, HTTPServerError, HTTPNotModified
|
|
24
|
+
from esi.aiopenapi3.plugins import Add304ContentType, PatchCompatibilityDatePlugin, Trim204ContentType
|
|
22
25
|
from esi.exceptions import ESIErrorLimitException
|
|
23
26
|
from esi.models import Token
|
|
24
27
|
from esi.stubs import ESIClientStub
|
|
@@ -121,14 +124,14 @@ def _load_aiopenapi_client_sync(
|
|
|
121
124
|
path=spec_file,
|
|
122
125
|
session_factory=session_factory,
|
|
123
126
|
use_operation_tags=True,
|
|
124
|
-
plugins=[PatchCompatibilityDatePlugin(), Trim204ContentType()]
|
|
127
|
+
plugins=[PatchCompatibilityDatePlugin(), Trim204ContentType(), Add304ContentType()]
|
|
125
128
|
)
|
|
126
129
|
else:
|
|
127
130
|
return OpenAPI.load_sync(
|
|
128
131
|
url=spec_url,
|
|
129
132
|
session_factory=session_factory,
|
|
130
133
|
use_operation_tags=True,
|
|
131
|
-
plugins=[PatchCompatibilityDatePlugin(), Trim204ContentType()]
|
|
134
|
+
plugins=[PatchCompatibilityDatePlugin(), Trim204ContentType(), Add304ContentType()]
|
|
132
135
|
)
|
|
133
136
|
|
|
134
137
|
|
|
@@ -176,14 +179,14 @@ async def _load_aiopenapi_client_async(
|
|
|
176
179
|
path=spec_file,
|
|
177
180
|
session_factory=session_factory,
|
|
178
181
|
use_operation_tags=True,
|
|
179
|
-
plugins=[PatchCompatibilityDatePlugin(), Trim204ContentType()]
|
|
182
|
+
plugins=[PatchCompatibilityDatePlugin(), Trim204ContentType(), Add304ContentType()]
|
|
180
183
|
)
|
|
181
184
|
else:
|
|
182
185
|
return await OpenAPI.load_async(
|
|
183
186
|
url=spec_url,
|
|
184
187
|
session_factory=session_factory,
|
|
185
188
|
use_operation_tags=True,
|
|
186
|
-
plugins=[PatchCompatibilityDatePlugin(), Trim204ContentType()]
|
|
189
|
+
plugins=[PatchCompatibilityDatePlugin(), Trim204ContentType(), Add304ContentType()]
|
|
187
190
|
)
|
|
188
191
|
|
|
189
192
|
|
|
@@ -322,7 +325,12 @@ class BaseEsiOperation():
|
|
|
322
325
|
Returns:
|
|
323
326
|
str: Key name
|
|
324
327
|
"""
|
|
325
|
-
|
|
328
|
+
# ignore the token this will break the cache
|
|
329
|
+
ignore_keys = [
|
|
330
|
+
"token",
|
|
331
|
+
]
|
|
332
|
+
_kwargs = { key:value for key, value in self._kwargs.items() if key not in ignore_keys }
|
|
333
|
+
data = (self.method + self.url + str(self._args) + str(_kwargs)).encode('utf-8')
|
|
326
334
|
str_hash = md5(data).hexdigest() # nosec B303
|
|
327
335
|
return f'esi_{str_hash}'
|
|
328
336
|
|
|
@@ -360,7 +368,7 @@ class BaseEsiOperation():
|
|
|
360
368
|
"""
|
|
361
369
|
return any(p.name == "before" or p.name == "after" for p in self.operation.parameters)
|
|
362
370
|
|
|
363
|
-
def _get_cache(self, cache_key: str) -> tuple[ResponseHeadersType | None, Any, Response | None]:
|
|
371
|
+
def _get_cache(self, cache_key: str, etag: str) -> tuple[ResponseHeadersType | None, Any, Response | None]:
|
|
364
372
|
"""Retrieve cached response and validate expiry
|
|
365
373
|
Args:
|
|
366
374
|
cache_key (str): The cache key to retrieve
|
|
@@ -376,12 +384,23 @@ class BaseEsiOperation():
|
|
|
376
384
|
|
|
377
385
|
if cached_response:
|
|
378
386
|
logger.debug(f"Cache Hit {self.url}")
|
|
379
|
-
|
|
387
|
+
expiry = _time_to_expiry(str(cached_response.headers.get('Expires')))
|
|
380
388
|
|
|
381
|
-
|
|
389
|
+
# force check to ensure cache isn't expired
|
|
382
390
|
if expiry < 0:
|
|
383
391
|
logger.warning("Cache expired by %d seconds, forcing expiry", expiry)
|
|
384
392
|
return None, None, None
|
|
393
|
+
|
|
394
|
+
# check if etag is same before building models from cache
|
|
395
|
+
if etag:
|
|
396
|
+
if cached_response.headers.get('Expires') == etag:
|
|
397
|
+
raise HTTPNotModified(
|
|
398
|
+
status_code=304,
|
|
399
|
+
headers=cached_response.headers
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
# build models
|
|
403
|
+
headers, data = self.parse_cached_request(cached_response)
|
|
385
404
|
return headers, data, cached_response
|
|
386
405
|
|
|
387
406
|
return None, None, None
|
|
@@ -485,7 +504,7 @@ class EsiOperation(BaseEsiOperation):
|
|
|
485
504
|
if not etag and app_settings.ESI_CACHE_RESPONSE:
|
|
486
505
|
etag = cache.get(etag_key)
|
|
487
506
|
|
|
488
|
-
headers, data, response = self._get_cache(cache_key)
|
|
507
|
+
headers, data, response = self._get_cache(cache_key, etag=etag) if use_cache else (None, None, None)
|
|
489
508
|
|
|
490
509
|
if response and use_cache:
|
|
491
510
|
expiry = _time_to_expiry(str(headers.get('Expires')))
|
|
@@ -499,17 +518,41 @@ class EsiOperation(BaseEsiOperation):
|
|
|
499
518
|
|
|
500
519
|
if not response:
|
|
501
520
|
logger.debug(f"Cache Miss {self.url}")
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
521
|
+
try:
|
|
522
|
+
headers, data, response = self._make_request(parameters, etag)
|
|
523
|
+
if response.status_code == 420:
|
|
524
|
+
reset = response.headers.get("X-RateLimit-Reset", None)
|
|
525
|
+
cache.set("esi_error_limit_reset", reset, timeout=reset)
|
|
526
|
+
raise ESIErrorLimitException(reset=reset)
|
|
527
|
+
self._store_cache(cache_key, response)
|
|
528
|
+
|
|
529
|
+
# if response.status_code == 304 and app_settings.ESI_CACHE_RESPONSE:
|
|
530
|
+
# cached = cache.get(cache_key)
|
|
531
|
+
# if cached:
|
|
532
|
+
# return (cached, response) if return_response else cached
|
|
533
|
+
# we dont want to do this, if we do this we have to store data longer than ttl. rip ram
|
|
534
|
+
|
|
535
|
+
# Shim our exceptions into Django-ESI
|
|
536
|
+
except base_HTTPServerError as e:
|
|
537
|
+
raise HTTPServerError(
|
|
538
|
+
status_code=e.status_code,
|
|
539
|
+
headers=e.headers,
|
|
540
|
+
data=e.data
|
|
541
|
+
)
|
|
542
|
+
|
|
543
|
+
except base_HTTPClientError as e:
|
|
544
|
+
raise HTTPClientError(
|
|
545
|
+
status_code=e.status_code,
|
|
546
|
+
headers=e.headers,
|
|
547
|
+
data=e.data
|
|
548
|
+
)
|
|
549
|
+
|
|
550
|
+
# Throw a 304 exception for catching.
|
|
551
|
+
if response.status_code == 304:
|
|
552
|
+
raise HTTPNotModified(
|
|
553
|
+
status_code=304,
|
|
554
|
+
headers=response.headers
|
|
555
|
+
)
|
|
513
556
|
|
|
514
557
|
return (data, response) if return_response else data
|
|
515
558
|
|
|
@@ -590,7 +633,7 @@ class EsiOperation(BaseEsiOperation):
|
|
|
590
633
|
my_languages.append(lang)
|
|
591
634
|
|
|
592
635
|
return {
|
|
593
|
-
language: self.results(
|
|
636
|
+
language: self.results(accept_language=language, **kwargs)
|
|
594
637
|
for language in my_languages
|
|
595
638
|
}
|
|
596
639
|
|
|
@@ -732,7 +775,7 @@ class EsiOperationAsync(BaseEsiOperation):
|
|
|
732
775
|
my_languages.append(lang)
|
|
733
776
|
|
|
734
777
|
return {
|
|
735
|
-
language: self.results(
|
|
778
|
+
language: self.results(accept_language=language, **kwargs)
|
|
736
779
|
for language in my_languages
|
|
737
780
|
}
|
|
738
781
|
|
|
@@ -804,7 +847,11 @@ class ESIClient(ESIClientStub):
|
|
|
804
847
|
if tag == "_":
|
|
805
848
|
return self.api._operationindex
|
|
806
849
|
|
|
807
|
-
|
|
850
|
+
# convert pythonic Planetary_Interaction to Planetary Interaction
|
|
851
|
+
if "_" in tag:
|
|
852
|
+
tag = tag.replace("_", " ")
|
|
853
|
+
|
|
854
|
+
if tag in set(self.api._operationindex._tags.keys()):
|
|
808
855
|
return ESITag(self.api._operationindex._tags[tag], self.api)
|
|
809
856
|
|
|
810
857
|
raise AttributeError(
|
|
@@ -827,7 +874,11 @@ class ESIClientAsync(ESIClientStub):
|
|
|
827
874
|
if tag == "_":
|
|
828
875
|
return self.api._operationindex
|
|
829
876
|
|
|
830
|
-
|
|
877
|
+
# convert pythonic Planetary_Interaction to Planetary Interaction
|
|
878
|
+
if "_" in tag:
|
|
879
|
+
tag = tag.replace("_", " ")
|
|
880
|
+
|
|
881
|
+
if tag in set(self.api._operationindex._tags.keys()):
|
|
831
882
|
return ESITagAsync(self.api._operationindex._tags[tag], self.api)
|
|
832
883
|
|
|
833
884
|
raise AttributeError(
|
|
@@ -839,7 +890,7 @@ class ESIClientAsync(ESIClientStub):
|
|
|
839
890
|
class ESIClientProvider:
|
|
840
891
|
"""Class for providing a single ESI client instance for a whole app
|
|
841
892
|
Args:
|
|
842
|
-
compatibility_date (str): The compatibility date for the ESI client.
|
|
893
|
+
compatibility_date (str | date): The compatibility date for the ESI client.
|
|
843
894
|
ua_appname (str): Name of the App for generating a User-Agent,
|
|
844
895
|
ua_version (str): Version of the App for generating a User-Agent,
|
|
845
896
|
ua_url (str, Optional): URL To the Source Code or Documentation for generating a User-Agent,
|
|
@@ -855,7 +906,7 @@ class ESIClientProvider:
|
|
|
855
906
|
|
|
856
907
|
def __init__(
|
|
857
908
|
self,
|
|
858
|
-
compatibility_date: str,
|
|
909
|
+
compatibility_date: str | date,
|
|
859
910
|
ua_appname: str,
|
|
860
911
|
ua_version: str,
|
|
861
912
|
ua_url: str | None = None,
|
|
@@ -863,7 +914,10 @@ class ESIClientProvider:
|
|
|
863
914
|
tenant: str = "tranquility",
|
|
864
915
|
**kwargs
|
|
865
916
|
) -> None:
|
|
866
|
-
|
|
917
|
+
if type(compatibility_date) is date:
|
|
918
|
+
self._compatibility_date = self._date_to_string(compatibility_date)
|
|
919
|
+
else:
|
|
920
|
+
self._compatibility_date = compatibility_date
|
|
867
921
|
self._ua_appname = ua_appname
|
|
868
922
|
self._ua_version = ua_version
|
|
869
923
|
self._ua_url = ua_url
|
|
@@ -899,5 +953,11 @@ class ESIClientProvider:
|
|
|
899
953
|
self._client_async = ESIClientAsync(api)
|
|
900
954
|
return self._client_async
|
|
901
955
|
|
|
956
|
+
@classmethod
|
|
957
|
+
def _date_to_string(cls, compatibility_date: date) -> str:
|
|
958
|
+
"""Turns a date object in a compatibility_date string"""
|
|
959
|
+
return f"{compatibility_date.year}-{compatibility_date.month:02}-{compatibility_date.day:02}"
|
|
960
|
+
|
|
961
|
+
|
|
902
962
|
def __str__(self) -> str:
|
|
903
963
|
return "ESIClientProvider"
|
esi/stubs.pyi
CHANGED
|
@@ -2010,6 +2010,36 @@ class GetMarketsStructuresStructureIdOperation(EsiOperation):
|
|
|
2010
2010
|
...
|
|
2011
2011
|
|
|
2012
2012
|
|
|
2013
|
+
class GetMetaChangelogOperation(EsiOperation):
|
|
2014
|
+
"""EsiOperation, use result(), results() or results_localized()"""
|
|
2015
|
+
def result(self, etag: str | None = None, return_response: bool = False, use_cache: bool = True, **extra) -> dict[str, Any]:
|
|
2016
|
+
"""Get the changelog of this API."""
|
|
2017
|
+
...
|
|
2018
|
+
|
|
2019
|
+
def results(self, etag: str | None = None, return_response: bool = False, use_cache: bool = True, **extra) -> list[dict[str, Any]]:
|
|
2020
|
+
"""Get the changelog of this API."""
|
|
2021
|
+
...
|
|
2022
|
+
|
|
2023
|
+
def results_localized(self, languages: list[str] | str | None = None, **extra) -> dict[str, list[dict[str, Any]]]:
|
|
2024
|
+
"""Get the changelog of this API."""
|
|
2025
|
+
...
|
|
2026
|
+
|
|
2027
|
+
|
|
2028
|
+
class GetMetaCompatibilityDatesOperation(EsiOperation):
|
|
2029
|
+
"""EsiOperation, use result(), results() or results_localized()"""
|
|
2030
|
+
def result(self, etag: str | None = None, return_response: bool = False, use_cache: bool = True, **extra) -> dict[str, Any]:
|
|
2031
|
+
"""Get a list of compatibility dates."""
|
|
2032
|
+
...
|
|
2033
|
+
|
|
2034
|
+
def results(self, etag: str | None = None, return_response: bool = False, use_cache: bool = True, **extra) -> list[dict[str, Any]]:
|
|
2035
|
+
"""Get a list of compatibility dates."""
|
|
2036
|
+
...
|
|
2037
|
+
|
|
2038
|
+
def results_localized(self, languages: list[str] | str | None = None, **extra) -> dict[str, list[dict[str, Any]]]:
|
|
2039
|
+
"""Get a list of compatibility dates."""
|
|
2040
|
+
...
|
|
2041
|
+
|
|
2042
|
+
|
|
2013
2043
|
class GetCharactersCharacterIdPlanetsOperation(EsiOperation):
|
|
2014
2044
|
"""EsiOperation, use result(), results() or results_localized()"""
|
|
2015
2045
|
def result(self, etag: str | None = None, return_response: bool = False, use_cache: bool = True, **extra) -> list[dict[str, Any]]:
|
|
@@ -3518,6 +3548,18 @@ class ESIClientStub:
|
|
|
3518
3548
|
|
|
3519
3549
|
Market: _Market = _Market()
|
|
3520
3550
|
|
|
3551
|
+
class _Meta:
|
|
3552
|
+
def GetMetaChangelog(self, Accept_Language: str | None = ..., If_None_Match: str | None = ..., X_Compatibility_Date: str | None = ..., X_Tenant: str | None = ..., **kwargs: Any) -> GetMetaChangelogOperation:
|
|
3553
|
+
"""Get the changelog of this API."""
|
|
3554
|
+
...
|
|
3555
|
+
|
|
3556
|
+
def GetMetaCompatibilityDates(self, Accept_Language: str | None = ..., If_None_Match: str | None = ..., X_Compatibility_Date: str | None = ..., X_Tenant: str | None = ..., **kwargs: Any) -> GetMetaCompatibilityDatesOperation:
|
|
3557
|
+
"""Get a list of compatibility dates."""
|
|
3558
|
+
...
|
|
3559
|
+
|
|
3560
|
+
|
|
3561
|
+
Meta: _Meta = _Meta()
|
|
3562
|
+
|
|
3521
3563
|
class _Planetary_Interaction:
|
|
3522
3564
|
def GetCharactersCharacterIdPlanets(self, character_id: int, token: Token, Accept_Language: str | None = ..., If_None_Match: str | None = ..., X_Compatibility_Date: str | None = ..., X_Tenant: str | None = ..., **kwargs: Any) -> GetCharactersCharacterIdPlanetsOperation:
|
|
3523
3565
|
"""Returns a list of all planetary colonies owned by a character."""
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from django.test import TestCase
|
|
2
|
+
from datetime import date
|
|
3
|
+
|
|
4
|
+
from esi.openapi_clients import ESIClientProvider
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TestOpenapiClientProvider(TestCase):
|
|
8
|
+
|
|
9
|
+
def test_compatibilitydate_date_to_string(self):
|
|
10
|
+
testdate_1 = date(2024, 1, 1)
|
|
11
|
+
testdate_2 = date(2025, 8, 26)
|
|
12
|
+
|
|
13
|
+
self.assertEqual("2024-01-01", ESIClientProvider._date_to_string(testdate_1))
|
|
14
|
+
self.assertEqual("2025-08-26", ESIClientProvider._date_to_string(testdate_2))
|
|
File without changes
|
|
File without changes
|