diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index c871480..5532484 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -24,6 +24,8 @@ jobs:
run: |
pip install pip-tools
pip-sync requirements.txt requirements-dev.txt
+ - name: Ruff
+ run: ruff --format=github .
- name: Test
run: pytest --cov=. --cov-branch --cov-report term-missing:skip-covered
working-directory: ./src/
diff --git a/.idea/watcherTasks.xml b/.idea/watcherTasks.xml
index 98b3257..e78ac61 100644
--- a/.idea/watcherTasks.xml
+++ b/.idea/watcherTasks.xml
@@ -21,7 +21,7 @@
-
+
@@ -41,5 +41,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 69b3773..30792dd 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -33,11 +33,6 @@ repos:
hooks:
- id: django-upgrade
args: [--target-version, "4.1"]
- - repo: https://github.com/PyCQA/isort
- rev: 5.11.4
- hooks:
- - id: isort
- args: [--profile, black]
- repo: https://github.com/psf/black
rev: 22.12.0
hooks:
@@ -47,20 +42,11 @@ repos:
rev: v1.5.2
hooks:
- id: djhtml
- - repo: https://github.com/flakeheaven/flakeheaven
- rev: 3.2.1
+ - repo: https://github.com/charliermarsh/ruff-pre-commit
+ rev: 'v0.0.237'
hooks:
- - id: flakeheaven
- additional_dependencies:
- - flake8-annotations-complexity
- - flake8-bandit
- - flake8-builtins
- - flake8-bugbear
- - flake8-comprehensions
- - flake8-docstrings
- - flake8-eradicate
- - flake8-noqa
- - pep8-naming
+ - id: ruff
+ args: [--fix]
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.0.0-alpha.4
hooks:
diff --git a/pyproject.toml b/pyproject.toml
index c274afb..0bbc00d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -30,44 +30,81 @@ python_files = [
]
###############################################################################
-# flake8 / flakeheaven
+# ruff
###############################################################################
-[tool.flakeheaven]
-max_complexity = 10
-format = "grouped"
+[tool.ruff]
+src = ["src"]
+target-version = "py311"
+select = [
+ "F", # pyflakes
+ "E", "W", # pycodestyle
+ "C90", # mccabe
+ "I", # isort
+ "N", # pep8-naming
+ "D", # pydocstyle
+ "S", # flake8-bandit
+ "FBT", # flake8-boolean-trap
+ "B", # flake8-bugbear
+ "A", # flake8-builtins
+ "C4", # flake8-comprehensions
+ "DTZ", # flake8-datetimez
+ "T10", # flake8-debugger
+ "EXE", # flake8-executable
+ "ISC", # flake8-implicit-str-concat
+ "ICN", # flake8-import-conventions
+ "G", # flake8-logging-format
+ "INP", # flake8-no-pep420
+ "PIE", # flake8-pie
+ "T20", # flake8-print
+ "PT", # flake8-pytest-style
+ "RET", # flake8-return
+ "SIM", # flake8-simplify
+ "TID", # flake8-tidy-imports
+ "ARG", # flake8-unused-arguments
+ "PTH", # flake8-use-pathlib
+ "ERA", # eradicate
+ "PD", # pandas-vet
+ "PGH", # pygrep-hooks
+ "PL", # pylint
+ "TRY", # tryceratops
+ "RUF", # ruff-specific rules
+]
+unfixable = ["T20", "RUF001", "RUF002", "RUF003"]
-# Base rules
-#############################
-[tool.flakeheaven.plugins]
-"*" = [
- "+*",
- "-E501", # long lines
- "-E203", # conflict with black on PEP8 interpretation
- "-W503", # deprecated rule: https://www.flake8rules.com/rules/W503.html
-]
-flake8-builtins = [
- "+*",
- "-A003", # class attribute is shadowing a python builtin
-]
-flake8-docstrings = [
- "+*",
- "-D1??", # missing docstring
-]
-flake8-bandit = [
- "+*",
- "-S308", # Use of mark_safe() may expose cross-site scripting vulnerabilities and should be reviewed.
- "-S703", # Potential XSS on mark_safe function.
+ignore = [
+ "UP", # pyupgrade
+ "YTT", # flake8-2020
+ "ANN", # flake8-annotations
+ "BLE", # flake8-blind-except
+ "COM", # flake8-commas
+ "EM", # flake8-errmsg
+ "Q", # flake8-quotes
+ "TCH", # flake8-type-checking / TODO: revisit later ?
+
+ "E501", # long lines
+ "D1", # missing docstring
+ "TRY003", # Avoid specifying long messages outside the exception class
]
-# Exceptions
-#############################
-[tool.flakeheaven.exceptions."**/tests/*"]
-flake8-bandit = [
- "+*",
- "-S101", # Use of assert detected.
- "-S106", # Possible hardcoded password.
- "-S311", # Standard pseudo-random generators are not suitable for security/cryptographic purposes.
+[tool.ruff.per-file-ignores]
+"**/tests/*" = [
+ "S101", # Use of assert detected.
+ "S106", # 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
]
-flake8-bugbear = [
- "-B011", # Do not call assert False since python -O removes these calls.
+# File {name} is part of an implicit namespace package. Add an `__init__.py`.
+"tasks.py" = ["INP001"]
+"src/conftest.py" = ["INP001"]
+"src/manage.py" = ["INP001"]
+
+"src/character/management/commands/*" = [
+ "RUF001", # String contains ambiguous unicode character
]
+
+[tool.ruff.pydocstyle]
+convention = "pep257"
+
+[tool.ruff.mccabe]
+max-complexity = 10
diff --git a/requirements-dev.in b/requirements-dev.in
index a8fe4d1..1330a39 100644
--- a/requirements-dev.in
+++ b/requirements-dev.in
@@ -14,3 +14,4 @@ hypothesis>=6.56.4
django-browser-reload>=1.6.0
black>=22.12.0
pip-tools>=6.0
+ruff>=0.0.237
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 048b16e..ed372e9 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -547,6 +547,24 @@ requests==2.28.2 \
# bpython
# pytest-base-url
# pytest-selenium
+ruff==0.0.237 \
+ --hash=sha256:0cc6cb7c1efcc260df5a939435649610a28f9f438b8b313384c8985ac6574f9f \
+ --hash=sha256:0d122433a21ce4a21fbba34b73fc3add0ccddd1643b3ff5abb8d2767952f872e \
+ --hash=sha256:2ea04d826ffca58a7ae926115a801960c757d53c9027f2ca9acbe84c9f2b2f04 \
+ --hash=sha256:3d6ed86d0d4d742360a262d52191581f12b669a68e59ae3b52e80d7483b3d7b3 \
+ --hash=sha256:46c5977b643aaf2b6f84641265f835b6c7f67fcca38dbae08c4f15602e084ca0 \
+ --hash=sha256:525e5ec81cee29b993f77976026a6bf44528a14aa6edb1ef47bd8079147395ae \
+ --hash=sha256:630c575f543733adf6c19a11d9a02ca9ecc364bd7140af8a4c854d4728be6b56 \
+ --hash=sha256:7eef0c7a1e45a4e30328ae101613575944cbf47a3a11494bf9827722da6c66b3 \
+ --hash=sha256:80ce10718abbf502818c0d650ebab99fdcef5e937a1ded3884493ddff804373c \
+ --hash=sha256:8d6a1d21ae15da2b1dcffeee2606e90de0e6717e72957da7d16ab6ae18dd0058 \
+ --hash=sha256:8ed113937fab9f73f8c1a6c0350bb4fe03e951370139c6e0adb81f48a8dcf4c6 \
+ --hash=sha256:b76311335adda4de3c1d471e64e89a49abfeebf02647e3db064e7740e7f36ed6 \
+ --hash=sha256:bb96796be5919871fa9ae7e88968ba9e14306d9a3f217ca6c204f68a5abeccdd \
+ --hash=sha256:e9bcb71a3efb5fe886eb48d739cfae5df4a15617e7b5a7668aa45ebf74c0d3fa \
+ --hash=sha256:ea239cfedf67b74ea4952e1074bb99a4281c2145441d70bc7e2f058d5c49f1c9 \
+ --hash=sha256:fedfb60f986c26cdb1809db02866e68508db99910c587d2c4066a5c07aa85593
+ # via -r requirements-dev.in
selenium==4.7.2 \
--hash=sha256:06a1c7d9f313130b21c3218ddd8852070d0e7419afdd31f96160cd576555a5ce \
--hash=sha256:3aefa14a28a42e520550c1cd0f29cf1d566328186ea63aa9a3e01fb265b5894d
diff --git a/src/character/admin.py b/src/character/admin.py
index d6cb3db..5df8223 100644
--- a/src/character/admin.py
+++ b/src/character/admin.py
@@ -43,10 +43,9 @@ class PathAdmin(admin.ModelAdmin):
category = models.Path.Category(instance.category)
if category == models.Path.Category.PROFILE:
return str(instance.profile)
- elif category == models.Path.Category.RACE:
+ if category == models.Path.Category.RACE:
return str(instance.race)
- else:
- return ""
+ return ""
@admin.register(models.RacialCapability)
diff --git a/src/character/management/commands/import_capabilities.py b/src/character/management/commands/import_capabilities.py
index c47d719..32e0ddf 100644
--- a/src/character/management/commands/import_capabilities.py
+++ b/src/character/management/commands/import_capabilities.py
@@ -8,7 +8,7 @@ from character.models import Capability, Path
class Command(BaseCommand):
- def handle(self, *args, **options):
+ def handle(self, *args, **options) -> None: # noqa: ARG002
url = "https://www.co-drs.org/fr/jeu/capacites"
self.setup_selenium()
self.selenium.get(url)
@@ -23,7 +23,7 @@ class Command(BaseCommand):
try:
self.import_capability(card)
except Exception as e:
- print(f"{type(e)}: {e}")
+ self.stderr.write(f"{type(e)}: {e}")
self.stdout.write(f"Finished processing {len(cards)} caps.")
def import_capability(self, card: WebElement):
diff --git a/src/character/management/commands/import_harmful_states.py b/src/character/management/commands/import_harmful_states.py
index a248a0f..722879f 100644
--- a/src/character/management/commands/import_harmful_states.py
+++ b/src/character/management/commands/import_harmful_states.py
@@ -7,7 +7,7 @@ from character.models.character import HarmfulState
class Command(BaseCommand):
- def handle(self, *args, **options) -> None:
+ def handle(self, *args, **options) -> None: # noqa: ARG002
url = "https://www.co-drs.org/fr/jeu/etats-prejudiciables"
self.setup_selenium()
self.selenium.get(url)
@@ -16,7 +16,7 @@ class Command(BaseCommand):
try:
self.import_row(url, state)
except Exception as e:
- print(f"{type(e)}: {e}")
+ self.stderr.write(f"{type(e)}: {e}")
self.stdout.write(f"Finished processing {len(states)} states.")
def import_row(self, url: str, state_row: WebElement) -> None:
diff --git a/src/character/management/commands/import_paths.py b/src/character/management/commands/import_paths.py
index 2217cd3..8d9fc1b 100644
--- a/src/character/management/commands/import_paths.py
+++ b/src/character/management/commands/import_paths.py
@@ -7,7 +7,7 @@ from character.models import Path, Profile, Race
class Command(BaseCommand):
- def handle(self, *args, **options):
+ def handle(self, *args, **options) -> None: # noqa: ARG002
url = "https://www.co-drs.org/fr/jeu/voies"
self.setup_selenium()
self.selenium.get(url)
@@ -23,7 +23,7 @@ class Command(BaseCommand):
try:
self.import_path(url)
except Exception as e:
- print(f"{type(e)}: {e}")
+ self.stderr.write(f"{type(e)}: {e}")
self.stdout.write(f"Finished processing {len(urls)} paths.")
def import_path(self, url: str):
@@ -81,9 +81,10 @@ class Command(BaseCommand):
profile_name = self.selenium.find_element(
By.CSS_SELECTOR, ".field--name-type + strong + a"
).text
- return Profile.objects.get_by_natural_key(profile_name)
except Exception:
self.stdout.write(self.style.WARNING(f"Couldn't find profile for {name}"))
+ else:
+ return Profile.objects.get_by_natural_key(profile_name)
def get_race(self, path_name: str) -> Race | None:
to_remove = ["voie de la", "voie de l'", "voie du"]
diff --git a/src/character/management/commands/import_profiles.py b/src/character/management/commands/import_profiles.py
index 6be6459..c7039d7 100644
--- a/src/character/management/commands/import_profiles.py
+++ b/src/character/management/commands/import_profiles.py
@@ -7,7 +7,7 @@ from character.models.dice import Dice
class Command(BaseCommand):
- def handle(self, *args, **options) -> None:
+ def handle(self, *args, **options) -> None: # noqa: ARG002
url = "https://www.co-drs.org/fr/jeu/profils"
self.setup_selenium()
self.selenium.get(url)
@@ -17,7 +17,7 @@ class Command(BaseCommand):
try:
self.import_profile(url)
except Exception as e:
- print(f"{type(e)}: {e}")
+ self.stderr.write(f"{type(e)}: {e}")
def import_profile(self, url: str) -> None:
self.selenium.get(url)
@@ -48,8 +48,7 @@ class Command(BaseCommand):
self.stdout.write(
self.style.WARNING(f"Multiple dice for {name}: {number_of_dice}")
)
- dice = Dice(dice_value)
- return dice
+ return Dice(dice_value)
def get_magical_strength(self) -> Profile.MagicalStrength:
try:
@@ -79,7 +78,7 @@ class Command(BaseCommand):
def get_mana_max_compute(self, name) -> Profile.ManaMax:
if name in ["Barde", "Druide", "Forgesort", "Prêtre"]:
return Profile.ManaMax.LEVEL
- elif name in ["Ensorceleur", "Magicien", "Nécromancien"]:
+ if name in ["Ensorceleur", "Magicien", "Nécromancien"]:
return Profile.ManaMax.DOUBLE_LEVEL
return Profile.ManaMax.NO_MANA
diff --git a/src/character/management/commands/import_races.py b/src/character/management/commands/import_races.py
index ea4f71a..f40654b 100644
--- a/src/character/management/commands/import_races.py
+++ b/src/character/management/commands/import_races.py
@@ -6,7 +6,7 @@ from character.models import Race, RacialCapability
class Command(BaseCommand):
- def handle(self, *args, **options) -> None:
+ def handle(self, *args, **options) -> None: # noqa: ARG002
url = "https://www.co-drs.org/fr/jeu/races"
self.setup_selenium()
self.selenium.get(url)
@@ -16,7 +16,7 @@ class Command(BaseCommand):
try:
self.import_race(url)
except Exception as e:
- print(f"{type(e)}: {e}")
+ self.stderr.write(f"{type(e)}: {e}")
self.stdout.write(f"Finished processing {len(urls)} races.")
def import_race(self, url: str) -> None:
diff --git a/src/character/management/commands/import_weapons.py b/src/character/management/commands/import_weapons.py
index 41c4bf0..e5646be 100644
--- a/src/character/management/commands/import_weapons.py
+++ b/src/character/management/commands/import_weapons.py
@@ -7,7 +7,7 @@ from character.models import Weapon
class Command(BaseCommand):
- def handle(self, *args, **options) -> None:
+ def handle(self, *args, **options) -> None: # noqa: ARG002
url = "https://www.co-drs.org/fr/ressources/equipements/armes"
self.setup_selenium()
self.selenium.get(url)
@@ -16,7 +16,7 @@ class Command(BaseCommand):
try:
self.import_row(url, state)
except Exception as e:
- print(f"{type(e)}: {e}")
+ self.stderr.write(f"{type(e)}: {e}")
self.stdout.write(f"Finished processing {len(states)} weapons.")
def import_row(self, url: str, state_row: WebElement) -> None:
diff --git a/src/character/migrations/0039_alter_character_profile_picture.py b/src/character/migrations/0039_alter_character_profile_picture.py
index 82be8cf..de976b9 100644
--- a/src/character/migrations/0039_alter_character_profile_picture.py
+++ b/src/character/migrations/0039_alter_character_profile_picture.py
@@ -22,7 +22,7 @@ class Migration(migrations.Migration):
null=True,
upload_to="profile_pictures",
validators=[
- functools.partial(
+ functools.partial( # noqa: PIE804
character.models.character.validate_image,
*(),
**{"megabytes_limit": 2},
diff --git a/src/character/models/capabilities.py b/src/character/models/capabilities.py
index 40aec7e..415ad75 100644
--- a/src/character/models/capabilities.py
+++ b/src/character/models/capabilities.py
@@ -59,10 +59,9 @@ class Path(DocumentedModel, UniquelyNamedModel, TimeStampedModel, models.Model):
category = Path.Category(self.category)
if category == Path.Category.PROFILE:
return self.profile
- elif category == Path.Category.RACE:
+ if category == Path.Category.RACE:
return self.race
- else:
- return None
+ return None
def get_next_capability(self, character) -> Capability:
next_rank = self.max_rank(character) + 1
@@ -71,9 +70,10 @@ class Path(DocumentedModel, UniquelyNamedModel, TimeStampedModel, models.Model):
def has_next_capability(self, character) -> bool:
try:
self.get_next_capability(character)
- return True
except Capability.DoesNotExist:
return False
+ else:
+ return True
def max_rank(self, character) -> int:
return character.capabilities.filter(path=self).count()
diff --git a/src/character/models/character.py b/src/character/models/character.py
index 15397b8..b838918 100644
--- a/src/character/models/character.py
+++ b/src/character/models/character.py
@@ -66,7 +66,7 @@ class HarmfulState(DocumentedModel, UniquelyNamedModel, TimeStampedModel, models
def modifier(value: int) -> int:
if not value:
return 0
- if 1 < value < 10:
+ if 1 < value < 10: # noqa: PLR2004
value -= 1
value -= 10
return int(value / 2)
@@ -343,10 +343,9 @@ class Character(models.Model):
mana_max_compute = self.profile.mana_max_compute
if mana_max_compute == Profile.ManaMax.NO_MANA:
return 0
- elif mana_max_compute == Profile.ManaMax.LEVEL:
+ if mana_max_compute == Profile.ManaMax.LEVEL:
return self.level + self.modifier_magic
- else:
- return 2 * self.level + self.modifier_magic
+ return 2 * self.level + self.modifier_magic
@property
def height_m(self) -> float:
diff --git a/src/character/templatetags/character_extras.py b/src/character/templatetags/character_extras.py
index f300549..76e838d 100644
--- a/src/character/templatetags/character_extras.py
+++ b/src/character/templatetags/character_extras.py
@@ -10,8 +10,7 @@ register = template.Library()
def modifier(value):
if value > 0:
return f"+{value}"
- else:
- return value
+ return value
@register.filter
@@ -24,10 +23,9 @@ def weapon_modifier(character: Character, weapon: Weapon):
value = character.get_modifier_for_weapon(weapon)
if value > 0:
return f"+ {value}"
- elif value < 0:
+ if value < 0:
return f"- {abs(value)}"
- else:
- return ""
+ return ""
@register.filter
diff --git a/src/character/tests/test_access.py b/src/character/tests/test_access.py
index 60bacec..af3a4fc 100644
--- a/src/character/tests/test_access.py
+++ b/src/character/tests/test_access.py
@@ -1,3 +1,5 @@
+from http import HTTPStatus
+
import pytest
from model_bakery import baker
@@ -6,7 +8,7 @@ from common.models import User
from party.models import Party
-@pytest.mark.django_db
+@pytest.mark.django_db()
def test_can_access_own_character(client):
player = User.objects.create_user("username", password="password")
@@ -15,14 +17,14 @@ def test_can_access_own_character(client):
character = baker.make(Character, player=player, notes=notes, gm_notes=gm_notes)
client.force_login(player)
res = client.get(character.get_absolute_url())
- assert res.status_code == 200
+ assert res.status_code == HTTPStatus.OK
body = res.content.decode("utf-8")
assert notes in body
assert gm_notes not in body
-@pytest.mark.django_db
+@pytest.mark.django_db()
def test_cant_access_random_character(client):
player = User.objects.create_user("user", password="password")
other = User.objects.create_user("other", password="password")
@@ -30,10 +32,10 @@ def test_cant_access_random_character(client):
character = baker.make(Character, player=other)
client.force_login(player)
res = client.get(character.get_absolute_url())
- assert res.status_code == 404
+ assert res.status_code == HTTPStatus.NOT_FOUND
-@pytest.mark.django_db
+@pytest.mark.django_db()
def test_can_access_character_in_party(client):
player = User.objects.create_user("user", password="password")
friend = User.objects.create_user("friend", password="password")
@@ -49,14 +51,14 @@ def test_can_access_character_in_party(client):
party.characters.add(friend_character)
client.force_login(player)
res = client.get(friend_character.get_absolute_url())
- assert res.status_code == 200
+ assert res.status_code == HTTPStatus.OK
body = res.content.decode("utf-8")
assert notes not in body
assert gm_notes not in body
-@pytest.mark.django_db
+@pytest.mark.django_db()
def test_game_master_can_access_character_in_party(client):
player = User.objects.create_user("user", password="password")
gm = User.objects.create_user("gm", password="password")
@@ -68,7 +70,7 @@ def test_game_master_can_access_character_in_party(client):
party.characters.add(character)
client.force_login(gm)
res = client.get(character.get_absolute_url())
- assert res.status_code == 200
+ assert res.status_code == HTTPStatus.OK
body = res.content.decode("utf-8")
assert notes in body
diff --git a/src/character/tests/test_character.py b/src/character/tests/test_character.py
index 63b6d4c..eb87589 100644
--- a/src/character/tests/test_character.py
+++ b/src/character/tests/test_character.py
@@ -8,7 +8,7 @@ from character.tests.utils import ability_values, levels, modifier_test
@pytest.mark.parametrize(
- "value,expected",
+ ("value", "expected"),
[
(1, -4),
(2, -4),
diff --git a/src/character/tests/test_interactions.py b/src/character/tests/test_interactions.py
index bab1081..70ae403 100644
--- a/src/character/tests/test_interactions.py
+++ b/src/character/tests/test_interactions.py
@@ -12,7 +12,7 @@ from character.models import Character, Profile
from common.models import User
-@pytest.mark.django_db
+@pytest.mark.django_db()
def test_create_character(selenium: WebDriver, live_server: LiveServer):
# Load fixtures
call_command("loaddata", "initial_data")
@@ -93,7 +93,7 @@ def test_create_character(selenium: WebDriver, live_server: LiveServer):
assert getattr(character, name) == value
-@pytest.mark.django_db
+@pytest.mark.django_db()
def test_list_characters(selenium: WebDriver, live_server: LiveServer):
# Load fixtures
call_command("loaddata", "initial_data")
@@ -125,7 +125,7 @@ def test_list_characters(selenium: WebDriver, live_server: LiveServer):
assert names == expected_names
-@pytest.mark.django_db
+@pytest.mark.django_db()
def test_delete_character(selenium: WebDriver, live_server: LiveServer):
call_command("loaddata", "initial_data")
@@ -146,7 +146,7 @@ def test_delete_character(selenium: WebDriver, live_server: LiveServer):
assert Character.objects.filter(pk=characters[0].pk).first() is None
-@pytest.mark.django_db
+@pytest.mark.django_db()
def test_reset_stats_view(
selenium: WebDriver, live_server: LiveServer, initial_data: None
):
diff --git a/src/character/tests/test_modifier.py b/src/character/tests/test_modifier.py
index 7de9128..28daef2 100644
--- a/src/character/tests/test_modifier.py
+++ b/src/character/tests/test_modifier.py
@@ -4,7 +4,7 @@ from character.models.character import modifier
@pytest.mark.parametrize(
- "value,expected",
+ ("value", "expected"),
[
(1, -4),
(2, -4),
diff --git a/src/character/views.py b/src/character/views.py
index 21d2554..491bc83 100644
--- a/src/character/views.py
+++ b/src/character/views.py
@@ -316,13 +316,12 @@ def character_equipment_change(request, pk: int):
f"character/snippets/character_details/{field}_display.html",
context,
)
- else:
- context["errors"] = form.errors
- return render(
- request,
- f"character/snippets/character_details/{field}_update.html",
- context,
- )
+ context["errors"] = form.errors
+ return render(
+ request,
+ f"character/snippets/character_details/{field}_update.html",
+ context,
+ )
@login_required
diff --git a/src/charasheet/context_processors.py b/src/charasheet/context_processors.py
index 18ab923..956ba17 100644
--- a/src/charasheet/context_processors.py
+++ b/src/charasheet/context_processors.py
@@ -1,5 +1,5 @@
from django.conf import settings
-def app(request):
+def app(_):
return settings.APP
diff --git a/src/charasheet/middleware.py b/src/charasheet/middleware.py
index 873ca33..269825d 100644
--- a/src/charasheet/middleware.py
+++ b/src/charasheet/middleware.py
@@ -1,7 +1,7 @@
from django.conf import settings
-def debug_toolbar_bypass_internal_ips(request) -> bool:
+def debug_toolbar_bypass_internal_ips(_) -> bool:
"""
Display debug toolbar according to the DEBUG_TOOLBAR setting only.
diff --git a/src/charasheet/settings.py b/src/charasheet/settings.py
index 992fc46..674ea39 100644
--- a/src/charasheet/settings.py
+++ b/src/charasheet/settings.py
@@ -247,17 +247,17 @@ APP = {
}
}
try:
- with open("/app/git/build-date") as f:
+ with Path("/app/git/build-date").open() as f:
APP["build"]["date"] = f.read().strip()
-except Exception: # noqa: S110
- pass
+except Exception:
+ pass # noqa: S110
try:
- with open("/app/git/git-commit") as f:
+ with Path("/app/git/git-commit").open() as f:
APP["build"]["commit"] = f.read().strip()
-except Exception: # noqa: S110
- pass
+except Exception:
+ pass # noqa: S110
try:
- with open("/app/git/git-describe") as f:
+ with Path("/app/git/git-describe").open() as f:
APP["build"]["describe"] = f.read().strip()
-except Exception: # noqa: S110
- pass
+except Exception:
+ pass # noqa: S110
diff --git a/src/common/tests/test_admin.py b/src/common/tests/test_admin.py
index ff9b60e..83942ba 100644
--- a/src/common/tests/test_admin.py
+++ b/src/common/tests/test_admin.py
@@ -1,3 +1,5 @@
+from http import HTTPStatus
+
import pytest
from django.contrib.auth import get_user_model
from django.urls import reverse
@@ -9,17 +11,17 @@ class TestUserAdmin:
def test_changelist(self, admin_client):
url = reverse("admin:common_user_changelist")
response = admin_client.get(url)
- assert response.status_code == 200
+ assert response.status_code == HTTPStatus.OK
def test_search(self, admin_client):
url = reverse("admin:common_user_changelist")
response = admin_client.get(url, data={"q": "test"})
- assert response.status_code == 200
+ assert response.status_code == HTTPStatus.OK
def test_add(self, admin_client):
url = reverse("admin:common_user_add")
response = admin_client.get(url)
- assert response.status_code == 200
+ assert response.status_code == HTTPStatus.OK
response = admin_client.post(
url,
@@ -29,11 +31,11 @@ class TestUserAdmin:
"password2": "My_R@ndom-P@ssw0rd",
},
)
- assert response.status_code == 302
+ assert response.status_code == HTTPStatus.FOUND
assert get_user_model().objects.filter(username="test").exists()
def test_view_user(self, admin_client):
user = get_user_model().objects.get(username="admin")
url = reverse("admin:common_user_change", kwargs={"object_id": user.pk})
response = admin_client.get(url)
- assert response.status_code == 200
+ assert response.status_code == HTTPStatus.OK
diff --git a/src/conftest.py b/src/conftest.py
index 7fbdccf..32c35ca 100644
--- a/src/conftest.py
+++ b/src/conftest.py
@@ -4,23 +4,23 @@ from selenium.webdriver.remote.webdriver import WebDriver
@pytest.fixture(scope="session", autouse=True)
-def collectstatic():
+def _collectstatic():
call_command("collectstatic", "--clear", "--noinput", "--verbosity=0")
-@pytest.fixture
+@pytest.fixture()
def live_server(settings, live_server):
settings.STATICFILES_STORAGE = "whitenoise.storage.CompressedStaticFilesStorage"
return live_server
-@pytest.fixture
+@pytest.fixture()
def firefox_options(firefox_options):
firefox_options.add_argument("-headless")
return firefox_options
-@pytest.fixture
+@pytest.fixture()
def selenium(selenium: WebDriver) -> WebDriver:
selenium.implicitly_wait(3)
selenium.set_window_size(3860, 2140)
@@ -33,6 +33,7 @@ def settings(settings):
return settings
-@pytest.fixture
-def initial_data(db: None) -> None:
+@pytest.fixture()
+@pytest.mark.django_db()
+def initial_data() -> None: # noqa: PT004
call_command("loaddata", "initial_data")
diff --git a/src/party/tests/test_interactions.py b/src/party/tests/test_interactions.py
index 219d454..06bf7df 100644
--- a/src/party/tests/test_interactions.py
+++ b/src/party/tests/test_interactions.py
@@ -16,7 +16,7 @@ from common.models import User
from party.models import BattleEffect, Party
-@pytest.mark.django_db
+@pytest.mark.django_db()
def test_add_character_to_existing_group(selenium: WebDriver, live_server: LiveServer):
username, password = "gm", "password"
gm = User.objects.create_user(username, password=password)
@@ -39,7 +39,7 @@ def test_add_character_to_existing_group(selenium: WebDriver, live_server: LiveS
assert set(party.invited_characters.all()) == {character}
-@pytest.mark.django_db
+@pytest.mark.django_db()
def test_gm_observe_invited_character_in_group(
selenium: WebDriver, live_server: LiveServer
):
@@ -63,7 +63,7 @@ def test_gm_observe_invited_character_in_group(
assert title == character.name
-@pytest.mark.django_db
+@pytest.mark.django_db()
def test_gm_observe_invited_character_in_two_groups(
selenium: WebDriver, live_server: LiveServer
):
@@ -89,7 +89,7 @@ def test_gm_observe_invited_character_in_two_groups(
assert title == character.name
-@pytest.mark.django_db
+@pytest.mark.django_db()
def test_reset_stats_view(
selenium: WebDriver, live_server: LiveServer, initial_data: None
):
@@ -117,7 +117,7 @@ def test_reset_stats_view(
assert character.luck_points_remaining == character.luck_points_max
-@pytest.mark.django_db
+@pytest.mark.django_db()
def test_player_can_add_effect_to_group(selenium: WebDriver, live_server: LiveServer):
"""Any member of a group can add effects to the group."""
user, password = "player", "password"
@@ -146,7 +146,7 @@ def test_player_can_add_effect_to_group(selenium: WebDriver, live_server: LiveSe
assert effect.description in element.text
-@pytest.mark.django_db
+@pytest.mark.django_db()
def test_gm_can_add_effect_to_group(selenium: WebDriver, live_server: LiveServer):
"""The GM of a group can add effects to the group."""
user, password = "gm", "password"
@@ -175,7 +175,7 @@ def test_gm_can_add_effect_to_group(selenium: WebDriver, live_server: LiveServer
assert effect.description in element.text
-@pytest.mark.django_db
+@pytest.mark.django_db()
def test_gm_can_change_remaining_rounds(selenium: WebDriver, live_server: LiveServer):
"""The GM of a group can increase or decrease the remaining rounds of effects."""
user, password = "gm", "password"
@@ -188,7 +188,7 @@ def test_gm_can_change_remaining_rounds(selenium: WebDriver, live_server: LiveSe
remaining_rounds=lambda: random.randint(3, 12),
party=party,
)
- active_nearly_terminated = baker.make( # noqa: F841
+ active_nearly_terminated = baker.make(
BattleEffect, _quantity=3, remaining_rounds=1, party=party
)
terminated = baker.make( # noqa: F841
@@ -244,7 +244,7 @@ def test_gm_can_change_remaining_rounds(selenium: WebDriver, live_server: LiveSe
)
-@pytest.mark.django_db
+@pytest.mark.django_db()
def test_gm_can_delete_any_existing_effect(
selenium: WebDriver, live_server: LiveServer
):
@@ -265,7 +265,7 @@ def test_gm_can_delete_any_existing_effect(
BattleEffect.objects.get(pk=effects[1].pk)
-@pytest.mark.django_db
+@pytest.mark.django_db()
def test_player_cant_change_existing_running_effect(
selenium: WebDriver, live_server: LiveServer
):
diff --git a/tasks.py b/tasks.py
index e0e9ee7..15e37f5 100644
--- a/tasks.py
+++ b/tasks.py
@@ -10,7 +10,7 @@ TEST_ENV = {"ENV_FILE": BASE_DIR / "envs" / "test-envs.env"}
@task
-def sync_dependencies(ctx: Context, update: bool = False):
+def sync_dependencies(ctx: Context, *, update: bool = False):
common_args = "-q --allow-unsafe --resolver=backtracking"
if update:
common_args += " --upgrade"
@@ -107,5 +107,5 @@ def import_from_co_drs(ctx: Context):
@task(pre=[import_from_co_drs, dump_initial])
-def update_fixtures(ctx: Context):
+def update_fixtures(_: Context):
pass