diff --git a/.gitignore b/.gitignore index eae1d26..1ffc2d9 100644 --- a/.gitignore +++ b/.gitignore @@ -276,3 +276,4 @@ dmypy.json staticfiles/ media/ db.sqlite3 +.direnv diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9925cf0..50147a3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,7 +1,10 @@ -exclude: (\.min\.(js|css)(\.map)?$|/vendor/) +exclude: \.min\.(js|css)(\.map)?$|^\.idea/|/vendor/ +ci: + skip: [pip-compile] + repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 + rev: v4.4.0 hooks: - id: check-ast - id: check-json @@ -20,52 +23,57 @@ repos: - id: trailing-whitespace args: - --markdown-linebreak-ext=md - - repo: https://github.com/timothycrosley/isort - rev: 5.10.1 - hooks: - - id: isort - - repo: https://github.com/psf/black - rev: 22.8.0 - hooks: - - id: black - - repo: https://github.com/asottile/pyupgrade - rev: v2.38.0 - hooks: - - id: pyupgrade - args: - - --py310-plus + - id: check-executables-have-shebangs + - id: check-shebang-scripts-are-executable - repo: https://github.com/adamchainz/django-upgrade - rev: 1.10.0 + rev: 1.13.0 hooks: - id: django-upgrade args: [--target-version, "4.1"] + - repo: https://github.com/psf/black + rev: 23.1.0 + hooks: + - id: black + args: [--target-version, py311] - repo: https://github.com/rtts/djhtml - rev: v1.5.2 + rev: 3.0.6 hooks: - id: djhtml - - repo: https://github.com/flakeheaven/flakeheaven - rev: 3.0.0 + - repo: https://github.com/charliermarsh/ruff-pre-commit + rev: v0.0.259 hooks: - - id: flakeheaven - additional_dependencies: - - flake8-annotations-complexity - - flake8-builtins - - flake8-bugbear - - flake8-comprehensions - - flake8-eradicate - - flake8-noqa - - flake8-pytest-style + - id: ruff + args: [--fix] - repo: https://github.com/pre-commit/mirrors-prettier - rev: v3.0.0-alpha.0 + rev: v3.0.0-alpha.6 hooks: - id: prettier types_or: [javascript, css] - repo: https://github.com/pre-commit/mirrors-eslint - rev: v8.24.0 + rev: v8.36.0 hooks: - id: eslint args: [--fix] types_or: [javascript, css] additional_dependencies: - - eslint@^7.29.0 - - eslint-config-prettier@^8.3.0 + - eslint@8.36.0 + - eslint-config-prettier@8.5.0 + - repo: https://github.com/tox-dev/pyproject-fmt + rev: 0.9.2 + hooks: + - id: pyproject-fmt + - repo: https://github.com/jazzband/pip-tools + rev: 6.12.3 + hooks: + - id: pip-compile + name: pip-compile requirements.txt + args: [-q, --allow-unsafe, --resolver=backtracking, requirements.in] + files: ^requirements\.(in|txt)$ + - id: pip-compile + name: pip-compile constraints.txt + args: [-q, --allow-unsafe, --resolver=backtracking, --strip-extras, --output-file=constraints.txt, requirements.in] + files: ^requirements\.in|constraints\.txt$ + - id: pip-compile + name: pip-compile requirements-dev.txt + args: [-q, --allow-unsafe, --resolver=backtracking, requirements-dev.in] + files: ^requirements-dev\.(in|txt)$ diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..2ab8199 --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +python 3.11.2 diff --git a/constraints.txt b/constraints.txt new file mode 100644 index 0000000..e776fea --- /dev/null +++ b/constraints.txt @@ -0,0 +1,90 @@ +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# pip-compile --allow-unsafe --output-file=constraints.txt --resolver=backtracking --strip-extras requirements.in +# +asgiref==3.6.0 + # via django +certifi==2022.12.7 + # via requests +charset-normalizer==3.1.0 + # via requests +contourpy==1.0.7 + # via matplotlib +crispy-bootstrap5==0.7 + # via -r requirements.in +cycler==0.11.0 + # via matplotlib +django==4.1.7 + # via + # -r requirements.in + # crispy-bootstrap5 + # django-anymail + # django-crispy-forms + # django-csp + # django-extensions + # django-htmx + # django-solo +django-anymail==9.1 + # via -r requirements.in +django-cleanup==7.0.0 + # via -r requirements.in +django-crispy-forms==2.0 + # via + # -r requirements.in + # crispy-bootstrap5 +django-csp==3.7 + # via -r requirements.in +django-environ==0.10.0 + # via -r requirements.in +django-extensions==3.2.1 + # via -r requirements.in +django-htmx==1.14.0 + # via -r requirements.in +django-solo==2.0.0 + # via -r requirements.in +fonttools==4.39.2 + # via matplotlib +freezegun==1.2.2 + # via -r requirements.in +gunicorn==20.1.0 + # via -r requirements.in +idna==3.4 + # via requests +kiwisolver==1.4.4 + # via matplotlib +matplotlib==3.7.1 + # via -r requirements.in +numpy==1.24.2 + # via + # contourpy + # matplotlib +packaging==23.0 + # via matplotlib +pillow==9.4.0 + # via + # -r requirements.in + # matplotlib +pyparsing==3.0.9 + # via matplotlib +python-dateutil==2.8.2 + # via + # freezegun + # matplotlib +requests==2.28.2 + # via + # -r requirements.in + # django-anymail +six==1.16.0 + # via python-dateutil +sqlparse==0.4.3 + # via django +urllib3==1.26.15 + # via requests +whitenoise==6.4.0 + # via -r requirements.in + +# The following packages are considered to be unsafe in a requirements file: +setuptools==67.6.0 + # via gunicorn diff --git a/pyproject.toml b/pyproject.toml index 1600fb0..bc6d103 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,48 +1,6 @@ -[tool.poetry] -name = "checkout" -version = "0.1.0" -description = "" -authors = ["Gabriel Augendre "] - -[tool.poetry.dependencies] -python = "^3.10" -django = "^4.0" -django-anymail = {version = "^8.4", extras = ["mailgun"]} -django-cleanup = "^6.0" -whitenoise = {extras = ["brotli"], version = "^6.0"} -django-csp = "^3.7" -django-environ = "^0.9" -requests = "^2.27.1" -django-extensions = "^3.1.5" -bpython = "^0.23" -gunicorn = "^20.1.0" -Pillow = "^9.1.0" -django-crispy-forms = "^1.14.0" -crispy-bootstrap5 = "^0.6" -matplotlib = "^3.5.1" -freezegun = "^1.2.1" -django-htmx = "^1.12.2" -django-solo = "^2.0.0" - -[tool.poetry.dev-dependencies] -pre-commit = "^2.7" -pytest = "^6.0" -pytest-django = "^4.5" -model-bakery = "^1.1" -pytest-cov = "^3.0" -poetry-deps-scanner = "^2.0" -invoke = "^1.7.0" -factory-boy = "^3.2.1" -pytest-selenium = "^4.0.0" -selenium = "^4.4.3" -kolo = "^2.0.3" - -[tool.black] -target-version = ['py310'] - -[tool.isort] -profile = "black" - +############################################################################### +# pytest +############################################################################### [tool.pytest.ini_options] addopts = "--color=yes --driver Firefox" minversion = "6.0" @@ -51,28 +9,46 @@ testpaths = [ "src", ] -[tool.flakeheaven] -max_complexity = 10 -format = "grouped" +############################################################################### +# ruff +############################################################################### -[tool.flakeheaven.plugins] -"*" = [ - "+*", - # long lines - "-E501", - # conflict with black on PEP8 interpretation - "-E203", - # deprecated rule: https://www.flake8rules.com/rules/W503.html - "-W503", +[tool.ruff] +src = ["src"] +target-version = "py311" +select = ["ALL"] +unfixable = ["T20", "RUF001", "RUF002", "RUF003"] + +ignore = [ + "ANN", # flake8-annotations + "BLE", # flake8-blind-except + "TCH", # flake8-type-checking / TODO: revisit later ? + + "E501", # long lines + "D1", # missing docstring + "TRY003", # Avoid specifying long messages outside the exception class ] -flake8-quotes = ["+*", "-Q000"] # found double quotes, conflict with black -flake8-commas = ["+*", "-C812"] # missing trailing comma, conflict with black -flake8-docstrings = ["+*", "-D1??"] # missing docstring -flake8-rst-docstrings = ["-*"] -[tool.flakeheaven.exceptions."**/tests/*"] -flake8-bandit = ["+*", "-S101"] # Use of assert detected. +[tool.ruff.per-file-ignores] +"**/tests/*" = [ + "S101", # Use of assert detected. + "S105", # Possible hardcoded password. + "B011", # Do not call assert False since python -O removes these calls. + "ARG001", # Unused function argument (mostly fixtures) + "PLR2004", # Magic value used in comparison, consider replacing {value} with a constant variable + "S311", # Standard pseudo-random generators are not suitable for cryptographic purposes. +] +# File {name} is part of an implicit namespace package. Add an `__init__.py`. +"tasks.py" = ["INP001"] +"src/conftest.py" = ["INP001"] +"src/manage.py" = ["INP001"] -[build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" +"src/purchase/management/commands/generate_dummy_baskets.py" = [ + "S311", # Standard pseudo-random generators are not suitable for cryptographic purposes. +] + +[tool.ruff.pydocstyle] +convention = "pep257" + +[tool.ruff.mccabe] +max-complexity = 10 diff --git a/requirements-dev.in b/requirements-dev.in new file mode 100644 index 0000000..f92d149 --- /dev/null +++ b/requirements-dev.in @@ -0,0 +1,14 @@ +-c constraints.txt +pre-commit>=2.7 +pytest>=6.0 +pytest-cov>=3.0.0 +pytest-django>=4.5.0 +pytest-selenium>=4.0.0 +pre-commit>=2.7 +model-bakery>=1.1 +invoke>=2.0.0 +factory-boy>=3.2.1 +selenium>=4.4.3 +black>=22.12.0 +pip-tools>=6.0 +ruff>=0.0.237 diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..aefc00f --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,183 @@ +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# pip-compile --allow-unsafe --resolver=backtracking requirements-dev.in +# +asgiref==3.6.0 + # via + # -c constraints.txt + # django +async-generator==1.10 + # via trio +attrs==22.2.0 + # via + # outcome + # pytest + # trio +black==23.1.0 + # via -r requirements-dev.in +build==0.10.0 + # via pip-tools +certifi==2022.12.7 + # via + # -c constraints.txt + # requests + # selenium +cfgv==3.3.1 + # via pre-commit +charset-normalizer==3.1.0 + # via + # -c constraints.txt + # requests +click==8.1.3 + # via + # black + # pip-tools +coverage[toml]==7.2.2 + # via pytest-cov +distlib==0.3.6 + # via virtualenv +django==4.1.7 + # via + # -c constraints.txt + # model-bakery +exceptiongroup==1.1.1 + # via trio-websocket +factory-boy==3.2.1 + # via -r requirements-dev.in +faker==18.3.1 + # via factory-boy +filelock==3.10.4 + # via virtualenv +h11==0.14.0 + # via wsproto +identify==2.5.22 + # via pre-commit +idna==3.4 + # via + # -c constraints.txt + # requests + # trio +iniconfig==2.0.0 + # via pytest +invoke==2.0.0 + # via -r requirements-dev.in +model-bakery==1.10.1 + # via -r requirements-dev.in +mypy-extensions==1.0.0 + # via black +nodeenv==1.7.0 + # via pre-commit +outcome==1.2.0 + # via trio +packaging==23.0 + # via + # -c constraints.txt + # black + # build + # pytest +pathspec==0.11.1 + # via black +pip-tools==6.12.3 + # via -r requirements-dev.in +platformdirs==3.1.1 + # via + # black + # virtualenv +pluggy==1.0.0 + # via pytest +pre-commit==3.2.1 + # via -r requirements-dev.in +py==1.11.0 + # via + # pytest + # pytest-html +pyproject-hooks==1.0.0 + # via build +pysocks==1.7.1 + # via urllib3 +pytest==6.2.5 + # via + # -r requirements-dev.in + # pytest-base-url + # pytest-cov + # pytest-django + # pytest-html + # pytest-metadata + # pytest-selenium + # pytest-variables +pytest-base-url==2.0.0 + # via pytest-selenium +pytest-cov==4.0.0 + # via -r requirements-dev.in +pytest-django==4.5.2 + # via -r requirements-dev.in +pytest-html==3.2.0 + # via pytest-selenium +pytest-metadata==2.0.4 + # via pytest-html +pytest-selenium==4.0.0 + # via -r requirements-dev.in +pytest-variables==2.0.0 + # via pytest-selenium +python-dateutil==2.8.2 + # via + # -c constraints.txt + # faker +pyyaml==6.0 + # via pre-commit +requests==2.28.2 + # via + # -c constraints.txt + # pytest-base-url + # pytest-selenium +ruff==0.0.259 + # via -r requirements-dev.in +selenium==4.8.3 + # via + # -r requirements-dev.in + # pytest-selenium +six==1.16.0 + # via + # -c constraints.txt + # python-dateutil + # tenacity +sniffio==1.3.0 + # via trio +sortedcontainers==2.4.0 + # via trio +sqlparse==0.4.3 + # via + # -c constraints.txt + # django +tenacity==6.3.1 + # via pytest-selenium +toml==0.10.2 + # via pytest +trio==0.22.0 + # via + # selenium + # trio-websocket +trio-websocket==0.10.2 + # via selenium +urllib3[socks]==1.26.15 + # via + # -c constraints.txt + # requests + # selenium +virtualenv==20.21.0 + # via pre-commit +wheel==0.40.0 + # via pip-tools +wsproto==1.2.0 + # via trio-websocket + +# The following packages are considered to be unsafe in a requirements file: +pip==23.0.1 + # via pip-tools +setuptools==67.6.0 + # via + # -c constraints.txt + # nodeenv + # pip-tools diff --git a/requirements.in b/requirements.in new file mode 100644 index 0000000..675b095 --- /dev/null +++ b/requirements.in @@ -0,0 +1,16 @@ +django>=4.1,<5.0 +django-anymail[mailgun]>=8.6 +django-cleanup>=6.0 +whitenoise>=6.2 +django-csp>=3.7 +django-environ>=0.9.0 +requests>=2.28.1 +django-extensions>=3.1.5 +gunicorn>=20.1.0 +Pillow>=9.3.0 +django-crispy-forms>=1.14.0 +crispy-bootstrap5>=0.6 +matplotlib>=3.5.1 +freezegun>=1.2.1 +django-htmx>=1.12.2 +django-solo>=2.0.0 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..bcbbaea --- /dev/null +++ b/requirements.txt @@ -0,0 +1,90 @@ +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# pip-compile --allow-unsafe --resolver=backtracking requirements.in +# +asgiref==3.6.0 + # via django +certifi==2022.12.7 + # via requests +charset-normalizer==3.1.0 + # via requests +contourpy==1.0.7 + # via matplotlib +crispy-bootstrap5==0.7 + # via -r requirements.in +cycler==0.11.0 + # via matplotlib +django==4.1.7 + # via + # -r requirements.in + # crispy-bootstrap5 + # django-anymail + # django-crispy-forms + # django-csp + # django-extensions + # django-htmx + # django-solo +django-anymail[mailgun]==9.1 + # via -r requirements.in +django-cleanup==7.0.0 + # via -r requirements.in +django-crispy-forms==2.0 + # via + # -r requirements.in + # crispy-bootstrap5 +django-csp==3.7 + # via -r requirements.in +django-environ==0.10.0 + # via -r requirements.in +django-extensions==3.2.1 + # via -r requirements.in +django-htmx==1.14.0 + # via -r requirements.in +django-solo==2.0.0 + # via -r requirements.in +fonttools==4.39.2 + # via matplotlib +freezegun==1.2.2 + # via -r requirements.in +gunicorn==20.1.0 + # via -r requirements.in +idna==3.4 + # via requests +kiwisolver==1.4.4 + # via matplotlib +matplotlib==3.7.1 + # via -r requirements.in +numpy==1.24.2 + # via + # contourpy + # matplotlib +packaging==23.0 + # via matplotlib +pillow==9.4.0 + # via + # -r requirements.in + # matplotlib +pyparsing==3.0.9 + # via matplotlib +python-dateutil==2.8.2 + # via + # freezegun + # matplotlib +requests==2.28.2 + # via + # -r requirements.in + # django-anymail +six==1.16.0 + # via python-dateutil +sqlparse==0.4.3 + # via django +urllib3==1.26.15 + # via requests +whitenoise==6.4.0 + # via -r requirements.in + +# The following packages are considered to be unsafe in a requirements file: +setuptools==67.6.0 + # via gunicorn diff --git a/src/checkout/settings.py b/src/checkout/settings.py index a8dab1a..f76eabd 100644 --- a/src/checkout/settings.py +++ b/src/checkout/settings.py @@ -110,7 +110,7 @@ MIDDLEWARE = [ try: import kolo # noqa: F401 - MIDDLEWARE = ["kolo.middleware.KoloMiddleware"] + MIDDLEWARE + MIDDLEWARE = ["kolo.middleware.KoloMiddleware", *MIDDLEWARE] except ImportError: # Don't add kolo if unavailable pass @@ -139,14 +139,14 @@ CACHES = { "default": { "BACKEND": "django.core.cache.backends.db.DatabaseCache", "LOCATION": "cache", - } + }, } # Database # https://docs.djangoproject.com/en/3.1/ref/settings/#databases DB_BASE_DIR = env("DB_BASE_DIR") -if not DB_BASE_DIR: +if not DB_BASE_DIR: # noqa: SIM108 # Protect against empty strings DB_BASE_DIR = BASE_DIR else: @@ -156,7 +156,7 @@ DATABASES = { "default": { "ENGINE": "django.db.backends.sqlite3", "NAME": DB_BASE_DIR / "db.sqlite3", - } + }, } INTERNAL_IPS = [ @@ -169,7 +169,7 @@ INTERNAL_IPS = [ AUTH_PASSWORD_VALIDATORS = [ { - "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator" + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", }, {"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"}, {"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"}, diff --git a/src/checkout/urls.py b/src/checkout/urls.py index 7cfca63..ff117d3 100644 --- a/src/checkout/urls.py +++ b/src/checkout/urls.py @@ -1,4 +1,4 @@ -"""checkout URL Configuration +"""checkout URL Configuration. The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/3.1/topics/http/urls/ @@ -24,7 +24,8 @@ urlpatterns = [ path( "robots.txt", TemplateView.as_view( - template_name="common/robots.txt", content_type="text/plain" + template_name="common/robots.txt", + content_type="text/plain", ), ), path("", include("common.urls")), diff --git a/src/common/migrations/0001_initial.py b/src/common/migrations/0001_initial.py index 2fa909a..2caceb7 100644 --- a/src/common/migrations/0001_initial.py +++ b/src/common/migrations/0001_initial.py @@ -7,7 +7,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - initial = True dependencies = [ @@ -31,7 +30,9 @@ class Migration(migrations.Migration): ( "last_login", models.DateTimeField( - blank=True, null=True, verbose_name="last login" + blank=True, + null=True, + verbose_name="last login", ), ), ( @@ -46,13 +47,13 @@ class Migration(migrations.Migration): "username", models.CharField( error_messages={ - "unique": "A user with that username already exists." + "unique": "A user with that username already exists.", }, help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", max_length=150, unique=True, validators=[ - django.contrib.auth.validators.UnicodeUsernameValidator() + django.contrib.auth.validators.UnicodeUsernameValidator(), ], verbose_name="username", ), @@ -60,19 +61,25 @@ class Migration(migrations.Migration): ( "first_name", models.CharField( - blank=True, max_length=150, verbose_name="first name" + blank=True, + max_length=150, + verbose_name="first name", ), ), ( "last_name", models.CharField( - blank=True, max_length=150, verbose_name="last name" + blank=True, + max_length=150, + verbose_name="last name", ), ), ( "email", models.EmailField( - blank=True, max_length=254, verbose_name="email address" + blank=True, + max_length=254, + verbose_name="email address", ), ), ( @@ -94,7 +101,8 @@ class Migration(migrations.Migration): ( "date_joined", models.DateTimeField( - default=django.utils.timezone.now, verbose_name="date joined" + default=django.utils.timezone.now, + verbose_name="date joined", ), ), ( diff --git a/src/common/templates/common/base.html b/src/common/templates/common/base.html index dacc53b..ed7f320 100644 --- a/src/common/templates/common/base.html +++ b/src/common/templates/common/base.html @@ -6,7 +6,7 @@ Checkout + rel="stylesheet">