mirror of https://github.com/astral-sh/ruff
Add DJ015 for leading slashes as well
This commit is contained in:
parent
aa4228632a
commit
dd7037a042
|
|
@ -0,0 +1,48 @@
|
||||||
|
from django.urls import path
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
# Errors - leading slash
|
||||||
|
urlpatterns = [
|
||||||
|
path("/help/", views.help_view), # DJ015
|
||||||
|
path("/about/", views.about_view), # DJ015
|
||||||
|
path("/contact/", views.contact_view), # DJ015
|
||||||
|
path("/api/users/", views.users_view), # DJ015
|
||||||
|
path("/blog/posts/", views.posts_view), # DJ015
|
||||||
|
]
|
||||||
|
|
||||||
|
# OK - no leading slash
|
||||||
|
urlpatterns_ok = [
|
||||||
|
path("help/", views.help_view),
|
||||||
|
path("about/", views.about_view),
|
||||||
|
path("contact/", views.contact_view),
|
||||||
|
path("api/users/", views.users_view),
|
||||||
|
path("blog/posts/", views.posts_view),
|
||||||
|
]
|
||||||
|
|
||||||
|
# OK - just root path
|
||||||
|
urlpatterns_root = [
|
||||||
|
path("/", views.index_view),
|
||||||
|
path("", views.home_view),
|
||||||
|
]
|
||||||
|
|
||||||
|
# OK - with path parameters
|
||||||
|
urlpatterns_params = [
|
||||||
|
path("users/<int:id>/", views.user_detail),
|
||||||
|
path("posts/<slug:slug>/", views.post_detail),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Mixed cases
|
||||||
|
urlpatterns_mixed = [
|
||||||
|
path("good/", views.good_view),
|
||||||
|
path("/bad/", views.bad_view), # DJ015
|
||||||
|
path("also-good/", views.also_good_view),
|
||||||
|
path("/also-bad/", views.also_bad_view), # DJ015
|
||||||
|
]
|
||||||
|
|
||||||
|
# Edge cases with different quote styles
|
||||||
|
urlpatterns_quotes = [
|
||||||
|
path('/single-quote/', views.single_quote_view), # DJ015
|
||||||
|
path("/double-quote/", views.double_quote_view), # DJ015
|
||||||
|
path('''/triple-single/''', views.triple_single_view), # DJ015
|
||||||
|
path("""/triple-double/""", views.triple_double_view), # DJ015
|
||||||
|
]
|
||||||
|
|
@ -1184,6 +1184,9 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
|
||||||
if checker.is_rule_enabled(Rule::DjangoURLPathWithoutTrailingSlash) {
|
if checker.is_rule_enabled(Rule::DjangoURLPathWithoutTrailingSlash) {
|
||||||
flake8_django::rules::url_path_without_trailing_slash(checker, call);
|
flake8_django::rules::url_path_without_trailing_slash(checker, call);
|
||||||
}
|
}
|
||||||
|
if checker.is_rule_enabled(Rule::DjangoURLPathWithLeadingSlash) {
|
||||||
|
flake8_django::rules::url_path_with_leading_slash(checker, call);
|
||||||
|
}
|
||||||
if checker.is_rule_enabled(Rule::UnsupportedMethodCallOnAll) {
|
if checker.is_rule_enabled(Rule::UnsupportedMethodCallOnAll) {
|
||||||
flake8_pyi::rules::unsupported_method_call_on_all(checker, func);
|
flake8_pyi::rules::unsupported_method_call_on_all(checker, func);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1101,6 +1101,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||||
(Flake8Django, "012") => rules::flake8_django::rules::DjangoUnorderedBodyContentInModel,
|
(Flake8Django, "012") => rules::flake8_django::rules::DjangoUnorderedBodyContentInModel,
|
||||||
(Flake8Django, "013") => rules::flake8_django::rules::DjangoNonLeadingReceiverDecorator,
|
(Flake8Django, "013") => rules::flake8_django::rules::DjangoNonLeadingReceiverDecorator,
|
||||||
(Flake8Django, "014") => rules::flake8_django::rules::DjangoURLPathWithoutTrailingSlash,
|
(Flake8Django, "014") => rules::flake8_django::rules::DjangoURLPathWithoutTrailingSlash,
|
||||||
|
(Flake8Django, "015") => rules::flake8_django::rules::DjangoURLPathWithLeadingSlash,
|
||||||
|
|
||||||
// flynt
|
// flynt
|
||||||
// Reserved: (Flynt, "001") => Rule: :StringConcatenationToFString,
|
// Reserved: (Flynt, "001") => Rule: :StringConcatenationToFString,
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ mod tests {
|
||||||
#[test_case(Rule::DjangoAllWithModelForm, Path::new("DJ007.py"))]
|
#[test_case(Rule::DjangoAllWithModelForm, Path::new("DJ007.py"))]
|
||||||
#[test_case(Rule::DjangoModelWithoutDunderStr, Path::new("DJ008.py"))]
|
#[test_case(Rule::DjangoModelWithoutDunderStr, Path::new("DJ008.py"))]
|
||||||
#[test_case(Rule::DjangoURLPathWithoutTrailingSlash, Path::new("DJ014.py"))]
|
#[test_case(Rule::DjangoURLPathWithoutTrailingSlash, Path::new("DJ014.py"))]
|
||||||
|
#[test_case(Rule::DjangoURLPathWithLeadingSlash, Path::new("DJ015.py"))]
|
||||||
#[test_case(Rule::DjangoUnorderedBodyContentInModel, Path::new("DJ012.py"))]
|
#[test_case(Rule::DjangoUnorderedBodyContentInModel, Path::new("DJ012.py"))]
|
||||||
#[test_case(Rule::DjangoNonLeadingReceiverDecorator, Path::new("DJ013.py"))]
|
#[test_case(Rule::DjangoNonLeadingReceiverDecorator, Path::new("DJ013.py"))]
|
||||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ pub(crate) use model_without_dunder_str::*;
|
||||||
pub(crate) use non_leading_receiver_decorator::*;
|
pub(crate) use non_leading_receiver_decorator::*;
|
||||||
pub(crate) use nullable_model_string_field::*;
|
pub(crate) use nullable_model_string_field::*;
|
||||||
pub(crate) use unordered_body_content_in_model::*;
|
pub(crate) use unordered_body_content_in_model::*;
|
||||||
|
pub(crate) use url_path_with_leading_slash::*;
|
||||||
pub(crate) use url_path_without_trailing_slash::*;
|
pub(crate) use url_path_without_trailing_slash::*;
|
||||||
|
|
||||||
mod all_with_model_form;
|
mod all_with_model_form;
|
||||||
|
|
@ -14,4 +15,5 @@ mod model_without_dunder_str;
|
||||||
mod non_leading_receiver_decorator;
|
mod non_leading_receiver_decorator;
|
||||||
mod nullable_model_string_field;
|
mod nullable_model_string_field;
|
||||||
mod unordered_body_content_in_model;
|
mod unordered_body_content_in_model;
|
||||||
|
mod url_path_with_leading_slash;
|
||||||
mod url_path_without_trailing_slash;
|
mod url_path_without_trailing_slash;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
|
use ruff_python_semantic::Modules;
|
||||||
|
use ruff_text_size::{Ranged, TextSize};
|
||||||
|
|
||||||
|
use crate::checkers::ast::Checker;
|
||||||
|
use crate::{AlwaysFixableViolation, Edit, Fix};
|
||||||
|
|
||||||
|
/// ## What it does
|
||||||
|
/// Checks that all Django URL route definitions using `django.urls.path()`
|
||||||
|
/// do not start with a leading slash.
|
||||||
|
///
|
||||||
|
/// ## Why is this bad?
|
||||||
|
/// Django's URL patterns should not start with a leading slash. When using
|
||||||
|
/// `include()` or when patterns are combined, leading slashes can cause
|
||||||
|
/// issues with URL resolution. The Django documentation recommends that
|
||||||
|
/// URL patterns should not have leading slashes, as they are not necessary
|
||||||
|
/// and can lead to unexpected behavior.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
/// ```python
|
||||||
|
/// from django.urls import path
|
||||||
|
/// from . import views
|
||||||
|
///
|
||||||
|
/// urlpatterns = [
|
||||||
|
/// path("/help/", views.help_view), # Leading slash
|
||||||
|
/// path("/about/", views.about_view), # Leading slash
|
||||||
|
/// ]
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Use instead:
|
||||||
|
/// ```python
|
||||||
|
/// from django.urls import path
|
||||||
|
/// from . import views
|
||||||
|
///
|
||||||
|
/// urlpatterns = [
|
||||||
|
/// path("help/", views.help_view),
|
||||||
|
/// path("about/", views.about_view),
|
||||||
|
/// ]
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## References
|
||||||
|
/// - [Django documentation: URL dispatcher](https://docs.djangoproject.com/en/stable/topics/http/urls/)
|
||||||
|
#[derive(ViolationMetadata)]
|
||||||
|
#[violation_metadata(preview_since = "v0.14.1")]
|
||||||
|
pub(crate) struct DjangoURLPathWithLeadingSlash {
|
||||||
|
url_pattern: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AlwaysFixableViolation for DjangoURLPathWithLeadingSlash {
|
||||||
|
#[derive_message_formats]
|
||||||
|
fn message(&self) -> String {
|
||||||
|
let DjangoURLPathWithLeadingSlash { url_pattern } = self;
|
||||||
|
format!("URL route `{url_pattern}` has an unnecessary leading slash")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fix_title(&self) -> String {
|
||||||
|
"Remove leading slash".to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// DJ015
|
||||||
|
pub(crate) fn url_path_with_leading_slash(checker: &Checker, call: &ast::ExprCall) {
|
||||||
|
if !checker.semantic().seen_module(Modules::DJANGO) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this is a call to django.urls.path
|
||||||
|
if !checker
|
||||||
|
.semantic()
|
||||||
|
.resolve_qualified_name(&call.func)
|
||||||
|
.is_some_and(|qualified_name| {
|
||||||
|
matches!(qualified_name.segments(), ["django", "urls", "path"])
|
||||||
|
})
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the first argument (the route pattern)
|
||||||
|
let Some(route_arg) = call.arguments.args.first() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if it's a string literal
|
||||||
|
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = route_arg {
|
||||||
|
let route = value.to_str();
|
||||||
|
|
||||||
|
// Skip empty strings and root path "/"
|
||||||
|
if route.is_empty() || route == "/" {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if route starts with a leading slash
|
||||||
|
if route.starts_with('/') {
|
||||||
|
// Report diagnostic for routes with leading slash
|
||||||
|
let mut diagnostic = checker.report_diagnostic(
|
||||||
|
DjangoURLPathWithLeadingSlash {
|
||||||
|
url_pattern: route.to_string(),
|
||||||
|
},
|
||||||
|
route_arg.range(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Determine the quote style to find the insertion point for removal
|
||||||
|
let string_content = checker.locator().slice(route_arg.range());
|
||||||
|
let quote_len =
|
||||||
|
if string_content.starts_with("'''") || string_content.starts_with("\"\"\"") {
|
||||||
|
3
|
||||||
|
} else if string_content.starts_with('\'') || string_content.starts_with('"') {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
return; // Invalid string format
|
||||||
|
};
|
||||||
|
|
||||||
|
// Remove the leading slash (after the opening quote(s))
|
||||||
|
let removal_start = route_arg.range().start() + TextSize::new(quote_len);
|
||||||
|
let removal_end = removal_start + TextSize::new(1); // Remove one character (the slash)
|
||||||
|
|
||||||
|
diagnostic.set_fix(Fix::safe_edit(Edit::deletion(removal_start, removal_end)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,216 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_linter/src/rules/flake8_django/mod.rs
|
||||||
|
---
|
||||||
|
DJ015 [*] URL route `/help/` has an unnecessary leading slash
|
||||||
|
--> DJ015.py:6:10
|
||||||
|
|
|
||||||
|
4 | # Errors - leading slash
|
||||||
|
5 | urlpatterns = [
|
||||||
|
6 | path("/help/", views.help_view), # DJ015
|
||||||
|
| ^^^^^^^^
|
||||||
|
7 | path("/about/", views.about_view), # DJ015
|
||||||
|
8 | path("/contact/", views.contact_view), # DJ015
|
||||||
|
|
|
||||||
|
help: Remove leading slash
|
||||||
|
3 |
|
||||||
|
4 | # Errors - leading slash
|
||||||
|
5 | urlpatterns = [
|
||||||
|
- path("/help/", views.help_view), # DJ015
|
||||||
|
6 + path("help/", views.help_view), # DJ015
|
||||||
|
7 | path("/about/", views.about_view), # DJ015
|
||||||
|
8 | path("/contact/", views.contact_view), # DJ015
|
||||||
|
9 | path("/api/users/", views.users_view), # DJ015
|
||||||
|
|
||||||
|
DJ015 [*] URL route `/about/` has an unnecessary leading slash
|
||||||
|
--> DJ015.py:7:10
|
||||||
|
|
|
||||||
|
5 | urlpatterns = [
|
||||||
|
6 | path("/help/", views.help_view), # DJ015
|
||||||
|
7 | path("/about/", views.about_view), # DJ015
|
||||||
|
| ^^^^^^^^^
|
||||||
|
8 | path("/contact/", views.contact_view), # DJ015
|
||||||
|
9 | path("/api/users/", views.users_view), # DJ015
|
||||||
|
|
|
||||||
|
help: Remove leading slash
|
||||||
|
4 | # Errors - leading slash
|
||||||
|
5 | urlpatterns = [
|
||||||
|
6 | path("/help/", views.help_view), # DJ015
|
||||||
|
- path("/about/", views.about_view), # DJ015
|
||||||
|
7 + path("about/", views.about_view), # DJ015
|
||||||
|
8 | path("/contact/", views.contact_view), # DJ015
|
||||||
|
9 | path("/api/users/", views.users_view), # DJ015
|
||||||
|
10 | path("/blog/posts/", views.posts_view), # DJ015
|
||||||
|
|
||||||
|
DJ015 [*] URL route `/contact/` has an unnecessary leading slash
|
||||||
|
--> DJ015.py:8:10
|
||||||
|
|
|
||||||
|
6 | path("/help/", views.help_view), # DJ015
|
||||||
|
7 | path("/about/", views.about_view), # DJ015
|
||||||
|
8 | path("/contact/", views.contact_view), # DJ015
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
9 | path("/api/users/", views.users_view), # DJ015
|
||||||
|
10 | path("/blog/posts/", views.posts_view), # DJ015
|
||||||
|
|
|
||||||
|
help: Remove leading slash
|
||||||
|
5 | urlpatterns = [
|
||||||
|
6 | path("/help/", views.help_view), # DJ015
|
||||||
|
7 | path("/about/", views.about_view), # DJ015
|
||||||
|
- path("/contact/", views.contact_view), # DJ015
|
||||||
|
8 + path("contact/", views.contact_view), # DJ015
|
||||||
|
9 | path("/api/users/", views.users_view), # DJ015
|
||||||
|
10 | path("/blog/posts/", views.posts_view), # DJ015
|
||||||
|
11 | ]
|
||||||
|
|
||||||
|
DJ015 [*] URL route `/api/users/` has an unnecessary leading slash
|
||||||
|
--> DJ015.py:9:10
|
||||||
|
|
|
||||||
|
7 | path("/about/", views.about_view), # DJ015
|
||||||
|
8 | path("/contact/", views.contact_view), # DJ015
|
||||||
|
9 | path("/api/users/", views.users_view), # DJ015
|
||||||
|
| ^^^^^^^^^^^^^
|
||||||
|
10 | path("/blog/posts/", views.posts_view), # DJ015
|
||||||
|
11 | ]
|
||||||
|
|
|
||||||
|
help: Remove leading slash
|
||||||
|
6 | path("/help/", views.help_view), # DJ015
|
||||||
|
7 | path("/about/", views.about_view), # DJ015
|
||||||
|
8 | path("/contact/", views.contact_view), # DJ015
|
||||||
|
- path("/api/users/", views.users_view), # DJ015
|
||||||
|
9 + path("api/users/", views.users_view), # DJ015
|
||||||
|
10 | path("/blog/posts/", views.posts_view), # DJ015
|
||||||
|
11 | ]
|
||||||
|
12 |
|
||||||
|
|
||||||
|
DJ015 [*] URL route `/blog/posts/` has an unnecessary leading slash
|
||||||
|
--> DJ015.py:10:10
|
||||||
|
|
|
||||||
|
8 | path("/contact/", views.contact_view), # DJ015
|
||||||
|
9 | path("/api/users/", views.users_view), # DJ015
|
||||||
|
10 | path("/blog/posts/", views.posts_view), # DJ015
|
||||||
|
| ^^^^^^^^^^^^^^
|
||||||
|
11 | ]
|
||||||
|
|
|
||||||
|
help: Remove leading slash
|
||||||
|
7 | path("/about/", views.about_view), # DJ015
|
||||||
|
8 | path("/contact/", views.contact_view), # DJ015
|
||||||
|
9 | path("/api/users/", views.users_view), # DJ015
|
||||||
|
- path("/blog/posts/", views.posts_view), # DJ015
|
||||||
|
10 + path("blog/posts/", views.posts_view), # DJ015
|
||||||
|
11 | ]
|
||||||
|
12 |
|
||||||
|
13 | # OK - no leading slash
|
||||||
|
|
||||||
|
DJ015 [*] URL route `/bad/` has an unnecessary leading slash
|
||||||
|
--> DJ015.py:37:10
|
||||||
|
|
|
||||||
|
35 | urlpatterns_mixed = [
|
||||||
|
36 | path("good/", views.good_view),
|
||||||
|
37 | path("/bad/", views.bad_view), # DJ015
|
||||||
|
| ^^^^^^^
|
||||||
|
38 | path("also-good/", views.also_good_view),
|
||||||
|
39 | path("/also-bad/", views.also_bad_view), # DJ015
|
||||||
|
|
|
||||||
|
help: Remove leading slash
|
||||||
|
34 | # Mixed cases
|
||||||
|
35 | urlpatterns_mixed = [
|
||||||
|
36 | path("good/", views.good_view),
|
||||||
|
- path("/bad/", views.bad_view), # DJ015
|
||||||
|
37 + path("bad/", views.bad_view), # DJ015
|
||||||
|
38 | path("also-good/", views.also_good_view),
|
||||||
|
39 | path("/also-bad/", views.also_bad_view), # DJ015
|
||||||
|
40 | ]
|
||||||
|
|
||||||
|
DJ015 [*] URL route `/also-bad/` has an unnecessary leading slash
|
||||||
|
--> DJ015.py:39:10
|
||||||
|
|
|
||||||
|
37 | path("/bad/", views.bad_view), # DJ015
|
||||||
|
38 | path("also-good/", views.also_good_view),
|
||||||
|
39 | path("/also-bad/", views.also_bad_view), # DJ015
|
||||||
|
| ^^^^^^^^^^^^
|
||||||
|
40 | ]
|
||||||
|
|
|
||||||
|
help: Remove leading slash
|
||||||
|
36 | path("good/", views.good_view),
|
||||||
|
37 | path("/bad/", views.bad_view), # DJ015
|
||||||
|
38 | path("also-good/", views.also_good_view),
|
||||||
|
- path("/also-bad/", views.also_bad_view), # DJ015
|
||||||
|
39 + path("also-bad/", views.also_bad_view), # DJ015
|
||||||
|
40 | ]
|
||||||
|
41 |
|
||||||
|
42 | # Edge cases with different quote styles
|
||||||
|
|
||||||
|
DJ015 [*] URL route `/single-quote/` has an unnecessary leading slash
|
||||||
|
--> DJ015.py:44:10
|
||||||
|
|
|
||||||
|
42 | # Edge cases with different quote styles
|
||||||
|
43 | urlpatterns_quotes = [
|
||||||
|
44 | path('/single-quote/', views.single_quote_view), # DJ015
|
||||||
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
45 | path("/double-quote/", views.double_quote_view), # DJ015
|
||||||
|
46 | path('''/triple-single/''', views.triple_single_view), # DJ015
|
||||||
|
|
|
||||||
|
help: Remove leading slash
|
||||||
|
41 |
|
||||||
|
42 | # Edge cases with different quote styles
|
||||||
|
43 | urlpatterns_quotes = [
|
||||||
|
- path('/single-quote/', views.single_quote_view), # DJ015
|
||||||
|
44 + path('single-quote/', views.single_quote_view), # DJ015
|
||||||
|
45 | path("/double-quote/", views.double_quote_view), # DJ015
|
||||||
|
46 | path('''/triple-single/''', views.triple_single_view), # DJ015
|
||||||
|
47 | path("""/triple-double/""", views.triple_double_view), # DJ015
|
||||||
|
|
||||||
|
DJ015 [*] URL route `/double-quote/` has an unnecessary leading slash
|
||||||
|
--> DJ015.py:45:10
|
||||||
|
|
|
||||||
|
43 | urlpatterns_quotes = [
|
||||||
|
44 | path('/single-quote/', views.single_quote_view), # DJ015
|
||||||
|
45 | path("/double-quote/", views.double_quote_view), # DJ015
|
||||||
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
46 | path('''/triple-single/''', views.triple_single_view), # DJ015
|
||||||
|
47 | path("""/triple-double/""", views.triple_double_view), # DJ015
|
||||||
|
|
|
||||||
|
help: Remove leading slash
|
||||||
|
42 | # Edge cases with different quote styles
|
||||||
|
43 | urlpatterns_quotes = [
|
||||||
|
44 | path('/single-quote/', views.single_quote_view), # DJ015
|
||||||
|
- path("/double-quote/", views.double_quote_view), # DJ015
|
||||||
|
45 + path("double-quote/", views.double_quote_view), # DJ015
|
||||||
|
46 | path('''/triple-single/''', views.triple_single_view), # DJ015
|
||||||
|
47 | path("""/triple-double/""", views.triple_double_view), # DJ015
|
||||||
|
48 | ]
|
||||||
|
|
||||||
|
DJ015 [*] URL route `/triple-single/` has an unnecessary leading slash
|
||||||
|
--> DJ015.py:46:10
|
||||||
|
|
|
||||||
|
44 | path('/single-quote/', views.single_quote_view), # DJ015
|
||||||
|
45 | path("/double-quote/", views.double_quote_view), # DJ015
|
||||||
|
46 | path('''/triple-single/''', views.triple_single_view), # DJ015
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
47 | path("""/triple-double/""", views.triple_double_view), # DJ015
|
||||||
|
48 | ]
|
||||||
|
|
|
||||||
|
help: Remove leading slash
|
||||||
|
43 | urlpatterns_quotes = [
|
||||||
|
44 | path('/single-quote/', views.single_quote_view), # DJ015
|
||||||
|
45 | path("/double-quote/", views.double_quote_view), # DJ015
|
||||||
|
- path('''/triple-single/''', views.triple_single_view), # DJ015
|
||||||
|
46 + path('''triple-single/''', views.triple_single_view), # DJ015
|
||||||
|
47 | path("""/triple-double/""", views.triple_double_view), # DJ015
|
||||||
|
48 | ]
|
||||||
|
|
||||||
|
DJ015 [*] URL route `/triple-double/` has an unnecessary leading slash
|
||||||
|
--> DJ015.py:47:10
|
||||||
|
|
|
||||||
|
45 | path("/double-quote/", views.double_quote_view), # DJ015
|
||||||
|
46 | path('''/triple-single/''', views.triple_single_view), # DJ015
|
||||||
|
47 | path("""/triple-double/""", views.triple_double_view), # DJ015
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
48 | ]
|
||||||
|
|
|
||||||
|
help: Remove leading slash
|
||||||
|
44 | path('/single-quote/', views.single_quote_view), # DJ015
|
||||||
|
45 | path("/double-quote/", views.double_quote_view), # DJ015
|
||||||
|
46 | path('''/triple-single/''', views.triple_single_view), # DJ015
|
||||||
|
- path("""/triple-double/""", views.triple_double_view), # DJ015
|
||||||
|
47 + path("""triple-double/""", views.triple_double_view), # DJ015
|
||||||
|
48 | ]
|
||||||
|
|
@ -3158,6 +3158,7 @@
|
||||||
"DJ012",
|
"DJ012",
|
||||||
"DJ013",
|
"DJ013",
|
||||||
"DJ014",
|
"DJ014",
|
||||||
|
"DJ015",
|
||||||
"DOC",
|
"DOC",
|
||||||
"DOC1",
|
"DOC1",
|
||||||
"DOC10",
|
"DOC10",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue