update ruff rules

This commit is contained in:
Gabriel Augendre 2023-03-02 13:10:25 +01:00
parent 963c0fdf69
commit f30b8ec975
31 changed files with 125 additions and 106 deletions

View file

@ -25,11 +25,6 @@ repos:
- --markdown-linebreak-ext=md - --markdown-linebreak-ext=md
- id: check-executables-have-shebangs - id: check-executables-have-shebangs
- id: check-shebang-scripts-are-executable - id: check-shebang-scripts-are-executable
- repo: https://github.com/asottile/pyupgrade
rev: v3.3.1
hooks:
- id: pyupgrade
args: [--py311-plus]
- repo: https://github.com/adamchainz/django-upgrade - repo: https://github.com/adamchainz/django-upgrade
rev: 1.13.0 rev: 1.13.0
hooks: hooks:

View file

@ -36,57 +36,18 @@ ignore_missing_imports = true
[tool.ruff] [tool.ruff]
src = ["src"] src = ["src"]
target-version = "py311" target-version = "py311"
select = [ select = ["ALL"]
"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"] unfixable = ["T20", "RUF001", "RUF002", "RUF003"]
ignore = [ ignore = [
"UP", # pyupgrade
"YTT", # flake8-2020
"ANN", # flake8-annotations "ANN", # flake8-annotations
"BLE", # flake8-blind-except "BLE", # flake8-blind-except
"COM", # flake8-commas
"EM", # flake8-errmsg
"Q", # flake8-quotes
"TCH", # flake8-type-checking / TODO: revisit later ? "TCH", # flake8-type-checking / TODO: revisit later ?
"E501", # long lines "E501", # long lines
"D1", # missing docstring "D1", # missing docstring
"TRY003", # Avoid specifying long messages outside the exception class "TRY003", # Avoid specifying long messages outside the exception class
] ]
[tool.ruff.per-file-ignores] [tool.ruff.per-file-ignores]
"**/tests/*" = [ "**/tests/*" = [
"S101", # Use of assert detected. "S101", # Use of assert detected.

View file

@ -48,7 +48,7 @@ class ArticleAdmin(admin.ModelAdmin):
("created_at", "updated_at"), ("created_at", "updated_at"),
("views_count",), ("views_count",),
("has_code", "has_custom_css"), ("has_code", "has_custom_css"),
] ],
}, },
), ),
( (
@ -116,11 +116,13 @@ class ArticleAdmin(admin.ModelAdmin):
"all": ( "all": (
"vendor/fonts/fira-code.css", "vendor/fonts/fira-code.css",
"admin_articles.css", "admin_articles.css",
) ),
} }
def response_post_save_add( def response_post_save_add(
self, request: WSGIRequest, obj: Article self,
request: WSGIRequest,
obj: Article,
) -> HttpResponseRedirect: ) -> HttpResponseRedirect:
if "_preview" in request.POST: if "_preview" in request.POST:
return cast(HttpResponseRedirect, redirect("article-detail", slug=obj.slug)) return cast(HttpResponseRedirect, redirect("article-detail", slug=obj.slug))

View file

@ -27,7 +27,9 @@ class LazyImageReferenceInlineProcessor(ImageReferenceInlineProcessor):
class LazyLoadingImageExtension(Extension): class LazyLoadingImageExtension(Extension):
def extendMarkdown(self, md: Markdown) -> None: # noqa: N802 def extendMarkdown(self, md: Markdown) -> None: # noqa: N802
md.inlinePatterns.register( md.inlinePatterns.register(
LazyImageInlineProcessor(IMAGE_LINK_RE, md), "image_link", 150 LazyImageInlineProcessor(IMAGE_LINK_RE, md),
"image_link",
150,
) )
md.inlinePatterns.register( md.inlinePatterns.register(
LazyImageReferenceInlineProcessor(IMAGE_REFERENCE_RE, md), LazyImageReferenceInlineProcessor(IMAGE_REFERENCE_RE, md),

View file

@ -30,7 +30,9 @@ class Migration(migrations.Migration):
( (
"last_login", "last_login",
models.DateTimeField( models.DateTimeField(
blank=True, null=True, verbose_name="last login" blank=True,
null=True,
verbose_name="last login",
), ),
), ),
( (
@ -45,13 +47,13 @@ class Migration(migrations.Migration):
"username", "username",
models.CharField( models.CharField(
error_messages={ 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.", help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.",
max_length=150, max_length=150,
unique=True, unique=True,
validators=[ validators=[
django.contrib.auth.validators.UnicodeUsernameValidator() django.contrib.auth.validators.UnicodeUsernameValidator(),
], ],
verbose_name="username", verbose_name="username",
), ),
@ -59,19 +61,25 @@ class Migration(migrations.Migration):
( (
"first_name", "first_name",
models.CharField( models.CharField(
blank=True, max_length=150, verbose_name="first name" blank=True,
max_length=150,
verbose_name="first name",
), ),
), ),
( (
"last_name", "last_name",
models.CharField( models.CharField(
blank=True, max_length=150, verbose_name="last name" blank=True,
max_length=150,
verbose_name="last name",
), ),
), ),
( (
"email", "email",
models.EmailField( models.EmailField(
blank=True, max_length=254, verbose_name="email address" blank=True,
max_length=254,
verbose_name="email address",
), ),
), ),
( (
@ -93,7 +101,8 @@ class Migration(migrations.Migration):
( (
"date_joined", "date_joined",
models.DateTimeField( models.DateTimeField(
default=django.utils.timezone.now, verbose_name="date joined" default=django.utils.timezone.now,
verbose_name="date joined",
), ),
), ),
( (

View file

@ -10,9 +10,12 @@ class Migration(migrations.Migration):
operations = [ operations = [
migrations.AlterModelOptions( migrations.AlterModelOptions(
name="page", options={"ordering": ["position", "-published_at"]} name="page",
options={"ordering": ["position", "-published_at"]},
), ),
migrations.AddField( migrations.AddField(
model_name="page", name="position", field=models.IntegerField(default=0) model_name="page",
name="position",
field=models.IntegerField(default=0),
), ),
] ]

View file

@ -25,7 +25,8 @@ class Migration(migrations.Migration):
( (
"username", "username",
models.CharField( models.CharField(
help_text="Will be displayed with your comment.", max_length=255 help_text="Will be displayed with your comment.",
max_length=255,
), ),
), ),
( (

View file

@ -10,7 +10,8 @@ class Migration(migrations.Migration):
operations = [ operations = [
migrations.AlterModelOptions( migrations.AlterModelOptions(
name="comment", options={"ordering": ["-created_at"]} name="comment",
options={"ordering": ["-created_at"]},
), ),
migrations.AddField( migrations.AddField(
model_name="comment", model_name="comment",

View file

@ -10,6 +10,8 @@ class Migration(migrations.Migration):
operations = [ operations = [
migrations.RenameField( migrations.RenameField(
model_name="comment", old_name="active", new_name="approved" model_name="comment",
old_name="active",
new_name="approved",
), ),
] ]

View file

@ -10,7 +10,8 @@ class Migration(migrations.Migration):
operations = [ operations = [
migrations.AlterModelOptions( migrations.AlterModelOptions(
name="comment", options={"ordering": ["created_at"]} name="comment",
options={"ordering": ["created_at"]},
), ),
migrations.AlterField( migrations.AlterField(
model_name="comment", model_name="comment",

View file

@ -10,6 +10,7 @@ class Migration(migrations.Migration):
operations = [ operations = [
migrations.AlterModelOptions( migrations.AlterModelOptions(
name="comment", options={"ordering": ["-created_at"]} name="comment",
options={"ordering": ["-created_at"]},
), ),
] ]

View file

@ -10,6 +10,7 @@ class Migration(migrations.Migration):
operations = [ operations = [
migrations.AlterModelOptions( migrations.AlterModelOptions(
name="comment", options={"ordering": ["created_at"]} name="comment",
options={"ordering": ["created_at"]},
), ),
] ]

View file

@ -13,7 +13,7 @@ def forwards(apps: Apps, schema_editor: BaseDatabaseSchemaEditor) -> None:
tags = [] tags = []
keyword: str keyword: str
for keyword in list( for keyword in list(
filter(None, (keyword.strip() for keyword in article.keywords.split(","))) filter(None, (keyword.strip() for keyword in article.keywords.split(","))),
): ):
tag = Tag.objects.using(db_alias).filter(name__iexact=keyword).first() tag = Tag.objects.using(db_alias).filter(name__iexact=keyword).first()
if tag is None: if tag is None:

View file

@ -13,7 +13,7 @@ class Migration(migrations.Migration):
model_name="article", model_name="article",
name="content", name="content",
field=models.TextField( field=models.TextField(
default='!!! warning "Draft"\n This article is still a draft. It may appear by error in your feed if I click on the "publish" button too early 😊' default='!!! warning "Draft"\n This article is still a draft. It may appear by error in your feed if I click on the "publish" button too early 😊',
), ),
), ),
] ]

View file

@ -13,7 +13,9 @@ class Migration(migrations.Migration):
model_name="article", model_name="article",
name="tags", name="tags",
field=models.ManyToManyField( field=models.ManyToManyField(
blank=True, related_name="articles", to="articles.Tag" blank=True,
related_name="articles",
to="articles.Tag",
), ),
), ),
] ]

View file

@ -6,7 +6,8 @@ from django.urls import reverse
def replace_with_wrapper_url( def replace_with_wrapper_url(
apps: Apps, schema_editor: BaseDatabaseSchemaEditor apps: Apps,
schema_editor: BaseDatabaseSchemaEditor,
) -> None: ) -> None:
Attachment = apps.get_model("attachments", "Attachment") Attachment = apps.get_model("attachments", "Attachment")
Article = apps.get_model("articles", "Article") Article = apps.get_model("articles", "Article")

View file

@ -140,10 +140,10 @@ class Article(models.Model):
def get_related_articles(self) -> Sequence[Article]: def get_related_articles(self) -> Sequence[Article]:
related_articles = set() related_articles = set()
published_articles = Article.objects.filter(status=Article.PUBLISHED).exclude( published_articles = Article.objects.filter(status=Article.PUBLISHED).exclude(
pk=self.pk pk=self.pk,
) )
for tag in self.tags.all().prefetch_related( for tag in self.tags.all().prefetch_related(
Prefetch("articles", published_articles, to_attr="published_articles") Prefetch("articles", published_articles, to_attr="published_articles"),
): ):
related_articles.update(tag.published_articles) related_articles.update(tag.published_articles)
sample_size = min([len(related_articles), 3]) sample_size = min([len(related_articles), 3])

View file

@ -9,7 +9,8 @@ from articles.utils import format_article_content
@pytest.mark.django_db() @pytest.mark.django_db()
def test_unauthenticated_render_redirects( def test_unauthenticated_render_redirects(
published_article: Article, client: Client published_article: Article,
client: Client,
) -> None: ) -> None:
api_res = client.post( api_res = client.post(
reverse("api-render-article", kwargs={"article_pk": published_article.pk}), reverse("api-render-article", kwargs={"article_pk": published_article.pk}),
@ -20,12 +21,13 @@ def test_unauthenticated_render_redirects(
@pytest.mark.django_db() @pytest.mark.django_db()
def test_render_article_same_content( def test_render_article_same_content(
published_article: Article, client: Client published_article: Article,
client: Client,
) -> None: ) -> None:
client.force_login(published_article.author) client.force_login(published_article.author)
api_res = post_article(client, published_article, published_article.content) api_res = post_article(client, published_article, published_article.content)
standard_res = client.get( standard_res = client.get(
reverse("article-detail", kwargs={"slug": published_article.slug}) reverse("article-detail", kwargs={"slug": published_article.slug}),
) )
assert api_res.status_code == 200 assert api_res.status_code == 200
assert standard_res.status_code == 200 assert standard_res.status_code == 200
@ -42,7 +44,8 @@ def test_render_article_same_content(
@pytest.mark.django_db() @pytest.mark.django_db()
def test_render_article_change_content( def test_render_article_change_content(
published_article: Article, client: Client published_article: Article,
client: Client,
) -> None: ) -> None:
client.force_login(published_article.author) client.force_login(published_article.author)
preview_content = "This is a different content **with strong emphasis**" preview_content = "This is a different content **with strong emphasis**"
@ -80,7 +83,7 @@ def post_article(client: Client, article: Article, content: str) -> HttpResponse
data={ data={
"content": content, "content": content,
"tag_ids": ",".join( "tag_ids": ",".join(
map(str, article.tags.all().values_list("pk", flat=True)) map(str, article.tags.all().values_list("pk", flat=True)),
), ),
}, },
) )

View file

@ -57,28 +57,32 @@ def _assert_article_is_rendered(item: Article, res: HttpResponse) -> None:
@pytest.mark.django_db() @pytest.mark.django_db()
def test_anonymous_cant_access_draft_detail( def test_anonymous_cant_access_draft_detail(
client: Client, unpublished_article: Article client: Client,
unpublished_article: Article,
) -> None: ) -> None:
res = client.get( res = client.get(
reverse("article-detail", kwargs={"slug": unpublished_article.slug}) reverse("article-detail", kwargs={"slug": unpublished_article.slug}),
) )
assert res.status_code == 404 assert res.status_code == 404
@pytest.mark.django_db() @pytest.mark.django_db()
def test_anonymous_can_access_draft_detail_with_key( def test_anonymous_can_access_draft_detail_with_key(
client: Client, unpublished_article: Article client: Client,
unpublished_article: Article,
) -> None: ) -> None:
res = client.get( res = client.get(
reverse("article-detail", kwargs={"slug": unpublished_article.slug}) reverse("article-detail", kwargs={"slug": unpublished_article.slug})
+ f"?draft_key={unpublished_article.draft_key}" + f"?draft_key={unpublished_article.draft_key}",
) )
_assert_article_is_rendered(unpublished_article, res) _assert_article_is_rendered(unpublished_article, res)
@pytest.mark.django_db() @pytest.mark.django_db()
def test_user_can_access_draft_detail( def test_user_can_access_draft_detail(
client: Client, author: User, unpublished_article: Article client: Client,
author: User,
unpublished_article: Article,
) -> None: ) -> None:
client.force_login(author) client.force_login(author)
_test_access_article_by_slug(client, unpublished_article) _test_access_article_by_slug(client, unpublished_article)
@ -86,7 +90,8 @@ def test_user_can_access_draft_detail(
@pytest.mark.django_db() @pytest.mark.django_db()
def test_anonymous_cant_access_drafts_list( def test_anonymous_cant_access_drafts_list(
client: Client, unpublished_article: Article client: Client,
unpublished_article: Article,
) -> None: ) -> None:
res = client.get(reverse("drafts-list")) res = client.get(reverse("drafts-list"))
assert res.status_code == 302 assert res.status_code == 302
@ -94,7 +99,9 @@ def test_anonymous_cant_access_drafts_list(
@pytest.mark.django_db() @pytest.mark.django_db()
def test_user_can_access_drafts_list( def test_user_can_access_drafts_list(
client: Client, author: User, unpublished_article: Article client: Client,
author: User,
unpublished_article: Article,
) -> None: ) -> None:
client.force_login(author) client.force_login(author)
res = client.get(reverse("drafts-list")) res = client.get(reverse("drafts-list"))
@ -114,7 +121,8 @@ def test_has_goatcounter_if_set(client: Client, settings: SettingsWrapper) -> No
@pytest.mark.django_db() @pytest.mark.django_db()
def test_doesnt_have_goatcounter_if_unset( def test_doesnt_have_goatcounter_if_unset(
client: Client, settings: SettingsWrapper client: Client,
settings: SettingsWrapper,
) -> None: ) -> None:
settings.GOATCOUNTER_DOMAIN = None settings.GOATCOUNTER_DOMAIN = None
res = client.get(reverse("articles-list")) res = client.get(reverse("articles-list"))
@ -125,7 +133,9 @@ def test_doesnt_have_goatcounter_if_unset(
@pytest.mark.django_db() @pytest.mark.django_db()
def test_logged_in_user_doesnt_have_goatcounter( def test_logged_in_user_doesnt_have_goatcounter(
client: Client, author: User, settings: SettingsWrapper client: Client,
author: User,
settings: SettingsWrapper,
) -> None: ) -> None:
client.force_login(author) client.force_login(author)
settings.GOATCOUNTER_DOMAIN = "gc.gabnotes.org" settings.GOATCOUNTER_DOMAIN = "gc.gabnotes.org"

View file

@ -25,7 +25,7 @@ def test_unpublish_article(published_article: Article) -> None:
def test_save_article_adds_missing_slug(author: User) -> None: def test_save_article_adds_missing_slug(author: User) -> None:
# Explicitly calling bulk_create with one article because it doesn't call save(). # Explicitly calling bulk_create with one article because it doesn't call save().
articles = Article.objects.bulk_create( articles = Article.objects.bulk_create(
[Article(author=author, title="noice title", slug="", status=Article.DRAFT)] [Article(author=author, title="noice title", slug="", status=Article.DRAFT)],
) )
article = articles[0] article = articles[0]
assert article.slug == "" assert article.slug == ""

View file

@ -10,7 +10,9 @@ urlpatterns = [
path("tag/<slug:slug>/", views.TagArticlesListView.as_view(), name="tag"), path("tag/<slug:slug>/", views.TagArticlesListView.as_view(), name="tag"),
path("feed/", views.CompleteFeed(), name="complete-feed"), path("feed/", views.CompleteFeed(), name="complete-feed"),
path( path(
"api/render/<int:article_pk>/", views.render_article, name="api-render-article" "api/render/<int:article_pk>/",
views.render_article,
name="api-render-article",
), ),
path("<slug:slug>/", views.view_article, name="article-detail"), path("<slug:slug>/", views.view_article, name="article-detail"),
] ]

View file

@ -24,7 +24,7 @@ def format_article_content(content: str) -> str:
TocExtension(anchorlink=True), TocExtension(anchorlink=True),
CodeHiliteExtension(linenums=False, guess_lang=False), CodeHiliteExtension(linenums=False, guess_lang=False),
LazyLoadingImageExtension(), LazyLoadingImageExtension(),
] ],
) )
content = re.sub(r"(\s)#(\w+)", r"\1\#\2", content) content = re.sub(r"(\s)#(\w+)", r"\1\#\2", content)
return md.convert(content) return md.convert(content)

View file

@ -22,7 +22,7 @@ class BaseFeed(Feed):
def _get_queryset(self) -> QuerySet[Article]: def _get_queryset(self) -> QuerySet[Article]:
return Article.objects.filter(status=Article.PUBLISHED).order_by( return Article.objects.filter(status=Article.PUBLISHED).order_by(
"-published_at" "-published_at",
) )

View file

@ -31,7 +31,7 @@ class BaseArticleListView(generic.ListView):
context["next_page_querystring"] = querystring context["next_page_querystring"] = querystring
if page_obj.has_previous(): if page_obj.has_previous():
querystring = self.build_querystring( querystring = self.build_querystring(
{"page": page_obj.previous_page_number()} {"page": page_obj.previous_page_number()},
) )
context["previous_page_querystring"] = querystring context["previous_page_querystring"] = querystring
return context return context
@ -55,7 +55,8 @@ class ArticlesListView(PublicArticleListView):
def get_context_data(self, **kwargs: Any) -> dict[str, Any]: def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
home_article = Article.objects.filter( home_article = Article.objects.filter(
status=Article.PUBLISHED, is_home=True status=Article.PUBLISHED,
is_home=True,
).first() ).first()
context["article"] = home_article context["article"] = home_article
return context return context
@ -80,11 +81,13 @@ class SearchArticlesListView(PublicArticleListView):
return queryset.filter( return queryset.filter(
reduce(operator.and_, (Q(title__icontains=term) for term in search_terms)) reduce(operator.and_, (Q(title__icontains=term) for term in search_terms))
| reduce( | reduce(
operator.and_, (Q(content__icontains=term) for term in search_terms) operator.and_,
(Q(content__icontains=term) for term in search_terms),
) )
| reduce( | reduce(
operator.and_, (Q(tags__name__icontains=term) for term in search_terms) operator.and_,
) (Q(tags__name__icontains=term) for term in search_terms),
),
).distinct() ).distinct()
def get_additional_querystring_params(self) -> dict[str, str]: def get_additional_querystring_params(self) -> dict[str, str]:
@ -100,7 +103,10 @@ class TagArticlesListView(PublicArticleListView):
html_title = "" html_title = ""
def dispatch( def dispatch(
self, request: WSGIRequest, *args: Any, **kwargs: Any self,
request: WSGIRequest,
*args: Any,
**kwargs: Any,
) -> HttpResponseBase: ) -> HttpResponseBase:
self.tag = get_object_or_404(Tag, slug=self.kwargs.get("slug")) self.tag = get_object_or_404(Tag, slug=self.kwargs.get("slug"))
self.main_title = self.html_title = f"{self.tag.name} articles" self.main_title = self.html_title = f"{self.tag.name} articles"

View file

@ -67,7 +67,9 @@ class AttachmentAdmin(admin.ModelAdmin):
@admin.action(description="Reprocess selected attachments") @admin.action(description="Reprocess selected attachments")
def reprocess_selected_attachments( def reprocess_selected_attachments(
self, request: WSGIRequest, queryset: QuerySet self,
request: WSGIRequest,
queryset: QuerySet,
) -> None: ) -> None:
if len(queryset) == 0: if len(queryset) == 0:
messages.error(request, "You must select at least one attachment") messages.error(request, "You must select at least one attachment")

View file

@ -20,7 +20,9 @@ class Migration(migrations.Migration):
model_name="attachment", model_name="attachment",
name="processed_file", name="processed_file",
field=attachments.models.AbsoluteUrlFileField( field=attachments.models.AbsoluteUrlFileField(
blank=True, null=True, upload_to="" blank=True,
null=True,
upload_to="",
), ),
), ),
] ]

View file

@ -83,7 +83,7 @@ class Attachment(models.Model):
"resize_height": settings.SHORTPIXEL_RESIZE_HEIGHT, "resize_height": settings.SHORTPIXEL_RESIZE_HEIGHT,
"keep_exif": 1, "keep_exif": 1,
"file_paths": json.dumps( "file_paths": json.dumps(
[f"{self.original_file.name}:{self.original_file.path}"] [f"{self.original_file.name}:{self.original_file.path}"],
), ),
} }
data = {**base_data, **post_data} data = {**base_data, **post_data}

View file

@ -151,7 +151,7 @@ CACHES = {
"default": { "default": {
"BACKEND": "django.core.cache.backends.db.DatabaseCache", "BACKEND": "django.core.cache.backends.db.DatabaseCache",
"LOCATION": "cache", "LOCATION": "cache",
} },
} }
# Database # Database
@ -169,7 +169,7 @@ INTERNAL_IPS = [
AUTH_PASSWORD_VALIDATORS = [ 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.MinimumLengthValidator"},
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"}, {"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},

View file

@ -26,7 +26,8 @@ urlpatterns = [
path( path(
"robots.txt", "robots.txt",
TemplateView.as_view( TemplateView.as_view(
template_name="blog/robots.txt", content_type="text/plain" template_name="blog/robots.txt",
content_type="text/plain",
), ),
), ),
path("admin/", admin.site.urls), path("admin/", admin.site.urls),

View file

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

View file

@ -16,7 +16,10 @@ def update_dependencies(ctx: Context, *, sync: bool = True) -> None:
@task @task
def compile_dependencies( def compile_dependencies(
ctx: Context, *, update: bool = False, sync: bool = False ctx: Context,
*,
update: bool = False,
sync: bool = False,
) -> None: ) -> None:
common_args = "-q --allow-unsafe --resolver=backtracking" common_args = "-q --allow-unsafe --resolver=backtracking"
if update: if update:
@ -85,7 +88,10 @@ def check(_ctx: Context) -> None:
def build(ctx: Context) -> None: def build(ctx: Context) -> None:
with ctx.cd(BASE_DIR): with ctx.cd(BASE_DIR):
ctx.run( ctx.run(
"docker-compose build django", pty=True, echo=True, env=COMPOSE_BUILD_ENV "docker-compose build django",
pty=True,
echo=True,
env=COMPOSE_BUILD_ENV,
) )
@ -93,7 +99,10 @@ def build(ctx: Context) -> None:
def publish(ctx: Context) -> None: def publish(ctx: Context) -> None:
with ctx.cd(BASE_DIR): with ctx.cd(BASE_DIR):
ctx.run( ctx.run(
"docker-compose push django", pty=True, echo=True, env=COMPOSE_BUILD_ENV "docker-compose push django",
pty=True,
echo=True,
env=COMPOSE_BUILD_ENV,
) )
@ -117,7 +126,8 @@ def check_alive(_ctx: Context) -> None:
else: else:
print("Server is up & running") # noqa: T201 print("Server is up & running") # noqa: T201
return return
raise RuntimeError("Failed to reach the server") from exception msg = "Failed to reach the server"
raise RuntimeError(msg) from exception
@task(pre=[check, build, publish, deploy], post=[check_alive]) @task(pre=[check, build, publish, deploy], post=[check_alive])