Merge branch 'v0.5.x' of https://github.com/juanifioren/django-oidc-provider into develop
This commit is contained in:
commit
e66b374803
24 changed files with 167 additions and 165 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -12,3 +12,4 @@ src/
|
||||||
docs/_build/
|
docs/_build/
|
||||||
.eggs/
|
.eggs/
|
||||||
.python-version
|
.python-version
|
||||||
|
.pytest_cache/
|
||||||
|
|
|
@ -10,8 +10,11 @@ env:
|
||||||
- DJANGO=1.9
|
- DJANGO=1.9
|
||||||
- DJANGO=1.10
|
- DJANGO=1.10
|
||||||
- DJANGO=1.11
|
- DJANGO=1.11
|
||||||
|
- DJANGO=2.0
|
||||||
matrix:
|
matrix:
|
||||||
exclude:
|
exclude:
|
||||||
|
- python: "2.7"
|
||||||
|
env: DJANGO=2.0
|
||||||
- python: "3.5"
|
- python: "3.5"
|
||||||
env: DJANGO=1.7
|
env: DJANGO=1.7
|
||||||
- python: "3.6"
|
- python: "3.6"
|
||||||
|
|
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -2,10 +2,18 @@
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
### [Unreleased]
|
### Unreleased
|
||||||
|
|
||||||
##### Added
|
##### Added
|
||||||
- Add pep8 compliance and checker
|
- Add pep8 compliance and checker.
|
||||||
|
|
||||||
|
##### Changed
|
||||||
|
- Test suit now uses pytest.
|
||||||
|
|
||||||
|
### [0.5.3] - 2018-03-09
|
||||||
|
|
||||||
|
##### Fixed
|
||||||
|
- Update project to support Django 2.0
|
||||||
|
|
||||||
### [0.5.2] - 2017-08-22
|
### [0.5.2] - 2017-08-22
|
||||||
|
|
||||||
|
@ -13,7 +21,6 @@ All notable changes to this project will be documented in this file.
|
||||||
- Fix infinite login loop if "prompt=login" (#198)
|
- Fix infinite login loop if "prompt=login" (#198)
|
||||||
- Fix Django 2.0 deprecation warnings (#185)
|
- Fix Django 2.0 deprecation warnings (#185)
|
||||||
|
|
||||||
|
|
||||||
### [0.5.1] - 2017-07-11
|
### [0.5.1] - 2017-07-11
|
||||||
|
|
||||||
##### Changed
|
##### Changed
|
||||||
|
|
|
@ -7,7 +7,7 @@ Requirements
|
||||||
============
|
============
|
||||||
|
|
||||||
* Python: ``2.7`` ``3.4`` ``3.5`` ``3.6``
|
* Python: ``2.7`` ``3.4`` ``3.5`` ``3.6``
|
||||||
* Django: ``1.7`` ``1.8`` ``1.9`` ``1.10`` ``1.11``
|
* Django: ``1.7`` ``1.8`` ``1.9`` ``1.10`` ``1.11`` ``2.0``
|
||||||
|
|
||||||
Quick Installation
|
Quick Installation
|
||||||
==================
|
==================
|
||||||
|
|
|
@ -29,11 +29,11 @@ MIDDLEWARE_CLASSES = [
|
||||||
'django.middleware.common.CommonMiddleware',
|
'django.middleware.common.CommonMiddleware',
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
|
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
'oidc_provider.middleware.SessionManagementMiddleware',
|
'oidc_provider.middleware.SessionManagementMiddleware',
|
||||||
]
|
]
|
||||||
|
MIDDLEWARE = MIDDLEWARE_CLASSES
|
||||||
|
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
from django.contrib.auth import views as auth_views
|
from django.contrib.auth import views as auth_views
|
||||||
from django.conf.urls import include, url
|
try:
|
||||||
|
from django.urls import include, url
|
||||||
|
except ImportError:
|
||||||
|
from django.conf.urls import include, url
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.views.generic import TemplateView
|
from django.views.generic import TemplateView
|
||||||
|
|
||||||
|
@ -8,8 +11,6 @@ urlpatterns = [
|
||||||
url(r'^$', TemplateView.as_view(template_name='home.html'), name='home'),
|
url(r'^$', TemplateView.as_view(template_name='home.html'), name='home'),
|
||||||
url(r'^accounts/login/$', auth_views.login, {'template_name': 'login.html'}, name='login'),
|
url(r'^accounts/login/$', auth_views.login, {'template_name': 'login.html'}, name='login'),
|
||||||
url(r'^accounts/logout/$', auth_views.logout, {'next_page': '/'}, name='logout'),
|
url(r'^accounts/logout/$', auth_views.logout, {'next_page': '/'}, name='logout'),
|
||||||
|
|
||||||
url(r'^', include('oidc_provider.urls', namespace='oidc_provider')),
|
url(r'^', include('oidc_provider.urls', namespace='oidc_provider')),
|
||||||
|
url(r'^admin/', admin.site.urls),
|
||||||
url(r'^admin/', include(admin.site.urls)),
|
|
||||||
]
|
]
|
||||||
|
|
5
oidc_provider/compat.py
Normal file
5
oidc_provider/compat.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
def get_attr_or_callable(obj, name):
|
||||||
|
target = getattr(obj, name)
|
||||||
|
if callable(target):
|
||||||
|
return target()
|
||||||
|
return target
|
|
@ -1,5 +1,8 @@
|
||||||
from django.contrib.auth import views as auth_views
|
from django.contrib.auth import views as auth_views
|
||||||
from django.conf.urls import include, url
|
try:
|
||||||
|
from django.urls import include, url
|
||||||
|
except ImportError:
|
||||||
|
from django.conf.urls import include, url
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.views.generic import TemplateView
|
from django.views.generic import TemplateView
|
||||||
|
|
||||||
|
@ -11,5 +14,5 @@ urlpatterns = [
|
||||||
|
|
||||||
url(r'^openid/', include('oidc_provider.urls', namespace='oidc_provider')),
|
url(r'^openid/', include('oidc_provider.urls', namespace='oidc_provider')),
|
||||||
|
|
||||||
url(r'^admin/', include(admin.site.urls)),
|
url(r'^admin/', admin.site.urls),
|
||||||
]
|
]
|
||||||
|
|
|
@ -13,7 +13,10 @@ from mock import patch, mock
|
||||||
|
|
||||||
from django.contrib.auth.models import AnonymousUser
|
from django.contrib.auth.models import AnonymousUser
|
||||||
from django.core.management import call_command
|
from django.core.management import call_command
|
||||||
from django.core.urlresolvers import reverse
|
try:
|
||||||
|
from django.urls import reverse
|
||||||
|
except ImportError:
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
from django.test import (
|
from django.test import (
|
||||||
RequestFactory,
|
RequestFactory,
|
||||||
override_settings,
|
override_settings,
|
|
@ -1,5 +1,8 @@
|
||||||
from django.core.management import call_command
|
from django.core.management import call_command
|
||||||
from django.core.urlresolvers import reverse
|
try:
|
||||||
|
from django.urls import reverse
|
||||||
|
except ImportError:
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from oidc_provider.lib.utils.token import (
|
from oidc_provider.lib.utils.token import (
|
|
@ -1,4 +1,7 @@
|
||||||
from django.conf.urls import url
|
try:
|
||||||
|
from django.urls import url
|
||||||
|
except ImportError:
|
||||||
|
from django.conf.urls import url
|
||||||
from django.test import TestCase, override_settings
|
from django.test import TestCase, override_settings
|
||||||
from django.views.generic import View
|
from django.views.generic import View
|
||||||
from mock import mock
|
from mock import mock
|
|
@ -1,4 +1,7 @@
|
||||||
from django.core.urlresolvers import reverse
|
try:
|
||||||
|
from django.urls import reverse
|
||||||
|
except ImportError:
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
from django.test import RequestFactory
|
from django.test import RequestFactory
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
|
@ -9,7 +9,10 @@ except ImportError:
|
||||||
from urllib import urlencode
|
from urllib import urlencode
|
||||||
|
|
||||||
from django.core.management import call_command
|
from django.core.management import call_command
|
||||||
from django.core.urlresolvers import reverse
|
try:
|
||||||
|
from django.urls import reverse
|
||||||
|
except ImportError:
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
from django.test import (
|
from django.test import (
|
||||||
RequestFactory,
|
RequestFactory,
|
||||||
override_settings,
|
override_settings,
|
|
@ -6,7 +6,10 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from urllib import urlencode
|
from urllib import urlencode
|
||||||
|
|
||||||
from django.core.urlresolvers import reverse
|
try:
|
||||||
|
from django.urls import reverse
|
||||||
|
except ImportError:
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
from django.test import RequestFactory
|
from django.test import RequestFactory
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
79
oidc_provider/tests/settings.py
Normal file
79
oidc_provider/tests/settings.py
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
DEBUG = False
|
||||||
|
|
||||||
|
SECRET_KEY = 'this-should-be-top-secret'
|
||||||
|
|
||||||
|
DATABASES = {
|
||||||
|
'default': {
|
||||||
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
|
'NAME': ':memory:',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SITE_ID = 1
|
||||||
|
|
||||||
|
MIDDLEWARE_CLASSES = [
|
||||||
|
'django.middleware.common.CommonMiddleware',
|
||||||
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
|
]
|
||||||
|
|
||||||
|
MIDDLEWARE = [
|
||||||
|
'django.middleware.common.CommonMiddleware',
|
||||||
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
|
]
|
||||||
|
|
||||||
|
TEMPLATES = [
|
||||||
|
{
|
||||||
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
|
'DIRS': [],
|
||||||
|
'APP_DIRS': True,
|
||||||
|
'OPTIONS': {
|
||||||
|
'context_processors': [
|
||||||
|
'django.template.context_processors.debug',
|
||||||
|
'django.template.context_processors.request',
|
||||||
|
'django.contrib.auth.context_processors.auth',
|
||||||
|
'django.contrib.messages.context_processors.messages',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
INSTALLED_APPS = [
|
||||||
|
'django.contrib.auth',
|
||||||
|
'django.contrib.contenttypes',
|
||||||
|
'django.contrib.sessions',
|
||||||
|
'django.contrib.sites',
|
||||||
|
'django.contrib.messages',
|
||||||
|
'django.contrib.admin',
|
||||||
|
'oidc_provider',
|
||||||
|
]
|
||||||
|
|
||||||
|
ROOT_URLCONF = 'oidc_provider.tests.app.urls'
|
||||||
|
|
||||||
|
TEMPLATE_DIRS = [
|
||||||
|
'oidc_provider/tests/templates',
|
||||||
|
]
|
||||||
|
|
||||||
|
USE_TZ = True
|
||||||
|
|
||||||
|
LOGGING = {
|
||||||
|
'version': 1,
|
||||||
|
'disable_existing_loggers': False,
|
||||||
|
'handlers': {
|
||||||
|
'console': {
|
||||||
|
'class': 'logging.StreamHandler',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'loggers': {
|
||||||
|
'oidc_provider': {
|
||||||
|
'handlers': ['console'],
|
||||||
|
'level': 'DEBUG',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
# OIDC Provider settings.
|
||||||
|
|
||||||
|
SITE_URL = 'http://localhost:8000'
|
||||||
|
OIDC_USERINFO = 'oidc_provider.tests.app.utils.userinfo'
|
|
@ -1,4 +1,7 @@
|
||||||
from django.conf.urls import url
|
try:
|
||||||
|
from django.urls import url
|
||||||
|
except ImportError:
|
||||||
|
from django.conf.urls import url
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
|
||||||
from oidc_provider import (
|
from oidc_provider import (
|
||||||
|
|
|
@ -11,13 +11,10 @@ from django.contrib.auth.views import (
|
||||||
redirect_to_login,
|
redirect_to_login,
|
||||||
logout,
|
logout,
|
||||||
)
|
)
|
||||||
|
try:
|
||||||
import django
|
|
||||||
if django.VERSION >= (1, 11):
|
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
else:
|
except ImportError:
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
from django.contrib.auth import logout as django_user_logout
|
from django.contrib.auth import logout as django_user_logout
|
||||||
from django.http import JsonResponse
|
from django.http import JsonResponse
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
|
@ -28,6 +25,7 @@ from django.views.decorators.http import require_http_methods
|
||||||
from django.views.generic import View
|
from django.views.generic import View
|
||||||
from jwkest import long_to_base64
|
from jwkest import long_to_base64
|
||||||
|
|
||||||
|
from oidc_provider.compat import get_attr_or_callable
|
||||||
from oidc_provider.lib.claims import StandardScopeClaims
|
from oidc_provider.lib.claims import StandardScopeClaims
|
||||||
from oidc_provider.lib.endpoints.authorize import AuthorizeEndpoint
|
from oidc_provider.lib.endpoints.authorize import AuthorizeEndpoint
|
||||||
from oidc_provider.lib.endpoints.token import TokenEndpoint
|
from oidc_provider.lib.endpoints.token import TokenEndpoint
|
||||||
|
@ -65,7 +63,7 @@ class AuthorizeView(View):
|
||||||
try:
|
try:
|
||||||
authorize.validate_params()
|
authorize.validate_params()
|
||||||
|
|
||||||
if (request.user.is_authenticated if django.VERSION >= (1, 10) else request.user.is_authenticated()):
|
if get_attr_or_callable(request.user, 'is_authenticated'):
|
||||||
# Check if there's a hook setted.
|
# Check if there's a hook setted.
|
||||||
hook_resp = settings.get('OIDC_AFTER_USERLOGIN_HOOK', import_str=True)(
|
hook_resp = settings.get('OIDC_AFTER_USERLOGIN_HOOK', import_str=True)(
|
||||||
request=request, user=request.user,
|
request=request, user=request.user,
|
||||||
|
|
114
runtests.py
114
runtests.py
|
@ -1,114 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import django
|
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_SETTINGS = dict(
|
|
||||||
|
|
||||||
DEBUG=False,
|
|
||||||
|
|
||||||
DATABASES={
|
|
||||||
'default': {
|
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
|
||||||
'NAME': ':memory:',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
SITE_ID=1,
|
|
||||||
|
|
||||||
MIDDLEWARE_CLASSES=[
|
|
||||||
'django.middleware.common.CommonMiddleware',
|
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
|
||||||
],
|
|
||||||
|
|
||||||
TEMPLATES=[
|
|
||||||
{
|
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
|
||||||
'DIRS': [],
|
|
||||||
'APP_DIRS': True,
|
|
||||||
'OPTIONS': {
|
|
||||||
'context_processors': [
|
|
||||||
'django.template.context_processors.debug',
|
|
||||||
'django.template.context_processors.request',
|
|
||||||
'django.contrib.auth.context_processors.auth',
|
|
||||||
'django.contrib.messages.context_processors.messages',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
LOGGING={
|
|
||||||
'version': 1,
|
|
||||||
'disable_existing_loggers': False,
|
|
||||||
'handlers': {
|
|
||||||
'console': {
|
|
||||||
'class': 'logging.StreamHandler',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'loggers': {
|
|
||||||
'oidc_provider': {
|
|
||||||
'handlers': ['console'],
|
|
||||||
'level': os.getenv('DJANGO_LOG_LEVEL', 'DEBUG'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
INSTALLED_APPS=[
|
|
||||||
'django.contrib.auth',
|
|
||||||
'django.contrib.contenttypes',
|
|
||||||
'django.contrib.sessions',
|
|
||||||
'django.contrib.sites',
|
|
||||||
'django.contrib.messages',
|
|
||||||
'django.contrib.admin',
|
|
||||||
'oidc_provider',
|
|
||||||
],
|
|
||||||
|
|
||||||
SECRET_KEY='this-should-be-top-secret',
|
|
||||||
|
|
||||||
ROOT_URLCONF='oidc_provider.tests.app.urls',
|
|
||||||
|
|
||||||
TEMPLATE_DIRS=[
|
|
||||||
'oidc_provider/tests/templates',
|
|
||||||
],
|
|
||||||
|
|
||||||
USE_TZ=True,
|
|
||||||
|
|
||||||
# OIDC Provider settings.
|
|
||||||
|
|
||||||
SITE_URL='http://localhost:8000',
|
|
||||||
OIDC_USERINFO='oidc_provider.tests.app.utils.userinfo',
|
|
||||||
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def runtests(*test_args):
|
|
||||||
if not settings.configured:
|
|
||||||
settings.configure(**DEFAULT_SETTINGS)
|
|
||||||
|
|
||||||
django.setup()
|
|
||||||
|
|
||||||
parent = os.path.dirname(os.path.abspath(__file__))
|
|
||||||
sys.path.insert(0, parent)
|
|
||||||
|
|
||||||
try:
|
|
||||||
from django.test.runner import DiscoverRunner
|
|
||||||
runner_class = DiscoverRunner
|
|
||||||
if not test_args:
|
|
||||||
test_args = ["oidc_provider.tests"]
|
|
||||||
except ImportError:
|
|
||||||
from django.test.simple import DjangoTestSuiteRunner
|
|
||||||
runner_class = DjangoTestSuiteRunner
|
|
||||||
if not test_args:
|
|
||||||
test_args = ["tests"]
|
|
||||||
|
|
||||||
failures = runner_class(verbosity=1, interactive=True, failfast=False).run_tests(test_args)
|
|
||||||
sys.exit(failures)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
runtests(*sys.argv[1:])
|
|
2
setup.py
2
setup.py
|
@ -10,7 +10,7 @@ os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir)))
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='django-oidc-provider',
|
name='django-oidc-provider',
|
||||||
version='0.5.2',
|
version='0.5.3',
|
||||||
packages=find_packages(),
|
packages=find_packages(),
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
license='MIT License',
|
license='MIT License',
|
||||||
|
|
48
tox.ini
48
tox.ini
|
@ -1,39 +1,37 @@
|
||||||
[tox]
|
[tox]
|
||||||
|
|
||||||
envlist=
|
envlist=
|
||||||
clean,
|
|
||||||
py27-django{17,18,19,110,111},
|
py27-django{17,18,19,110,111},
|
||||||
py34-django{17,18,19,110,111},
|
py34-django{17,18,19,110,111,20},
|
||||||
py35-django{18,19,110,111},
|
py35-django{18,19,110,111,20},
|
||||||
py36-django{18,19,110,111},
|
py36-django{18,19,110,111,20},
|
||||||
flake8
|
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
|
changedir=
|
||||||
|
oidc_provider/tests/cases
|
||||||
deps =
|
deps =
|
||||||
|
mock
|
||||||
|
psycopg2
|
||||||
|
pytest
|
||||||
|
pytest-django
|
||||||
|
pytest-flake8
|
||||||
|
pytest-cov
|
||||||
django17: django>=1.7,<1.8
|
django17: django>=1.7,<1.8
|
||||||
django18: django>=1.8,<1.9
|
django18: django>=1.8,<1.9
|
||||||
django19: django>=1.9,<1.10
|
django19: django>=1.9,<1.10
|
||||||
django110: django>=1.10,<1.11
|
django110: django>=1.10,<1.11
|
||||||
django111: django>=1.11,<1.12
|
django111: django>=1.11,<1.12
|
||||||
coverage
|
django20: django>=2.0,<2.1
|
||||||
mock
|
|
||||||
|
|
||||||
commands =
|
commands =
|
||||||
coverage run setup.py test
|
pytest --flake8 --cov=oidc_provider {posargs}
|
||||||
|
|
||||||
[testenv:clean]
|
[pytest]
|
||||||
|
DJANGO_SETTINGS_MODULE = oidc_provider.tests.settings
|
||||||
commands=
|
python_files = test_*.py
|
||||||
coverage erase
|
flake8-max-line-length = 99
|
||||||
|
flake8-ignore =
|
||||||
[testenv:stats]
|
.git ALL
|
||||||
|
__pycache__ ALL
|
||||||
commands=
|
.ropeproject ALL
|
||||||
coverage report -m
|
*/migrations ALL
|
||||||
|
manage.py ALL
|
||||||
[testenv:flake8]
|
|
||||||
basepython=python
|
|
||||||
deps=flake8
|
|
||||||
commands =
|
|
||||||
flake8 --max-line-length=120
|
|
||||||
|
|
Loading…
Reference in a new issue