Skip to content

Commit

Permalink
Use ruff for formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
monk-time committed May 27, 2024
1 parent 820bcd0 commit 4204383
Show file tree
Hide file tree
Showing 28 changed files with 771 additions and 607 deletions.
10 changes: 5 additions & 5 deletions .github/workflows/django.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,22 @@ jobs:
strategy:
max-parallel: 4
matrix:
python-version: [3.9]
python-version: [3.12]

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install Dependencies
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Migrate
run: |
cd api_yamdb
python manage.py migrate
- name: Run Tests
- name: Run tests
run: |
pytest -qq --tb=line
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
Полная документация к API находится в файле `api_yamdb/static/redoc.yaml` и по эндпоинту `/redoc/`.

### Используемые технологии
- Python 3.9
- Python 3.12
- Django
- DRF
- djangorestframework-simplejwt
Expand Down
6 changes: 3 additions & 3 deletions api_yamdb/api/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@


class TitleFilter(FilterSet):
category = CharFilter(field_name="category__slug")
genre = CharFilter(field_name="genre__slug")
category = CharFilter(field_name='category__slug')
genre = CharFilter(field_name='genre__slug')

class Meta:
model = Title
fields = ['category', 'genre', 'name', 'year']
fields = ('category', 'genre', 'name', 'year')
9 changes: 5 additions & 4 deletions api_yamdb/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class Meta:


class ReviewSerializer(serializers.ModelSerializer):
"""Сериализатор для Отзывов"""
"""Сериализатор для Отзывов."""

author = serializers.SlugRelatedField(
read_only=True,
Expand All @@ -71,14 +71,15 @@ def validate(self, attrs):
title_id = self.context['view'].kwargs['title_id']
title = get_object_or_404(Title, pk=title_id)
if title.reviews.filter(author=request.user).exists():
raise validators.ValidationError(
'Нельзя оставлять отзыв дважды на одно и тоже произвдение'
msg = (
'Нельзя оставлять отзыв дважды на одно и тоже произведение'
)
raise validators.ValidationError(msg)
return attrs


class CommentSerializer(serializers.ModelSerializer):
"""Сериализатор для Комментариев"""
"""Сериализатор для Комментариев."""

author = serializers.SlugRelatedField(
read_only=True,
Expand Down
18 changes: 9 additions & 9 deletions api_yamdb/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,24 @@


class TitleViewSet(ModelViewSet):
"""Вьюсет названия произведения"""
"""Вьюсет названия произведения."""

queryset = Title.objects.annotate(rating=Avg('reviews__score')).order_by(
'name'
)
permission_classes = (IsAdminOrReadOnly,)
filter_backends = (DjangoFilterBackend,)
filterset_class = TitleFilter
http_method_names = ['get', 'post', 'patch', 'delete']
http_method_names = ('get', 'post', 'patch', 'delete')

def get_serializer_class(self):
if self.request.method in ('POST', 'PATCH'):
if self.request.method in {'POST', 'PATCH'}:
return TitleWriteSerializer
return TitleReadSerializer


class GenreViewSet(ListCreateDestroyMixin, GenericViewSet):
"""Вьюсет жанра произведения"""
"""Вьюсет жанра произведения."""

queryset = Genre.objects.all()
serializer_class = GenreSerializer
Expand All @@ -49,7 +49,7 @@ class GenreViewSet(ListCreateDestroyMixin, GenericViewSet):


class CategoryViewSet(ListCreateDestroyMixin, GenericViewSet):
"""Вьюсет категории произведения"""
"""Вьюсет категории произведения."""

queryset = Category.objects.all()
serializer_class = CategorySerializer
Expand All @@ -61,11 +61,11 @@ class CategoryViewSet(ListCreateDestroyMixin, GenericViewSet):


class ReviewViewSet(ModelViewSet):
"""Вьюсет для отзывов"""
"""Вьюсет для отзывов."""

serializer_class = ReviewSerializer
permission_classes = (IsStaffOrAuthorOrReadOnly,)
http_method_names = ['get', 'post', 'patch', 'delete']
http_method_names = ('get', 'post', 'patch', 'delete')

def get_title(self):
return get_object_or_404(Title, pk=self.kwargs['title_id'])
Expand All @@ -80,11 +80,11 @@ def perform_create(self, serializer):


class CommentViewSet(ModelViewSet):
"""Вьюсет для комментариев"""
"""Вьюсет для комментариев."""

serializer_class = CommentSerializer
permission_classes = (IsStaffOrAuthorOrReadOnly,)
http_method_names = ['get', 'post', 'patch', 'delete']
http_method_names = ('get', 'post', 'patch', 'delete')

def get_review(self):
return get_object_or_404(
Expand Down
10 changes: 6 additions & 4 deletions api_yamdb/manage.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""

import os
import sys

Expand All @@ -9,11 +10,12 @@ def main():
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
msg = (
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
'available on your PYTHONPATH environment variable? Did you '
'forget to activate a virtual environment?'
)
raise ImportError(msg) from exc
execute_from_command_line(sys.argv)


Expand Down
7 changes: 4 additions & 3 deletions api_yamdb/reviews/management/commands/load.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,14 @@ def mapped(self, row: dict) -> dict:

def load(self):
try:
with open(BASE_DIR / self.filename, encoding='utf-8') as csvfile:
with (BASE_DIR / self.filename).open(encoding='utf-8') as csvfile:
reader = csv.DictReader(csvfile)
self.model.objects.bulk_create(
self.model(**self.mapped(row)) for row in reader
)
except FileNotFoundError:
raise CommandError(f'File {self.filename} not found')
except FileNotFoundError as e:
msg = f'File {self.filename} not found'
raise CommandError(msg) from e


CATEGORY_MAPPING = {'category': 'category_id'}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Generated by Django 5.0.6 on 2024-05-27 11:38

import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('reviews', '0003_auto_20230514_0000'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.AlterField(
model_name='comment',
name='author',
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name='%(class)ss',
to=settings.AUTH_USER_MODEL,
verbose_name='Автор',
),
),
migrations.AlterField(
model_name='review',
name='author',
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name='%(class)ss',
to=settings.AUTH_USER_MODEL,
verbose_name='Автор',
),
),
]
14 changes: 7 additions & 7 deletions api_yamdb/reviews/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@


class Genre(models.Model):
"""Модель жанра произведения"""
"""Модель жанра произведения."""

name = models.CharField(max_length=GENRE_MAX_LENGTH, verbose_name='Жанр')
slug = models.SlugField(unique=True)
Expand All @@ -29,7 +29,7 @@ def __str__(self):


class Category(models.Model):
"""Модель категории произведения"""
"""Модель категории произведения."""

name = models.CharField(
max_length=CATEGORY_MAX_LENGTH,
Expand All @@ -47,7 +47,7 @@ def __str__(self):


class Title(models.Model):
"""Модель произведения"""
"""Модель произведения."""

name = models.CharField(
max_length=TITLE_MAX_LENGTH, verbose_name='Название'
Expand Down Expand Up @@ -95,7 +95,7 @@ class Meta:


class Review(AbstractPost):
"""Модель Отзывов на произведения"""
"""Модель Отзывов на произведения."""

title = models.ForeignKey(
Title,
Expand All @@ -115,19 +115,19 @@ class Meta:
ordering = ('-pub_date',)
verbose_name = 'Отзыв'
verbose_name_plural = 'Отзывы'
constraints = [
constraints = (
models.UniqueConstraint(
name='unique_author_title',
fields=['author', 'title'],
),
]
)

def __str__(self):
return self.text[:STR_LENGTH]


class Comment(AbstractPost):
"""Модель Комментариев к отзывам"""
"""Модель Комментариев к отзывам."""

review = models.ForeignKey(
Review,
Expand Down
3 changes: 2 additions & 1 deletion api_yamdb/reviews/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

def validate_year(year):
if year > timezone.now().year:
raise ValidationError(
msg = (
'Некорретный год публикации произведения.'
f'{year} больше текущего года'
)
raise ValidationError(msg)
4 changes: 2 additions & 2 deletions api_yamdb/users/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ class User(AbstractUser):
USER = 'user'
MODERATOR = 'moderator'
ADMIN = 'admin'
ROLE_CHOICES = [
ROLE_CHOICES = (
(USER, 'Пользователь'),
(MODERATOR, 'Модератор'),
(ADMIN, 'Администратор'),
]
)

username = models.CharField(
'Имя пользователя',
Expand Down
3 changes: 2 additions & 1 deletion api_yamdb/users/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@

def validate_username_not_me(value):
if value.lower() == 'me':
raise ValidationError('Имя пользователя "me" использовать нельзя')
msg = 'Имя пользователя "me" использовать нельзя'
raise ValidationError(msg)
2 changes: 1 addition & 1 deletion api_yamdb/users/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def post(self, request):

if not default_token_generator.check_token(user, confirmation_code):
return Response(
{"confirmation_code": ["Код подтверждения неверный"]},
{'confirmation_code': ['Код подтверждения неверный']},
status=status.HTTP_400_BAD_REQUEST,
)

Expand Down
75 changes: 66 additions & 9 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,67 @@
[tool.black]
[tool.pytest.ini_options]
pythonpath = ['api_yamdb']
DJANGO_SETTINGS_MODULE = 'api_yamdb.settings'
addopts = '-vv -p no:cacheprovider'
testpaths = ['tests/']
python_files = ['test_*.py']
disable_test_id_escaping_and_forfeit_all_rights_to_community_support = true

[tool.ruff]
target-version = "py312"
line-length = 79
skip-string-normalization = true
extend-exclude = 'tests|migrations'

[tool.isort]
profile = "black"
line_length = 79
src_paths = ["api_yamdb"]
extend_skip = ["tests", "migrations"]
preview = true
output-format = "concise" # preview mode switches this to full
exclude = ["migrations"]
src = ["api_yamdb"]

[tool.ruff.lint]
select = ["ALL"]
ignore = [
"D1", # pydocstyle (allow missing docstrings)
"ANN", # flake8-annotations (demands type annotations)
"S", # flake8-bandit (security testing)
"COM", # flake8-commas (conflicts with formatter)
"CPY", # flake8-copyright (demands copyright notices)
"Q", # flake8-quotes (conflicts with formatter)
"T20", # flake8-print (prohibits print statements)
"ISC001", # single-line-implicit-string-concatenation (conflicts with formatter)
"PGH003", # blanket-type-ignore (PyLance doesn't provide error codes)
"B905", # zip-without-explicit-strict (makes zip too bulky)
"E731", # lambda-assignment (precludes a concise functional style)
"PLC0415", # import-outside-top-level (sometimes imports in a function are necessary)
"PLR2004", # magic-value-comparison (demands too many constants)
"TD003", # missing-todo-link (too cumbersome)
"G004", # logging-f-string (pointless micro-optimization in most cases)
"PLR6301", # no-self-use (django: method overrides often don't use self)
"ARG002", # unused-method-argument (django: method overrides often have unused arguments)
"TID252", # relative-imports (django: apps are more portable with relative imports)
]
allowed-confusables = [
"а", "б", "в", "г", "е", "з", "и", "к", "м", "н", "о", "р", "с", "у", "ф", "х",
"А", "Б", "В", "Г", "Е", "З", "И", "К", "М", "Н", "О", "Р", "С", "У", "Ф", "Х",
]

[tool.ruff.lint.per-file-ignores]
"tests/*" = [
"N802", # invalid-function-name (common in test methods)
"PLR6301", # no-self-use (common in test methods)
"ARG002", # unused-method-argument (common with fixtures)
"BLE001", # blind-except (common in tests)
"PLR0913", # too-many-arguments (common with fixtures)
"PLR0917", # too-many-positional (common with fixtures)
"RUF012", # mutable-class-default (common in test classes)
]

[tool.ruff.lint.pycodestyle]
max-line-length = 100 # don't report (E501) lines of length 80..100 that cannot be split by autoformatter

[tool.ruff.lint.pydocstyle]
convention = "pep257"

[tool.ruff.lint.flake8-pytest-style]
fixture-parentheses = false
parametrize-names-type = "csv"
parametrize-values-type = "tuple"

[tool.ruff.format]
quote-style = "single"
Loading

0 comments on commit 4204383

Please sign in to comment.