Skip to content

Commit

Permalink
Linter Updates & Dropped Python 3.8
Browse files Browse the repository at this point in the history
  • Loading branch information
GitRon authored Jul 15, 2024
1 parent 1c3dd2f commit 8a183b7
Show file tree
Hide file tree
Showing 37 changed files with 140 additions and 88 deletions.
31 changes: 31 additions & 0 deletions .ambient-package-update/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,36 @@
RuffIgnoredInspection(key="DTZ005", comment='TODO will affect "tz_today()" method'),
RuffIgnoredInspection(key="TD002", comment="Missing author in TODO"),
RuffIgnoredInspection(key="TD003", comment="Missing issue link on the line following this TODO"),
RuffIgnoredInspection(key="D1", comment="Missing docstring"),
RuffIgnoredInspection(
key="D200",
comment="Fits-on-one-line",
),
RuffIgnoredInspection(
key="D203", comment="one-blank-line-before-class - incompatible to D211 no-blank-line-before-class"
),
RuffIgnoredInspection(
key="D205", comment="Checks docstring summary lines not separated from the docstring description"
),
RuffIgnoredInspection(
key="D212",
comment="Multi-line-summary-first-line",
),
RuffIgnoredInspection(
key="D400",
comment="Checks docstrings in which the first line does not end in a period -> ! ? should also be allowed",
),
RuffIgnoredInspection(
key="D401", comment="Checks function docstrings that include the function's signature in the summary line"
),
RuffIgnoredInspection(
key="D415",
comment='Checks first line docstrings doesn\'t end in a punctuation mark, ".", "?", "!" -> weird behavior',
),
RuffIgnoredInspection(key="TRY002", comment="Checks for code that raises Exception directly."),
RuffIgnoredInspection(
key="TRY003",
comment="Checks for long exception messages that are not defined in the exception class itself.",
),
],
)
4 changes: 1 addition & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,10 @@ jobs:
runs-on: ubuntu-22.04
strategy:
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', ]
python-version: ['3.9', '3.10', '3.11', '3.12', ]
django-version: ['42', '50', ]

exclude:
- python-version: '3.8'
django-version: 50
- python-version: '3.9'
django-version: 50

Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ repos:
rev: v3.16.0
hooks:
- id: pyupgrade
args: [ --py38-plus ]
args: [ --py39-plus ]
stages: [ push ]

- repo: https://github.com/adamchainz/django-upgrade
Expand Down
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

**10.2.0** (2024-07-15)
* Dropped Python 3.8 support
* Added new Ruff linting rules

**10.1.1** (2024-07-08)
* Fixed documentation nitpick

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ The migration is really simple, just:

- Install the package via pip:

`pip install ambient-toolbox`
`pip install ambient_toolbox`

or via pipenv:

`pipenv install ambient-toolbox`
`pipenv install ambient_toolbox`

- Add module to `INSTALLED_APPS` within the main django `settings.py`:

Expand Down
5 changes: 3 additions & 2 deletions ambient_toolbox/admin/model_admins/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ def get_readonly_fields(self, request, obj=None):
"""
Set the fields CommonInfo handles to readonly to avoid users fiddling around with them.
"""
return super().get_readonly_fields(request, obj) + (
return (
*super().get_readonly_fields(request, obj),
"created_by",
"lastmodified_by",
"created_at",
Expand Down Expand Up @@ -121,7 +122,7 @@ class DeactivatableChangeViewAdminMixin:

def can_see_change_view(self, request) -> bool:
"""
This method determines if the change view is disabled or visible.
A method that determines if the change view is disabled or visible.
"""
return self.enable_change_view

Expand Down
2 changes: 1 addition & 1 deletion ambient_toolbox/drf/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def validate(self, data):

class CommonInfoSerializer(BaseModelSerializer):
"""
This serializer should be used for all models that extend "CommonInfo". It adds the data for
A serializer that should be used for all models that extend "CommonInfo". It adds the data for
`lastmodified_by` and `created_by` when saving a model through the serializer.
This cannot be done in the model's `save` function, since the request is required.
"""
Expand Down
1 change: 0 additions & 1 deletion ambient_toolbox/drf/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ def execute_request( # noqa: PLR0913
viewset_kwargs: ViewSets need a dict to tell it, what action we want to execute
data_format: Format of the sent data, defaults to JSON, might be "multipart" etc.
"""

# Set proper default fallback value for dicts
if view_kwargs is None:
view_kwargs = {}
Expand Down
1 change: 0 additions & 1 deletion ambient_toolbox/gitlab/coverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,6 @@ def process(self):
Compare coverage from target branch (latest develop) with the current one.
At first, we try to get a successfully finished pipeline for the "self.target_branch" (usually "develop")
"""

# Check, if coverage is supposed to run. If not, inform the user and return early.
if self.disable_coverage:
print("Coverage was skipped!")
Expand Down
2 changes: 1 addition & 1 deletion ambient_toolbox/graphql/forms/mutations.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def on_resolve(payload):
try:
payload.client_mutation_id = input.get("client_mutation_id")
except Exception as e:
raise Exception(f"Cannot set client_mutation_id in the payload object {repr(payload)}") from e
raise Exception(f"Cannot set client_mutation_id in the payload object {payload!r}") from e
return payload

result = cls.mutate_and_get_payload(root, info, **input)
Expand Down
4 changes: 2 additions & 2 deletions ambient_toolbox/graphql/tests/base_test.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import json
from typing import Optional

from django.test import Client, TestCase

Expand All @@ -22,15 +23,14 @@ def setUpClass(cls):

cls._client = Client(cls.GRAPHQL_SCHEMA)

def query(self, query: str, op_name: str = None, input_data: dict = None):
def query(self, query: str, op_name: Optional[str] = None, input_data: Optional[dict] = None):
"""
:param query: GraphQL query to run
:param op_name: If the query is a mutation or named query, you must supply the op_name.
For annon queries ("{ ... }"), should be None (default).
:param input_data: If provided, the $input variable in GraphQL will be set to this value
:return: Response object from client
"""

body = {"query": query}
if op_name:
body["operation_name"] = op_name
Expand Down
9 changes: 6 additions & 3 deletions ambient_toolbox/mixins/bleacher.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import ClassVar

import bleach


Expand Down Expand Up @@ -29,15 +31,16 @@ class BleacherMixin:
* img: alt, src
"""

BLEACH_FIELD_LIST = []
BLEACH_FIELD_LIST: ClassVar = []

DEFAULT_ALLOWED_ATTRIBUTES = {
DEFAULT_ALLOWED_ATTRIBUTES: ClassVar = {
"*": ["class", "style", "id"],
"a": ["href", "rel"],
"img": ["alt", "src"],
}

DEFAULT_ALLOWED_TAGS = bleach.ALLOWED_TAGS + [
DEFAULT_ALLOWED_TAGS: ClassVar = [
*bleach.ALLOWED_TAGS,
"span",
"p",
"h1",
Expand Down
5 changes: 2 additions & 3 deletions ambient_toolbox/permissions/fixtures/declarations.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import dataclasses
from typing import List


@dataclasses.dataclass
class PermissionModelDeclaration:
app_label: str
codename_list: List[str]
codename_list: list[str]
model: str


@dataclasses.dataclass
class GroupPermissionDeclaration:
name: str
permission_list: List[PermissionModelDeclaration]
permission_list: list[PermissionModelDeclaration]
5 changes: 1 addition & 4 deletions ambient_toolbox/permissions/fixtures/helpers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
from typing import List


def generate_default_permissions(model_name: str) -> List[str]:
def generate_default_permissions(model_name: str) -> list[str]:
return [
f"add_{model_name}",
f"change_{model_name}",
Expand Down
4 changes: 1 addition & 3 deletions ambient_toolbox/permissions/fixtures/services.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from typing import List

from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
from django.db import transaction
Expand All @@ -18,7 +16,7 @@ def __init__(self, group_declaration: GroupPermissionDeclaration, dry_run: bool
self.dry_run = dry_run

@transaction.atomic
def process(self) -> (List[Permission], List[Permission]):
def process(self) -> (list[Permission], list[Permission]):
# Fetch or create group
group, created = Group.objects.get_or_create(name=self.group_declaration.name)

Expand Down
2 changes: 1 addition & 1 deletion ambient_toolbox/selectors/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

class Selector(manager.Manager):
"""
This is the base class for query selectors. Please refer to the docs for further enlightenment how this novel
A base class for query selectors. Please refer to the docs for further enlightenment how this novel
pattern works.
It's derived from the manager to use Django's magic to inject the current class into the "model" attribute.
"""
Expand Down
10 changes: 6 additions & 4 deletions ambient_toolbox/sentry/helpers.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import List, Optional
from typing import Optional

from sentry_sdk.serializer import serialize

Expand All @@ -13,11 +13,13 @@ class SentryEventScrubber:
before_send_transaction = SentryEventScrubber().scrub_sensitive_data_from_sentry_event
"""

def __init__(self, denylist: Optional[List[str]] = None, standard_denylist: Optional[bool] = True) -> None:
def __init__(self, denylist: Optional[list[str]] = None, standard_denylist: Optional[bool] = True) -> None:
"""
Arguments:
* denylist: A list of keys that should be scrubbed from the Sentry event.
* standard_denylist: By default certain keys are already scrubbed from the event.
---------
denylist: A list of keys that should be scrubbed from the Sentry event.
standard_denylist: By default, certain keys are already scrubbed from the event.
"""
self.denylist = [] if denylist is None else denylist
self.standard_denylist = (
Expand Down
13 changes: 7 additions & 6 deletions ambient_toolbox/services/custom_scrubber.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging
from typing import ClassVar, Final

from django.conf import settings
from django.contrib.admin.models import LogEntry
Expand All @@ -13,14 +14,14 @@ class ScrubbingError(RuntimeError):


class AbstractScrubbingService:
DEFAULT_USER_PASSWORD = "Admin0404!"
DEFAULT_USER_PASSWORD: Final[str] = "Admin0404!"

# Over-writable values
keep_session_data = False
keep_scrubber_data = False
keep_django_admin_log = False
pre_scrub_functions = []
post_scrub_functions = []
keep_session_data: bool = False
keep_scrubber_data: bool = False
keep_django_admin_log: bool = False
pre_scrub_functions: ClassVar = []
post_scrub_functions: ClassVar = []

def __init__(self):
self._logger = logging.getLogger("django_scrubber")
Expand Down
1 change: 0 additions & 1 deletion ambient_toolbox/templatetags/ai_date_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,4 @@ def format_to_minutes(time):
:param time:
:return minutes:
"""

return time.seconds // 60
2 changes: 1 addition & 1 deletion ambient_toolbox/templatetags/ai_email_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@


def obfuscate_string(value):
return "".join([f"&#{str(ord(char)):s};" for char in value])
return "".join([f"&#{ord(char)!s:s};" for char in value])


@register.filter
Expand Down
2 changes: 1 addition & 1 deletion ambient_toolbox/templatetags/ai_file_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@ def filesize(value):
"""
try:
return os.path.getsize(f"{settings.MEDIA_ROOT}{value}")
except Exception:
except Exception: # noqa: BLE001
return 0
1 change: 0 additions & 1 deletion ambient_toolbox/tests/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ def _authentication(request, user):

def _get_response(self, method, user, data, url_params=None, *args, **kwargs):
"""Returns response."""

# Catch case that URL does not get any params passed to
if not url_params:
url_params = {}
Expand Down
4 changes: 2 additions & 2 deletions ambient_toolbox/utils/date.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import datetime
from calendar import monthrange
from typing import Tuple, Union
from typing import Optional, Union

import pytz
from dateutil.relativedelta import relativedelta
Expand Down Expand Up @@ -150,7 +150,7 @@ def date_month_delta(start_date: datetime.date, end_date: datetime.date) -> floa
return delta


def get_first_and_last_of_month(date_object: datetime.date = None) -> Tuple[datetime.date, datetime.date]:
def get_first_and_last_of_month(date_object: Optional[datetime.date] = None) -> tuple[datetime.date, datetime.date]:
"""
Returns first and last day of a month as date objects.
Will either return first/last of current month (if no datetime_object is passed), or will determine first/last of
Expand Down
8 changes: 4 additions & 4 deletions ambient_toolbox/utils/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ def get_filename_without_ending(file_path: str) -> str:
"""
Returns the filename without extension
"""

# if filename has file_path parts
if "/" in file_path:
filename = file_path.rsplit("/")[-1]
Expand All @@ -17,12 +16,13 @@ def get_filename_without_ending(file_path: str) -> str:


def crc(file_path: str) -> str:
"""Calculates the cyclic redundancy checksum (CRC) of the given file.
"""
Calculates the cyclic redundancy checksum (CRC) of the given file.
See ``open`` for all the exceptions that can be raised.
:param file_path: the file for which the CRC checksum should be calculated.
:return: returns the CRC checksum of the file in hexadecimal format (8 characters).
:param file_path: The file for which the CRC checksum should be calculated.
:return: Returns the CRC checksum of the file in hexadecimal format (8 characters).
"""
prev = 0
with open(file_path, "rb") as f:
Expand Down
5 changes: 3 additions & 2 deletions ambient_toolbox/utils/model.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
from typing import Optional

from django.db.models import ForeignKey


def object_to_dict(obj, blacklisted_fields: list = None, include_id: bool = False) -> dict:
def object_to_dict(obj, blacklisted_fields: Optional[list] = None, include_id: bool = False) -> dict:
"""
Returns a dict with all data defined in the model class as a key-value-dict
Attention: Does not work for M2M fields!
"""

# Default blacklist
blacklisted_fields = blacklisted_fields if blacklisted_fields else []

Expand Down
6 changes: 3 additions & 3 deletions ambient_toolbox/utils/named_tuple.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def is_valid(self, selection):
return Choices._make([val for val, name, desc in choices_tuple])


def get_value_from_tuple_by_key(choices: tuple, key) -> Any:
def get_value_from_tuple_by_key(choices: tuple, key) -> Any: # noqa: ANN401
"""
Fetches the tuple value by a given key
Useful for getting the name of a key from a model choice tuple of tuples.
Expand All @@ -108,13 +108,13 @@ def get_value_from_tuple_by_key(choices: tuple, key) -> Any:
return "-"


def get_key_from_tuple_by_value(choices: tuple, value) -> Any:
def get_key_from_tuple_by_value(choices: tuple, value) -> Any: # noqa: ANN401
"""
Fetches the tuple key by a given value
Useful for getting the key of a value from a model choice tuple of tuples.
Usage: project_type_a_name = get_value_from_tuple_by_key(PROJECT_TYPE_CHOICES, 'Budget-Project')
"""
try:
return [x[0] for x in choices if x[1] == value][0]
return [x[0] for x in choices if x[1] == value][0] # noqa: RUF015
except IndexError:
return "-"
Loading

0 comments on commit 8a183b7

Please sign in to comment.