mirror of
https://gitlab.com/allianceauth/allianceauth.git
synced 2025-07-18 08:50:17 +02:00
Compare commits
11 Commits
762a28948f
...
c4835cd065
Author | SHA1 | Date | |
---|---|---|---|
|
c4835cd065 | ||
|
4021b2dc72 | ||
|
10dac36dcc | ||
|
0ff17de419 | ||
|
6ee6986174 | ||
|
49364e7d27 | ||
|
f15c4fc708 | ||
|
6452b082a8 | ||
|
daaffaeabc | ||
|
95608db611 | ||
|
523aac6a08 |
@ -24,20 +24,14 @@ exclude: |
|
|||||||
)
|
)
|
||||||
|
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
# Code Upgrades
|
||||||
rev: v0.11.4
|
|
||||||
hooks:
|
|
||||||
# Run the linter, and only the linter
|
|
||||||
- id: ruff
|
|
||||||
|
|
||||||
- repo: https://github.com/adamchainz/django-upgrade
|
- repo: https://github.com/adamchainz/django-upgrade
|
||||||
rev: 1.24.0
|
rev: 1.25.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: django-upgrade
|
- id: django-upgrade
|
||||||
args: [--target-version=5.1]
|
args: [--target-version=5.2]
|
||||||
|
- repo: https://github.com/asottile/pyupgrade
|
||||||
- repo: https://github.com/asottile/pyupgrade # Ruff doesnt get everything.
|
rev: v3.20.0
|
||||||
rev: v3.19.1
|
|
||||||
hooks:
|
hooks:
|
||||||
- id: pyupgrade
|
- id: pyupgrade
|
||||||
args: [--py310-plus]
|
args: [--py310-plus]
|
||||||
@ -59,7 +53,7 @@ repos:
|
|||||||
- id: detect-private-key
|
- id: detect-private-key
|
||||||
- id: check-case-conflict
|
- id: check-case-conflict
|
||||||
# Python checks
|
# Python checks
|
||||||
#
|
# - id: check-docstring-first
|
||||||
- id: debug-statements
|
- id: debug-statements
|
||||||
# - id: requirements-txt-fixer
|
# - id: requirements-txt-fixer
|
||||||
- id: fix-encoding-pragma
|
- id: fix-encoding-pragma
|
||||||
@ -77,21 +71,22 @@ repos:
|
|||||||
hooks:
|
hooks:
|
||||||
- id: editorconfig-checker
|
- id: editorconfig-checker
|
||||||
- repo: https://github.com/igorshubovych/markdownlint-cli
|
- repo: https://github.com/igorshubovych/markdownlint-cli
|
||||||
rev: v0.44.0
|
rev: v0.45.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: markdownlint
|
- id: markdownlint
|
||||||
language: node
|
language: node
|
||||||
args:
|
args:
|
||||||
- --disable=MD013
|
- --disable=MD013
|
||||||
|
|
||||||
# Infrastructure
|
# Infrastructure
|
||||||
- repo: https://github.com/tox-dev/pyproject-fmt
|
- repo: https://github.com/tox-dev/pyproject-fmt
|
||||||
rev: v2.5.1
|
rev: v2.6.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: pyproject-fmt
|
- id: pyproject-fmt
|
||||||
args:
|
args:
|
||||||
- --indent=4
|
- --indent=4
|
||||||
additional_dependencies:
|
additional_dependencies:
|
||||||
- tox==4.24.1 # https://github.com/tox-dev/tox/releases/latest
|
- tox==4.26.0 # https://github.com/tox-dev/tox/releases/latest
|
||||||
- repo: https://github.com/tox-dev/tox-ini-fmt
|
- repo: https://github.com/tox-dev/tox-ini-fmt
|
||||||
rev: 1.5.0
|
rev: 1.5.0
|
||||||
hooks:
|
hooks:
|
||||||
|
@ -5,7 +5,7 @@ manage online service access.
|
|||||||
# This will make sure the app is always imported when
|
# This will make sure the app is always imported when
|
||||||
# Django starts so that shared_task will use this app.
|
# Django starts so that shared_task will use this app.
|
||||||
|
|
||||||
__version__ = '5.0.0a1'
|
__version__ = '5.0.0a3'
|
||||||
__title__ = 'Alliance Auth'
|
__title__ = 'AllianceAuth'
|
||||||
__url__ = 'https://gitlab.com/allianceauth/allianceauth'
|
__url__ = 'https://gitlab.com/allianceauth/allianceauth'
|
||||||
NAME = f'{__title__} v{__version__}'
|
NAME = f'{__title__} v{__version__}'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
from bravado.client import SwaggerClient
|
||||||
from bravado.exception import HTTPError, HTTPNotFound, HTTPUnprocessableEntity
|
from bravado.exception import HTTPError, HTTPNotFound, HTTPUnprocessableEntity
|
||||||
from jsonschema.exceptions import RefResolutionError
|
from jsonschema.exceptions import RefResolutionError
|
||||||
|
|
||||||
@ -8,7 +9,7 @@ from django.conf import settings
|
|||||||
|
|
||||||
from esi.clients import esi_client_factory
|
from esi.clients import esi_client_factory
|
||||||
|
|
||||||
from allianceauth import __version__
|
from allianceauth import __version__, __title__, __url__
|
||||||
from allianceauth.utils.django import StartupCommand
|
from allianceauth.utils.django import StartupCommand
|
||||||
|
|
||||||
SWAGGER_SPEC_PATH = os.path.join(os.path.dirname(
|
SWAGGER_SPEC_PATH = os.path.join(os.path.dirname(
|
||||||
@ -175,7 +176,11 @@ class EveProvider:
|
|||||||
|
|
||||||
|
|
||||||
class EveSwaggerProvider(EveProvider):
|
class EveSwaggerProvider(EveProvider):
|
||||||
def __init__(self, token=None, adapter=None):
|
def __init__(self, token=None, adapter=None) -> None:
|
||||||
|
self._token = token
|
||||||
|
self.adapter = adapter or self
|
||||||
|
self._faction_list = None # what are the odds this will change? could cache forever!
|
||||||
|
|
||||||
if settings.DEBUG or StartupCommand().is_management_command:
|
if settings.DEBUG or StartupCommand().is_management_command:
|
||||||
self._client = None
|
self._client = None
|
||||||
logger.info('ESI client will be loaded on-demand')
|
logger.info('ESI client will be loaded on-demand')
|
||||||
@ -183,9 +188,10 @@ class EveSwaggerProvider(EveProvider):
|
|||||||
logger.info('Loading ESI client')
|
logger.info('Loading ESI client')
|
||||||
try:
|
try:
|
||||||
self._client = esi_client_factory(
|
self._client = esi_client_factory(
|
||||||
token=token,
|
token=self._token,
|
||||||
spec_file=SWAGGER_SPEC_PATH,
|
ua_appname=__title__,
|
||||||
app_info_text=f"allianceauth v{__version__}"
|
ua_version=__version__,
|
||||||
|
ua_url=__url__,
|
||||||
)
|
)
|
||||||
except (HTTPError, RefResolutionError):
|
except (HTTPError, RefResolutionError):
|
||||||
logger.exception(
|
logger.exception(
|
||||||
@ -194,15 +200,14 @@ class EveSwaggerProvider(EveProvider):
|
|||||||
)
|
)
|
||||||
self._client = None
|
self._client = None
|
||||||
|
|
||||||
self._token = token
|
|
||||||
self.adapter = adapter or self
|
|
||||||
self._faction_list = None # what are the odds this will change? could cache forever!
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def client(self):
|
def client(self) -> SwaggerClient:
|
||||||
if self._client is None:
|
if self._client is None:
|
||||||
self._client = esi_client_factory(
|
self._client = esi_client_factory(
|
||||||
token=self._token, spec_file=SWAGGER_SPEC_PATH, app_info_text=("allianceauth v" + __version__)
|
token=self._token,
|
||||||
|
ua_appname=__title__,
|
||||||
|
ua_version=__version__,
|
||||||
|
ua_url=__url__,
|
||||||
)
|
)
|
||||||
return self._client
|
return self._client
|
||||||
|
|
||||||
|
File diff suppressed because one or more lines are too long
@ -676,16 +676,6 @@ class TestEveSwaggerProvider(TestCase):
|
|||||||
self.assertTrue(mock_esi_client_factory.called)
|
self.assertTrue(mock_esi_client_factory.called)
|
||||||
self.assertIsNotNone(my_provider._client)
|
self.assertIsNotNone(my_provider._client)
|
||||||
|
|
||||||
@patch(MODULE_PATH + '.SWAGGER_SPEC_PATH', SWAGGER_OLD_SPEC_PATH)
|
|
||||||
@patch(MODULE_PATH + '.settings.DEBUG', False)
|
|
||||||
@patch('socket.socket')
|
|
||||||
def test_create_client_on_normal_startup_w_old_swagger_spec(
|
|
||||||
self, mock_socket
|
|
||||||
):
|
|
||||||
mock_socket.side_effect = Exception('Network blocked for testing')
|
|
||||||
my_provider = EveSwaggerProvider()
|
|
||||||
self.assertIsNone(my_provider._client)
|
|
||||||
|
|
||||||
@patch(MODULE_PATH + '.settings.DEBUG', True)
|
@patch(MODULE_PATH + '.settings.DEBUG', True)
|
||||||
@patch(MODULE_PATH + '.esi_client_factory')
|
@patch(MODULE_PATH + '.esi_client_factory')
|
||||||
def test_dont_create_client_on_debug_startup(self, mock_esi_client_factory):
|
def test_dont_create_client_on_debug_startup(self, mock_esi_client_factory):
|
||||||
@ -722,6 +712,6 @@ class TestEveSwaggerProvider(TestCase):
|
|||||||
my_provider = EveSwaggerProvider()
|
my_provider = EveSwaggerProvider()
|
||||||
my_client = my_provider.client
|
my_client = my_provider.client
|
||||||
operation = my_client.Universe.get_universe_factions()
|
operation = my_client.Universe.get_universe_factions()
|
||||||
self.assertEqual(
|
self.assertIn(
|
||||||
operation.future.request.headers['User-Agent'], 'allianceauth v1.0.0 dummy@example.net'
|
'AllianceAuth/1.0.0 (dummy@example.net; +https://gitlab.com/allianceauth/allianceauth)', operation.future.request.headers['User-Agent']
|
||||||
)
|
)
|
||||||
|
@ -57,9 +57,10 @@ CELERYBEAT_SCHEDULE = {
|
|||||||
'task': 'esi.tasks.cleanup_callbackredirect',
|
'task': 'esi.tasks.cleanup_callbackredirect',
|
||||||
'schedule': crontab(minute='0', hour='*/4'),
|
'schedule': crontab(minute='0', hour='*/4'),
|
||||||
},
|
},
|
||||||
'esi_cleanup_token': {
|
'esi_cleanup_token': { # 1/48th * 1hr = 48Hr/2Day Refresh Cycles.
|
||||||
'task': 'esi.tasks.cleanup_token',
|
'task': 'esi.tasks.cleanup_token_subset',
|
||||||
'schedule': crontab(minute='0', hour='0'),
|
'schedule': crontab(minute="0", hour="*"),
|
||||||
|
'apply_offset': True
|
||||||
},
|
},
|
||||||
'run_model_update': {
|
'run_model_update': {
|
||||||
'task': 'allianceauth.eveonline.tasks.run_model_update',
|
'task': 'allianceauth.eveonline.tasks.run_model_update',
|
||||||
|
@ -2,7 +2,7 @@ import os
|
|||||||
|
|
||||||
from esi.clients import EsiClientProvider
|
from esi.clients import EsiClientProvider
|
||||||
|
|
||||||
from allianceauth import __version__
|
from allianceauth import __version__, __title__, __url__
|
||||||
|
|
||||||
SWAGGER_SPEC = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'swagger.json')
|
SWAGGER_SPEC = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'swagger.json')
|
||||||
|
|
||||||
@ -12,8 +12,8 @@ get_killmails_killmail_id_killmail_hash
|
|||||||
get_universe_types_type_id
|
get_universe_types_type_id
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
esi = EsiClientProvider(
|
esi = EsiClientProvider(
|
||||||
spec_file=SWAGGER_SPEC,
|
ua_appname=__title__,
|
||||||
app_info_text=("allianceauth v" + __version__)
|
ua_version=__version__,
|
||||||
|
ua_url=__url__,
|
||||||
)
|
)
|
||||||
|
File diff suppressed because one or more lines are too long
@ -1,7 +1,7 @@
|
|||||||
PROTOCOL=https://
|
PROTOCOL=https://
|
||||||
AUTH_SUBDOMAIN=%AUTH_SUBDOMAIN%
|
AUTH_SUBDOMAIN=%AUTH_SUBDOMAIN%
|
||||||
DOMAIN=%DOMAIN%
|
DOMAIN=%DOMAIN%
|
||||||
AA_DOCKER_TAG=registry.gitlab.com/allianceauth/allianceauth/auth:v5.0.0a1
|
AA_DOCKER_TAG=registry.gitlab.com/allianceauth/allianceauth/auth:v5.0.0a3
|
||||||
|
|
||||||
# Nginx Proxy Manager
|
# Nginx Proxy Manager
|
||||||
PROXY_HTTP_PORT=80
|
PROXY_HTTP_PORT=80
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
FROM python:3.12-slim
|
FROM python:3.12-slim
|
||||||
ARG AUTH_VERSION=v5.0.0a1
|
ARG AUTH_VERSION=v5.0.0a3
|
||||||
ARG AUTH_PACKAGE=allianceauth==${AUTH_VERSION}
|
ARG AUTH_PACKAGE=allianceauth==${AUTH_VERSION}
|
||||||
ENV AUTH_USER=allianceauth
|
ENV AUTH_USER=allianceauth
|
||||||
ENV AUTH_GROUP=allianceauth
|
ENV AUTH_GROUP=allianceauth
|
||||||
|
@ -266,14 +266,14 @@ Every Alliance Auth installation will come with a couple of special celery relat
|
|||||||
|
|
||||||
Celery-once is a celery extension "that allows you to prevent multiple execution and queuing of celery tasks". What that means is that you can ensure that only one instance of a celery task runs at any given time. This can be useful, for example, if you do not want multiple instances of your task to talk to the same external service at the same time.
|
Celery-once is a celery extension "that allows you to prevent multiple execution and queuing of celery tasks". What that means is that you can ensure that only one instance of a celery task runs at any given time. This can be useful, for example, if you do not want multiple instances of your task to talk to the same external service at the same time.
|
||||||
|
|
||||||
We use a custom backend for celery_once in Alliance Auth defined [here](https://gitlab.com/allianceauth/allianceauth/-/blob/master/allianceauth/services/tasks.py#L14)
|
We use a custom backend for celery_once in Alliance Auth defined in [allianceauth.services.tasks](https://gitlab.com/allianceauth/allianceauth/-/blob/master/allianceauth/services/tasks.py#L14)
|
||||||
You can import it for use like so:
|
You can import it for use like so:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from allianceauth.services.tasks import QueueOnce
|
from allianceauth.services.tasks import QueueOnce
|
||||||
```
|
```
|
||||||
|
|
||||||
An example of Alliance Auth's use within the `@sharedtask` decorator, can be seen [here](https://gitlab.com/allianceauth/allianceauth/-/blob/master/allianceauth/services/modules/discord/tasks.py#L62) in the discord module
|
An example of Alliance Auth's use within the `@sharedtask` decorator, can be seen in [allianceauth.services.modules.discord.tasks](https://gitlab.com/allianceauth/allianceauth/-/blob/master/allianceauth/services/modules/discord/tasks.py#L62) in the discord module
|
||||||
You can use it like so:
|
You can use it like so:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
@ -17,7 +17,7 @@ If at any point `docker compose` does not work, but `docker-compose` does, you h
|
|||||||
1. run `bash <(curl -s https://gitlab.com/allianceauth/allianceauth/-/raw/master/docker/scripts/download.sh)`. This will download all the files you need to install Alliance Auth and place them in a directory named `aa-docker`. Feel free to rename/move this folder.
|
1. run `bash <(curl -s https://gitlab.com/allianceauth/allianceauth/-/raw/master/docker/scripts/download.sh)`. This will download all the files you need to install Alliance Auth and place them in a directory named `aa-docker`. Feel free to rename/move this folder.
|
||||||
1. run `./scripts/prepare-env.sh` to set up your environment
|
1. run `./scripts/prepare-env.sh` to set up your environment
|
||||||
1. (optional) Change `PROTOCOL` to `http://` if not using SSL in `.env`
|
1. (optional) Change `PROTOCOL` to `http://` if not using SSL in `.env`
|
||||||
1. run `docker compose --env-file=.env up -d` (NOTE: if this command hangs, follow the instructions [here](https://www.digitalocean.com/community/tutorials/how-to-setup-additional-entropy-for-cloud-servers-using-haveged))
|
1. run `docker compose --env-file=.env up -d` (NOTE: if this command hangs, follow the instructions on [How to Setup Entropy](https://www.digitalocean.com/community/tutorials/how-to-setup-additional-entropy-for-cloud-servers-using-haveged))
|
||||||
1. run `docker compose exec allianceauth_gunicorn bash` to open up a terminal inside an auth container
|
1. run `docker compose exec allianceauth_gunicorn bash` to open up a terminal inside an auth container
|
||||||
1. run `auth migrate`
|
1. run `auth migrate`
|
||||||
1. run `auth collectstatic`
|
1. run `auth collectstatic`
|
||||||
|
@ -69,7 +69,7 @@ Whatever you decide to use, remember it because we'll need it when configuring y
|
|||||||
|
|
||||||
##### Number of workers
|
##### Number of workers
|
||||||
|
|
||||||
By default, Gunicorn will spawn only one worker. The number you set this to will depend on your own server environment, how many visitors you have etc. Gunicorn suggests `(2 x $num_cores) + 1` for the number of workers. So, for example, if you have 2 cores, you want 2 x 2 + 1 = 5 workers. See [here](https://docs.gunicorn.org/en/stable/design.html#how-many-workers) for the official discussion on this topic.
|
By default, Gunicorn will spawn only one worker. The number you set this to will depend on your own server environment, how many visitors you have etc. Gunicorn suggests `(2 x $num_cores) + 1` for the number of workers. So, for example, if you have 2 cores, you want 2 x 2 + 1 = 5 workers. See [How Mnay Workers](https://docs.gunicorn.org/en/stable/design.html#how-many-workers) for the official discussion on this topic.
|
||||||
|
|
||||||
Change it by adding `--workers=5` to the command.
|
Change it by adding `--workers=5` to the command.
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
The default installation will have 3 workers configured for Gunicorn. This will be fine on most systems, but if your system as more than one core than you might want to increase the number of workers to get better response times. Note that more workers will also need more RAM though.
|
The default installation will have 3 workers configured for Gunicorn. This will be fine on most systems, but if your system as more than one core than you might want to increase the number of workers to get better response times. Note that more workers will also need more RAM though.
|
||||||
|
|
||||||
The number you set this to will depend on your own server environment, how many visitors you have etc. Gunicorn suggests `(2 x $num_cores) + 1` for the number of workers. So for example, if you have 2 cores, you want 2 x 2 + 1 = 5 workers. See [here](https://docs.gunicorn.org/en/stable/design.html#how-many-workers) for the official discussion on this topic.
|
The number you set this to will depend on your own server environment, how many visitors you have etc. Gunicorn suggests `(2 x $num_cores) + 1` for the number of workers. So for example, if you have 2 cores, you want 2 x 2 + 1 = 5 workers. See [How Many Workers](https://docs.gunicorn.org/en/stable/design.html#how-many-workers) for the official discussion on this topic.
|
||||||
|
|
||||||
::::{tabs}
|
::::{tabs}
|
||||||
:::{group-tab} Ubuntu 2204, 2404
|
:::{group-tab} Ubuntu 2204, 2404
|
||||||
|
@ -20,7 +20,7 @@ classifiers = [
|
|||||||
"Environment :: Web Environment",
|
"Environment :: Web Environment",
|
||||||
"Framework :: Celery",
|
"Framework :: Celery",
|
||||||
"Framework :: Django",
|
"Framework :: Django",
|
||||||
"Framework :: Django :: 5.1",
|
"Framework :: Django :: 5.2",
|
||||||
"Intended Audience :: Developers",
|
"Intended Audience :: Developers",
|
||||||
"License :: OSI Approved :: GNU General Public License v2 (GPLv2)",
|
"License :: OSI Approved :: GNU General Public License v2 (GPLv2)",
|
||||||
"Operating System :: POSIX :: Linux",
|
"Operating System :: POSIX :: Linux",
|
||||||
@ -42,13 +42,13 @@ dependencies = [
|
|||||||
"beautifulsoup4",
|
"beautifulsoup4",
|
||||||
"celery>=5.5,<6",
|
"celery>=5.5,<6",
|
||||||
"celery-once>=3.0.1",
|
"celery-once>=3.0.1",
|
||||||
"django>=5.1,<5.2",
|
"django>=5.2,<6",
|
||||||
"django-bootstrap-form",
|
"django-bootstrap-form",
|
||||||
"django-bootstrap5>=23.3",
|
"django-bootstrap5>=23.3",
|
||||||
"django-celery-beat>=2.7",
|
"django-celery-beat>=2.8",
|
||||||
"django-esi>=5",
|
"django-esi>=7.0.0b1",
|
||||||
"django-redis>=5.4",
|
"django-redis>=5.4",
|
||||||
"django-registration>=5.1,<6",
|
"django-registration>=5.2,<6",
|
||||||
"django-solo",
|
"django-solo",
|
||||||
"django-sortedm2m",
|
"django-sortedm2m",
|
||||||
"django-sri",
|
"django-sri",
|
||||||
@ -87,50 +87,6 @@ scripts.allianceauth = "allianceauth.bin.allianceauth:main"
|
|||||||
[tool.flit.module]
|
[tool.flit.module]
|
||||||
name = "allianceauth"
|
name = "allianceauth"
|
||||||
|
|
||||||
[tool.ruff]
|
|
||||||
line-length = 119
|
|
||||||
format.line-ending = "lf"
|
|
||||||
lint.select = [
|
|
||||||
"B", # flake8-bugbear
|
|
||||||
"C", # pylint convention
|
|
||||||
# "D", # pydocstyle, Want to turn these on, but our docstrings are lightly used
|
|
||||||
"D300", # use triple double-quotes in docstrings PEP 257
|
|
||||||
"DJ", # flake8-django
|
|
||||||
"DOC", # pylintdoc
|
|
||||||
"E", # pycodestyle error
|
|
||||||
"F", # pyflakes (flake8 base)
|
|
||||||
"G010", # logging-warn, Warn on using logging.warn
|
|
||||||
"I", # isort
|
|
||||||
"PGH005", # pygrep-hooks, python-check-mock-method
|
|
||||||
"RUF100", # basically yesqa
|
|
||||||
"UP", # pyupgrade, will target requires-python
|
|
||||||
"W", # pycodestyle Warning
|
|
||||||
]
|
|
||||||
lint.ignore = [
|
|
||||||
"E501", # Line too long, WIP across repo.
|
|
||||||
]
|
|
||||||
|
|
||||||
lint.per-file-ignores = { "*local.py" = [ "F405", "F403" ] }
|
|
||||||
|
|
||||||
lint.isort.combine-as-imports = true # profile=django
|
|
||||||
lint.isort.section-order = [
|
|
||||||
"future",
|
|
||||||
"standard-library",
|
|
||||||
"third-party",
|
|
||||||
"DJANGO",
|
|
||||||
"ESI",
|
|
||||||
"first-party",
|
|
||||||
"local-folder",
|
|
||||||
]
|
|
||||||
lint.isort.sections."DJANGO" = [
|
|
||||||
"django",
|
|
||||||
"django_redis",
|
|
||||||
"django_registration",
|
|
||||||
]
|
|
||||||
lint.isort.sections."ESI" = [
|
|
||||||
"esi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[tool.isort]
|
[tool.isort]
|
||||||
profile = "django"
|
profile = "django"
|
||||||
sections = [
|
sections = [
|
||||||
|
Loading…
x
Reference in New Issue
Block a user