Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move template linting and formatting to ruff #5613

Open
wants to merge 34 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ea6ff79
Move template linting and formatting to ruff
browniebroke Dec 31, 2024
122070c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 31, 2024
7542965
Remove comments as they're wrongly placed
browniebroke Dec 31, 2024
f1b173a
Tweak multi-line comment
browniebroke Dec 31, 2024
23af0dc
Remove a couple of commented out Ruff rules
browniebroke Dec 31, 2024
97a74b9
Fix extend-exclude in Ruff config
browniebroke Dec 31, 2024
33a1cd7
Adjust Ruff line length
browniebroke Dec 31, 2024
3ccfc21
Run Ruff pre-commit hook
browniebroke Dec 31, 2024
9297598
Run Ruff with --unsafe-fixes
browniebroke Dec 31, 2024
7d1e843
Run Ruff with --add-noqa
browniebroke Dec 31, 2024
448fb81
Run Ruff formatter
browniebroke Dec 31, 2024
304acae
Drop Python 2 in pre/post generation hooks
browniebroke Dec 31, 2024
972a072
Restore print statements in pre/post-generation hooks
browniebroke Dec 31, 2024
e5acc9f
Restore print statements in scripts
browniebroke Dec 31, 2024
2a6be9e
Indent toml with 2 spaces
browniebroke Dec 31, 2024
2747e02
Exclude docs and revert most changes from Ruff
browniebroke Dec 31, 2024
1e9bed3
Merge branch 'master' into lint-format-template-with-ruff
browniebroke Jan 9, 2025
5489c66
Merge branch 'master' into lint-format-template-with-ruff
browniebroke Jan 11, 2025
9275c8f
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 11, 2025
ea15146
Fix Ruff issue
browniebroke Jan 11, 2025
b02f9b4
Merge branch 'master' into lint-format-template-with-ruff
browniebroke Jan 16, 2025
b72e5b2
Disable PLR0133 in pre/post commit hooks
browniebroke Jan 17, 2025
4b67c72
Migrate post-generation hook to pathlib
browniebroke Jan 17, 2025
7b2be01
Migrate post-generation hook to pathlib
browniebroke Jan 18, 2025
b31fa10
Fix typo in folder name
browniebroke Jan 18, 2025
496da71
Migrate test generation to pathlib
browniebroke Jan 18, 2025
3ddf863
Merge branch 'migrate-post-gen-hook-pathlib' into lint-format-templat…
browniebroke Jan 18, 2025
73324e5
Merge branch 'migrate-tests-to-pathlib' into lint-format-template-wit…
browniebroke Jan 18, 2025
ed25095
Merge branch 'master' into migrate-post-gen-hook-pathlib
browniebroke Jan 20, 2025
9902f4d
Merge branch 'migrate-post-gen-hook-pathlib' into lint-format-templat…
browniebroke Jan 20, 2025
fff7144
Fix typo in folder name
browniebroke Jan 20, 2025
fff4183
Format comment better
browniebroke Jan 20, 2025
671b7b9
Merge branch 'master' into lint-format-template-with-ruff
browniebroke Jan 20, 2025
2159cf7
Update pyproject.toml
browniebroke Jan 21, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .flake8

This file was deleted.

25 changes: 5 additions & 20 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,27 +26,12 @@ repos:
- id: prettier
args: ["--tab-width", "2"]

- repo: https://github.com/asottile/pyupgrade
rev: v3.19.1
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.4
hooks:
- id: pyupgrade
args: [--py312-plus]
exclude: hooks/

- repo: https://github.com/psf/black
rev: 24.10.0
hooks:
- id: black

- repo: https://github.com/PyCQA/isort
rev: 5.13.2
hooks:
- id: isort

- repo: https://github.com/PyCQA/flake8
rev: 7.1.1
hooks:
- id: flake8
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
- id: ruff-format

- repo: https://github.com/tox-dev/pyproject-fmt
rev: "v2.5.0"
Expand Down
6 changes: 3 additions & 3 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@
"cookiecutter-django Documentation",
"cookiecutter-django",
"manual",
)
),
]

# The name of an image file (relative to this directory) to place at the top of
Expand Down Expand Up @@ -223,7 +223,7 @@
"Cookiecutter Django documentation",
["Daniel Roy Greenfeld"],
1,
)
),
]

# If true, show URL addresses after external links.
Expand All @@ -242,7 +242,7 @@
"Cookiecutter Django documentation",
"Daniel Roy Greenfeld",
"Cookiecutter Django",
"A Cookiecutter template for creating production-ready " "Django projects quickly.",
"A Cookiecutter template for creating production-ready Django projects quickly.",
"Miscellaneous",
)
]
Expand Down
Empty file added hooks/__init__.py
Empty file.
37 changes: 16 additions & 21 deletions hooks/post_gen_project.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# ruff: noqa: PLR0133
import json
import random
import shutil
Expand Down Expand Up @@ -79,7 +80,7 @@ def remove_heroku_files():
file_names = ["Procfile", "runtime.txt", "requirements.txt"]
for file_name in file_names:
if file_name == "requirements.txt" and "{{ cookiecutter.ci_tool }}".lower() == "travis":
# don't remove the file if we are using travisci but not using heroku
# Don't remove the file if we are using Travis CI but not using Heroku
continue
Path(file_name).unlink()
shutil.rmtree("bin")
Expand Down Expand Up @@ -179,7 +180,7 @@ def handle_js_runner(choice, use_docker, use_async):
"dev": "concurrently npm:dev:*",
"dev:webpack": "webpack serve --config webpack/dev.config.js",
"dev:django": dev_django_cmd,
}
},
)
else:
remove_dev_deps.append("concurrently")
Expand Down Expand Up @@ -239,7 +240,7 @@ def remove_dotdrone_file():
Path(".drone.yml").unlink()


def generate_random_string(length, using_digits=False, using_ascii_letters=False, using_punctuation=False):
def generate_random_string(length, using_digits=False, using_ascii_letters=False, using_punctuation=False): # noqa: FBT002
"""
Example:
opting out for 50 symbol-long, [a-z][A-Z][0-9] string
Expand Down Expand Up @@ -268,7 +269,7 @@ def set_flag(file_path: Path, flag, value=None, formatted=None, *args, **kwargs)
if random_string is None:
print(
"We couldn't find a secure pseudo-random number generator on your "
"system. Please, make sure to manually {} later.".format(flag)
f"system. Please, make sure to manually {flag} later.",
)
random_string = flag
if formatted is not None:
Expand All @@ -285,68 +286,62 @@ def set_flag(file_path: Path, flag, value=None, formatted=None, *args, **kwargs)


def set_django_secret_key(file_path: Path):
django_secret_key = set_flag(
return set_flag(
file_path,
"!!!SET DJANGO_SECRET_KEY!!!",
length=64,
using_digits=True,
using_ascii_letters=True,
)
return django_secret_key


def set_django_admin_url(file_path: Path):
django_admin_url = set_flag(
return set_flag(
file_path,
"!!!SET DJANGO_ADMIN_URL!!!",
formatted="{}/",
length=32,
using_digits=True,
using_ascii_letters=True,
)
return django_admin_url


def generate_random_user():
return generate_random_string(length=32, using_ascii_letters=True)


def generate_postgres_user(debug=False):
def generate_postgres_user(debug=False): # noqa: FBT002
return DEBUG_VALUE if debug else generate_random_user()


def set_postgres_user(file_path, value):
postgres_user = set_flag(file_path, "!!!SET POSTGRES_USER!!!", value=value)
return postgres_user
return set_flag(file_path, "!!!SET POSTGRES_USER!!!", value=value)


def set_postgres_password(file_path, value=None):
postgres_password = set_flag(
return set_flag(
file_path,
"!!!SET POSTGRES_PASSWORD!!!",
value=value,
length=64,
using_digits=True,
using_ascii_letters=True,
)
return postgres_password


def set_celery_flower_user(file_path, value):
celery_flower_user = set_flag(file_path, "!!!SET CELERY_FLOWER_USER!!!", value=value)
return celery_flower_user
return set_flag(file_path, "!!!SET CELERY_FLOWER_USER!!!", value=value)


def set_celery_flower_password(file_path, value=None):
celery_flower_password = set_flag(
return set_flag(
file_path,
"!!!SET CELERY_FLOWER_PASSWORD!!!",
value=value,
length=64,
using_digits=True,
using_ascii_letters=True,
)
return celery_flower_password


def append_to_gitignore_file(ignored_line):
Expand All @@ -355,7 +350,7 @@ def append_to_gitignore_file(ignored_line):
gitignore_file.write("\n")


def set_flags_in_envs(postgres_user, celery_flower_user, debug=False):
def set_flags_in_envs(postgres_user, celery_flower_user, debug=False): # noqa: FBT002
local_django_envs_path = Path(".envs", ".local", ".django")
production_django_envs_path = Path(".envs", ".production", ".django")
local_postgres_envs_path = Path(".envs", ".local", ".postgres")
Expand Down Expand Up @@ -405,7 +400,7 @@ def remove_drf_starter_files():
shutil.rmtree(Path("{{cookiecutter.project_slug}}", "users", "tests", "api"))


def main():
def main(): # noqa: C901, PLR0912, PLR0915
debug = "{{ cookiecutter.debug }}".lower() == "y"

set_flags_in_envs(
Expand Down Expand Up @@ -444,7 +439,7 @@ def main():
print(
INFO + ".env(s) are only utilized when Docker Compose and/or "
"Heroku support is enabled so keeping them does not make sense "
"given your current setup." + TERMINATOR
"given your current setup." + TERMINATOR,
)
remove_envs_and_associated_files()
else:
Expand All @@ -471,7 +466,7 @@ def main():
if "{{ cookiecutter.cloud_provider }}" == "None" and "{{ cookiecutter.use_docker }}".lower() == "n":
print(
WARNING + "You chose to not use any cloud providers nor Docker, "
"media files won't be served in production." + TERMINATOR
"media files won't be served in production." + TERMINATOR,
)

if "{{ cookiecutter.use_celery }}".lower() == "n":
Expand Down
5 changes: 3 additions & 2 deletions hooks/pre_gen_project.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# ruff: noqa: PLR0133
import sys

TERMINATOR = "\x1b[0m"
Expand All @@ -16,9 +17,9 @@

project_slug = "{{ cookiecutter.project_slug }}"
if hasattr(project_slug, "isidentifier"):
assert project_slug.isidentifier(), "'{}' project slug is not a valid Python identifier.".format(project_slug)
assert project_slug.isidentifier(), f"'{project_slug}' project slug is not a valid Python identifier."

assert project_slug == project_slug.lower(), "'{}' project slug should be all lowercase".format(project_slug)
assert project_slug == project_slug.lower(), f"'{project_slug}' project slug should be all lowercase"

assert "\\" not in "{{ cookiecutter.author_name }}", "Don't include backslashes in author name."

Expand Down
84 changes: 72 additions & 12 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,22 +58,82 @@ docs = [
"sphinx-rtd-theme>=3",
]

[tool.black]
[tool.ruff]
target-version = "py39"
line-length = 119
target-version = [
'py312',
# Exclude the template content as most files aren't parseable
extend-exclude = [
"[{]{2}cookiecutter.project_slug[}]{2}/*",
"docs/*",
]

# ==== isort ====

[tool.isort]
profile = "black"
line_length = 119
known_first_party = [
"tests",
"scripts",
"hooks",
lint.select = [
"A",
# "ANN", # flake8-annotations: we should support this in the future but many errors atm
"ASYNC",
"B",
"BLE",
"C4",
"C90",
"COM",
"DTZ",
"E",
"EM",
"ERA",
"EXE",
"F",
"FA",
"FBT",
"FLY",
"G",
"I",
"ICN",
"INP",
"INT",
"ISC",
"N",
"PD",
"PERF",
"PGH",
"PIE",
"PL",
"PT",
# "ARG", # Unused function argument
"PTH",
"PYI",
"Q",
"RET",
"RSE",
# "FURB",
# "LOG",
"RUF",
"S",
"SIM",
"SLF",
"SLOT",
"T10",
"TC",
"TID",
"TRY",
"UP",
"W",
"YTT",
]
lint.ignore = [
"EM101",
"RUF012", # Mutable class attributes should be annotated with `typing.ClassVar`
"S101", # Use of assert detected https://docs.astral.sh/ruff/rules/assert/
"SIM102", # sometimes it's better to nest
# Checks for uses of isinstance/issubclass that take a tuple of types for comparison.
# Deactivated because it can make the code slow: https://github.com/astral-sh/ruff/issues/7871
"UP038",
]
# The fixes in extend-unsafe-fixes will require
# provide the `--unsafe-fixes` flag when fixing.
lint.extend-unsafe-fixes = [
"UP038",
]
lint.isort.force-single-line = true

[tool.pyproject-fmt]
keep_full_version = true
Expand Down
21 changes: 12 additions & 9 deletions scripts/create_django_issue.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,17 @@
import os
import re
import sys
from collections.abc import Iterable
from pathlib import Path
from typing import TYPE_CHECKING, Any, NamedTuple
from typing import TYPE_CHECKING
from typing import Any
from typing import NamedTuple

import requests
from github import Github

if TYPE_CHECKING:
from collections.abc import Iterable

from github.Issue import Issue

CURRENT_FILE = Path(__file__)
Expand Down Expand Up @@ -58,7 +61,7 @@ def parse_to_tuple(cls, version_str: str):
def get_package_info(package: str) -> dict:
"""Get package metadata using PyPI API."""
# "django" converts to "Django" on redirect
r = requests.get(f"https://pypi.org/pypi/{package}/json", allow_redirects=True)
r = requests.get(f"https://pypi.org/pypi/{package}/json", allow_redirects=True) # noqa: S113
if not r.ok:
print(f"Couldn't find package: {package}")
sys.exit(1)
Expand All @@ -83,7 +86,7 @@ def get_name_and_version(requirements_line: str) -> tuple[str, ...]:


def get_all_latest_django_versions(
django_max_version: tuple[DjVersion] = None,
django_max_version: tuple[DjVersion] | None = None,
) -> tuple[DjVersion, list[DjVersion]]:
"""
Grabs all Django versions that are worthy of a GitHub issue.
Expand Down Expand Up @@ -213,16 +216,15 @@ def get_compatibility(self, package_name: str, package_info: dict, needed_dj_ver
for classifier in package_info["info"]["classifiers"]:
# Usually in the form of "Framework :: Django :: 3.2"
tokens = classifier.split(" ")
if len(tokens) >= 5 and tokens[2].lower() == "django" and "." in tokens[4]:
if len(tokens) >= 5 and tokens[2].lower() == "django" and "." in tokens[4]: # noqa: PLR2004
version = DjVersion.parse(tokens[4])
if len(version) == 2:
if len(version) == 2: # noqa: PLR2004
supported_dj_versions.append(version)

if supported_dj_versions:
if any(v >= needed_dj_version for v in supported_dj_versions):
return package_info["info"]["version"], "✅"
else:
return "", "❌"
return "", "❌"

# Django classifier DNE; assume it isn't a Django lib
# Great exceptions include pylint-django, where we need to do this manually...
Expand Down Expand Up @@ -299,7 +301,8 @@ def main(django_max_version=None) -> None:

if __name__ == "__main__":
if GITHUB_REPO is None:
raise RuntimeError("No github repo, please set the environment variable GITHUB_REPOSITORY")
msg = "No github repo, please set the environment variable GITHUB_REPOSITORY"
raise RuntimeError(msg)
max_version = None
last_arg = sys.argv[-1]
if CURRENT_FILE.name not in last_arg:
Expand Down
Loading
Loading