From 6079bcf1f753d949e34ff26d25bdfa17f4985384 Mon Sep 17 00:00:00 2001 From: heumsi Date: Wed, 1 Apr 2026 02:01:58 +0900 Subject: [PATCH 1/3] =?UTF-8?q?=E2=9C=A8=20feat:=20Allow=20dot=20(.)=20in?= =?UTF-8?q?=20rule=20names?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 (1M context) --- python_naming_linter/config.py | 4 ++-- python_naming_linter/ignore.py | 2 +- tests/test_config.py | 4 ++-- tests/test_ignore.py | 8 ++++++++ 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/python_naming_linter/config.py b/python_naming_linter/config.py index 2486789..3ec0655 100644 --- a/python_naming_linter/config.py +++ b/python_naming_linter/config.py @@ -6,7 +6,7 @@ import yaml -_VALID_RULE_NAME_RE = re.compile(r"^[a-zA-Z0-9_-]+$") +_VALID_RULE_NAME_RE = re.compile(r"^[a-zA-Z0-9_.\-]+$") @dataclass @@ -36,7 +36,7 @@ class Config: def _validate_rule_name(name: str) -> None: if not _VALID_RULE_NAME_RE.match(name): raise ValueError( - f"Invalid rule name '{name}'. Rule names must match [a-zA-Z0-9_-]+" + f"Invalid rule name '{name}'. Rule names must match [a-zA-Z0-9_.-]+" ) diff --git a/python_naming_linter/ignore.py b/python_naming_linter/ignore.py index 15f1bdd..0fa8635 100644 --- a/python_naming_linter/ignore.py +++ b/python_naming_linter/ignore.py @@ -4,7 +4,7 @@ from python_naming_linter.checkers import Violation -_IGNORE_RE = re.compile(r"#\s*pnl:\s*ignore(?:=([a-zA-Z0-9_,\s-]+))?$") +_IGNORE_RE = re.compile(r"#\s*pnl:\s*ignore(?:=([a-zA-Z0-9_.,\s-]+))?$") def parse_ignore_comments(source: str) -> dict[int, set[str] | None]: diff --git a/tests/test_config.py b/tests/test_config.py index 9a393b7..7661c3e 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -127,7 +127,7 @@ def test_find_config_skips_pyproject_without_section(tmp_path, monkeypatch): @pytest.mark.parametrize( "name", - ["attribute-matches-type", "bool_method", "rule1", "My-Rule_2"], + ["attribute-matches-type", "bool_method", "rule1", "My-Rule_2", "rule.name", "shared.domain"], ) def test_valid_rule_names(tmp_path, name): config_content = f"""\ @@ -148,7 +148,7 @@ def test_valid_rule_names(tmp_path, name): @pytest.mark.parametrize( "name", - ["my rule", "rule!name", "rule name 123", "rule.name", "rule@name"], + ["my rule", "rule!name", "rule name 123", "rule@name"], ) def test_invalid_rule_names(tmp_path, name): config_content = f"""\ diff --git a/tests/test_ignore.py b/tests/test_ignore.py index 840454c..1ac655c 100644 --- a/tests/test_ignore.py +++ b/tests/test_ignore.py @@ -44,6 +44,14 @@ def test_extra_space_after_pnl(self): source = "x: int = 1 # pnl: ignore\n" assert parse_ignore_comments(source) == {1: None} + def test_ignore_rule_with_dot(self): + source = "x: int = 1 # pnl: ignore=shared.domain\n" + assert parse_ignore_comments(source) == {1: {"shared.domain"}} + + def test_ignore_multiple_rules_with_dots(self): + source = "x: int = 1 # pnl: ignore=shared.domain,context.adapters\n" + assert parse_ignore_comments(source) == {1: {"shared.domain", "context.adapters"}} + class TestFilterViolations: def _make_violation(self, rule_name: str, lineno: int) -> Violation: From 9123c11bdc5017920c2fc0897e1ada114a0ea597 Mon Sep 17 00:00:00 2001 From: heumsi Date: Wed, 1 Apr 2026 02:04:27 +0900 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=94=A7=20chore:=20Fix=20line=20length?= =?UTF-8?q?=20lint=20errors=20in=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 (1M context) --- tests/test_config.py | 9 ++++++++- tests/test_ignore.py | 4 +++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/tests/test_config.py b/tests/test_config.py index 7661c3e..9f191d3 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -127,7 +127,14 @@ def test_find_config_skips_pyproject_without_section(tmp_path, monkeypatch): @pytest.mark.parametrize( "name", - ["attribute-matches-type", "bool_method", "rule1", "My-Rule_2", "rule.name", "shared.domain"], + [ + "attribute-matches-type", + "bool_method", + "rule1", + "My-Rule_2", + "rule.name", + "shared.domain", + ], ) def test_valid_rule_names(tmp_path, name): config_content = f"""\ diff --git a/tests/test_ignore.py b/tests/test_ignore.py index 1ac655c..15756c2 100644 --- a/tests/test_ignore.py +++ b/tests/test_ignore.py @@ -50,7 +50,9 @@ def test_ignore_rule_with_dot(self): def test_ignore_multiple_rules_with_dots(self): source = "x: int = 1 # pnl: ignore=shared.domain,context.adapters\n" - assert parse_ignore_comments(source) == {1: {"shared.domain", "context.adapters"}} + assert parse_ignore_comments(source) == { + 1: {"shared.domain", "context.adapters"} + } class TestFilterViolations: From ab67c5af54f446b02a444c92f304f68d15a7bd51 Mon Sep 17 00:00:00 2001 From: heumsi Date: Wed, 1 Apr 2026 02:05:38 +0900 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=93=9D=20docs:=20Add=20allowed=20char?= =?UTF-8?q?acters=20for=20rule=20name=20field?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/guide/rules.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guide/rules.md b/docs/guide/rules.md index 8fc4d62..e49ab08 100644 --- a/docs/guide/rules.md +++ b/docs/guide/rules.md @@ -21,7 +21,7 @@ The `name` is used to reference the rule in `apply` blocks and in `# pnl: ignore | Field | Required | Description | |-------|----------|-------------| -| `name` | Yes | Unique identifier, referenced in `apply` and `# pnl: ignore` | +| `name` | Yes | Unique identifier, referenced in `apply` and `# pnl: ignore`. Must match `[a-zA-Z0-9_.-]+` | | `description` | No | Human-readable description shown in violation output | | `type` | Yes | What kind of name to lint (`variable`, `function`, `class`, `module`, `package`) | | `filter` | No | Narrow which names are checked (see [Filters](#filters) below) | @@ -917,7 +917,7 @@ apply: | Field | Required | Description | |-------|----------|-------------| -| `name` | Yes | Unique identifier, referenced in `apply` and `# pnl: ignore` | +| `name` | Yes | Unique identifier, referenced in `apply` and `# pnl: ignore`. Must match `[a-zA-Z0-9_.-]+` | | `description` | No | Human-readable description shown in violation output | | `type` | Yes | What kind of name to lint (`variable`, `function`, `class`, `module`, `package`) | | `filter` | No | Narrow which names are checked |