django-esi 8.0.0a2__tar.gz → 8.0.0a3__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.

Potentially problematic release.


This version of django-esi might be problematic. Click here for more details.

Files changed (86) hide show
  1. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/PKG-INFO +1 -1
  2. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/__init__.py +2 -2
  3. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/aiopenapi3/plugins.py +21 -0
  4. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/exceptions.py +29 -0
  5. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/locale/en/LC_MESSAGES/django.po +3 -3
  6. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/locale/ru/LC_MESSAGES/django.po +8 -7
  7. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/openapi_clients.py +89 -29
  8. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/stubs.pyi +42 -0
  9. django_esi-8.0.0a3/esi/tests/test_openapi.py +14 -0
  10. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/LICENSE +0 -0
  11. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/README.md +0 -0
  12. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/admin.py +0 -0
  13. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/app_settings.py +0 -0
  14. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/apps.py +0 -0
  15. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/checks.py +0 -0
  16. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/clients.py +0 -0
  17. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/decorators.py +0 -0
  18. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/errors.py +0 -0
  19. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/helpers.py +0 -0
  20. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/locale/de/LC_MESSAGES/django.mo +0 -0
  21. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/locale/de/LC_MESSAGES/django.po +0 -0
  22. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/locale/en/LC_MESSAGES/django.mo +0 -0
  23. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/locale/es/LC_MESSAGES/django.mo +0 -0
  24. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/locale/es/LC_MESSAGES/django.po +0 -0
  25. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/locale/fr_FR/LC_MESSAGES/django.mo +0 -0
  26. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/locale/fr_FR/LC_MESSAGES/django.po +0 -0
  27. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/locale/it_IT/LC_MESSAGES/django.mo +0 -0
  28. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/locale/it_IT/LC_MESSAGES/django.po +0 -0
  29. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/locale/ja/LC_MESSAGES/django.mo +0 -0
  30. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/locale/ja/LC_MESSAGES/django.po +0 -0
  31. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/locale/ko_KR/LC_MESSAGES/django.mo +0 -0
  32. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/locale/ko_KR/LC_MESSAGES/django.po +0 -0
  33. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/locale/ru/LC_MESSAGES/django.mo +0 -0
  34. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
  35. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/locale/zh_Hans/LC_MESSAGES/django.po +0 -0
  36. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/management/commands/__init__.py +0 -0
  37. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/management/commands/generate_esi_stubs.py +0 -0
  38. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/management/commands/migrate_to_ssov2.py +0 -0
  39. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/managers.py +0 -0
  40. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/managers.pyi +0 -0
  41. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/migrations/0001_initial.py +0 -0
  42. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/migrations/0002_scopes_20161208.py +0 -0
  43. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/migrations/0003_hide_tokens_from_admin_site.py +0 -0
  44. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/migrations/0004_remove_unique_access_token.py +0 -0
  45. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/migrations/0005_remove_token_length_limit.py +0 -0
  46. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/migrations/0006_remove_url_length_limit.py +0 -0
  47. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/migrations/0007_fix_mysql_8_migration.py +0 -0
  48. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/migrations/0008_nullable_refresh_token.py +0 -0
  49. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/migrations/0009_set_old_tokens_to_sso_v1.py +0 -0
  50. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/migrations/0010_set_new_tokens_to_sso_v2.py +0 -0
  51. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/migrations/0011_add_token_indices.py +0 -0
  52. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/migrations/0012_fix_token_type_choices.py +0 -0
  53. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/migrations/0013_squashed_0012_fix_token_type_choices.py +0 -0
  54. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/migrations/__init__.py +0 -0
  55. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/models.py +0 -0
  56. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/rate_limiting.py +0 -0
  57. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/static/esi/img/EVE_SSO_Login_Buttons_Large_Black.png +0 -0
  58. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/static/esi/img/EVE_SSO_Login_Buttons_Large_White.png +0 -0
  59. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/static/esi/img/EVE_SSO_Login_Buttons_Small_Black.png +0 -0
  60. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/static/esi/img/EVE_SSO_Login_Buttons_Small_White.png +0 -0
  61. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/stubs.py +0 -0
  62. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/tasks.py +0 -0
  63. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/templates/esi/select_token.html +0 -0
  64. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/templatetags/__init__.py +0 -0
  65. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/templatetags/scope_tags.py +0 -0
  66. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/tests/__init__.py +0 -0
  67. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/tests/client_authed_pilot.py +0 -0
  68. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/tests/client_public_pilot.py +0 -0
  69. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/tests/factories.py +0 -0
  70. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/tests/factories_2.py +0 -0
  71. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/tests/jwt_factory.py +0 -0
  72. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/tests/test_checks.py +0 -0
  73. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/tests/test_clients.py +0 -0
  74. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/tests/test_decorators.py +0 -0
  75. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/tests/test_management_command.py +0 -0
  76. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/tests/test_managers.py +0 -0
  77. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/tests/test_models.py +0 -0
  78. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/tests/test_swagger.json +0 -0
  79. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/tests/test_swagger_full.json +0 -0
  80. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/tests/test_tasks.py +0 -0
  81. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/tests/test_templatetags.py +0 -0
  82. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/tests/test_views.py +0 -0
  83. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/tests/threading_pilot.py +0 -0
  84. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/urls.py +0 -0
  85. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/esi/views.py +0 -0
  86. {django_esi-8.0.0a2 → django_esi-8.0.0a3}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-esi
3
- Version: 8.0.0a2
3
+ Version: 8.0.0a3
4
4
  Summary: Django app for accessing the EVE Swagger Interface (ESI).
5
5
  Keywords: eveonline
6
6
  Author-email: Alliance Auth <adarnof@gmail.com>
@@ -1,6 +1,6 @@
1
1
  """Django app for accessing the EVE Swagger Interface (ESI)."""
2
2
 
3
- __version__ = '8.0.0a2'
3
+ __version__ = '8.0.0a3'
4
4
  __title__ = 'Django-ESI'
5
5
  __url__ = 'https://gitlab.com/allianceauth/django-esi'
6
- __build_date__ = "2025-09-03"
6
+ __build_date__ = "2025-09-17"
@@ -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
@@ -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-06-30 10:57+1000\n"
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:73
21
+ #: esi/models.py:66
22
22
  msgid "Character"
23
23
  msgstr ""
24
24
 
25
- #: esi/models.py:74
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: 2023-10-08 23:42+1000\n"
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: Filipp Chertiev <f@fzfx.ru>, 2023\n"
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:72
25
+ #: esi/models.py:73
25
26
  msgid "Character"
26
- msgstr ""
27
+ msgstr "Персонаж"
27
28
 
28
- #: esi/models.py:73
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
@@ -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.aiopenapi3.plugins import PatchCompatibilityDatePlugin, Trim204ContentType
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
- data = (self.method + self.url + str(self._args) + str(self._kwargs)).encode('utf-8')
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
- headers, data = self.parse_cached_request(cached_response)
387
+ expiry = _time_to_expiry(str(cached_response.headers.get('Expires')))
380
388
 
381
- expiry = _time_to_expiry(str(headers.get('Expires')))
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
- headers, data, response = self._make_request(parameters, etag)
503
- if response.status_code == 420:
504
- reset = response.headers.get("X-RateLimit-Reset", None)
505
- cache.set("esi_error_limit_reset", reset, timeout=reset)
506
- raise ESIErrorLimitException(reset=reset)
507
- # if response.status_code == 304 and app_settings.ESI_CACHE_RESPONSE:
508
- # cached = cache.get(cache_key)
509
- # if cached:
510
- # return (cached, response) if return_response else cached
511
- # we dont want to do this, if we do this we have to store data longer than ttl. rip ram
512
- self._store_cache(cache_key, response)
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(language=language, **kwargs)
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(language=language, **extra)
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
- elif tag in set(self.api._operationindex._tags.keys()):
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
- elif tag in set(self.api._operationindex._tags.keys()):
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
- self._compatibility_date = compatibility_date
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"
@@ -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
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