galaxy-tool-refactor-rules 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- galaxy_tool_refactor_rules/__init__.py +11 -0
- galaxy_tool_refactor_rules/meta.py +86 -0
- galaxy_tool_refactor_rules/py.typed +0 -0
- galaxy_tool_refactor_rules/reference.py +48 -0
- galaxy_tool_refactor_rules/rulesets.py +84 -0
- galaxy_tool_refactor_rules/violation.py +39 -0
- galaxy_tool_refactor_rules-0.2.0.dist-info/METADATA +55 -0
- galaxy_tool_refactor_rules-0.2.0.dist-info/RECORD +9 -0
- galaxy_tool_refactor_rules-0.2.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Shared rule-metadata vocabulary for the galaxy-tool-refactor tiers.
|
|
2
|
+
|
|
3
|
+
Tier 0.5 of the refactoring framework: a dependency-free home for the
|
|
4
|
+
``RuleMeta`` descriptor shared by the formatter (tier 3) and the codemod
|
|
5
|
+
framework (tier 2), plus a pure markdown render helper for rule glossaries.
|
|
6
|
+
|
|
7
|
+
Following the project's dignified-python conventions there are no re-exports;
|
|
8
|
+
callers import ``RuleMeta`` from ``galaxy_tool_refactor_rules.meta`` and
|
|
9
|
+
``render_rule_reference_table`` from ``galaxy_tool_refactor_rules.reference``
|
|
10
|
+
directly.
|
|
11
|
+
"""
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""The ``RuleMeta`` descriptor shared across the refactor tiers.
|
|
2
|
+
|
|
3
|
+
Both a tier-3 formatter rule (``galaxy_tool_fmt.rules.Rule``) and a tier-2
|
|
4
|
+
codemod (``galaxy_tool_codemod.codemod.CodemodCommand``) carry a
|
|
5
|
+
``meta: ClassVar[RuleMeta]`` so the two tiers expose one uniform vocabulary for
|
|
6
|
+
the GTR rule registry. The descriptor is pure data — it deliberately knows
|
|
7
|
+
nothing about lxml, edits, or the cursor walk, which keeps this package
|
|
8
|
+
dependency-free and the tiers independent.
|
|
9
|
+
|
|
10
|
+
Versioning convention: stability for consumers comes from pinning the owning
|
|
11
|
+
tier's package version in their lockfile, not from this metadata. ``since`` /
|
|
12
|
+
``until`` are documentary only — ``until`` stays ``None`` while a rule is active
|
|
13
|
+
and is stamped (for the changelog) in the same commit that retires the rule.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from dataclasses import dataclass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass(frozen=True)
|
|
22
|
+
class RuleMeta:
|
|
23
|
+
"""Metadata descriptor for a refactor rule (a fmt rule or a codemod).
|
|
24
|
+
|
|
25
|
+
Attributes:
|
|
26
|
+
code: Short unique rule identifier (e.g. ``"GTR001"``).
|
|
27
|
+
summary: One-line human-readable description.
|
|
28
|
+
since: Version in which this rule was introduced.
|
|
29
|
+
until: Version in which this rule was removed, or ``None`` if active.
|
|
30
|
+
cite: Optional reference URL or citation.
|
|
31
|
+
order: Application order; lower values run first. Each family is ordered
|
|
32
|
+
independently by this value — the formatter tier sequences its
|
|
33
|
+
cosmetic rules, and the codemod tier sequences its canonical codemods
|
|
34
|
+
(the registry's apply phase sorts each family by ``order``). An
|
|
35
|
+
upgrade-only or report-only rule leaves it at the default.
|
|
36
|
+
detect_only: Whether the rule only *reports* (a lint with no automatic
|
|
37
|
+
fix), as opposed to the fixable fmt rules and codemods. The advisory
|
|
38
|
+
check tier (``galaxy-tool-lint``) sets this ``True``; a
|
|
39
|
+
report-only consumer like the ``check`` CLI uses it to treat such
|
|
40
|
+
findings as informational rather than as a failing gate.
|
|
41
|
+
applies_to: The document kinds the rule operates on — a subset of
|
|
42
|
+
``{"tool", "macro"}``. A generic XML rule (canonical indentation,
|
|
43
|
+
empty-element shorthand) applies to both; a tool-structural rule
|
|
44
|
+
(``<tool>`` child order, a blank line between ``<tool>`` sections,
|
|
45
|
+
attribute order, profile upgrades) applies only to ``"tool"``; a
|
|
46
|
+
macro-library rule applies only to ``"macro"``. The default
|
|
47
|
+
``{"tool"}`` is the conservative choice — a rule runs on a macro
|
|
48
|
+
file only when it explicitly opts in. Consumers run a rule against a
|
|
49
|
+
document only when the document's kind is in this set.
|
|
50
|
+
parent: The code of the **partition parent** this rule is a sub-rule of,
|
|
51
|
+
or ``None`` for a standalone rule. A best-practice that splits into a
|
|
52
|
+
provably-fixable part and an advisory residual is modelled as a parent
|
|
53
|
+
practice code (e.g. ``"GTR020"``) with two sub-rules whose own ``code``
|
|
54
|
+
is dotted: ``"GTR020.1"`` (fixable) and ``"GTR020.2"`` (advisory). The
|
|
55
|
+
parent is a registry-level grouping (selectable, expands to its
|
|
56
|
+
children), not itself a rule; this field is what the registry derives
|
|
57
|
+
the groups from. See registry ``docs/decisions.md`` D10.
|
|
58
|
+
rulesets: The names of the rule-sets this rule belongs to (the catalog
|
|
59
|
+
lives in ``rulesets.py``). This is the maintainer-facing "mark which
|
|
60
|
+
rules belong to which set" mechanism: the registry groups rules by
|
|
61
|
+
these names into selectable sets, and the CLI ``--ruleset`` flag
|
|
62
|
+
selects the **union** of the named sets. The default empty set means
|
|
63
|
+
the rule is never independently selectable — e.g. an upgrade-only
|
|
64
|
+
codemod driven internally by ``UpgradeToLatest``. Every name used
|
|
65
|
+
here must appear in the ``rulesets.py`` catalog (guarded by a test).
|
|
66
|
+
planemo_linters: The names of the planemo (``galaxy.tool_util.lint``)
|
|
67
|
+
linter classes this rule covers, e.g. ``{"HelpMissing", "HelpEmpty"}``.
|
|
68
|
+
One GTR rule may cover several planemo linters (planemo splits some
|
|
69
|
+
single practices across linter classes). The registry derives a
|
|
70
|
+
``planemo name → GTR code`` index from this so a planemo user can
|
|
71
|
+
select/find a rule by its planemo name (``--select HelpMissing``) and
|
|
72
|
+
so the parity table can be generated. Empty for our own rules with no
|
|
73
|
+
planemo equivalent (the cosmetic fmt rules, the XSD-restoring repairs).
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
code: str
|
|
77
|
+
summary: str
|
|
78
|
+
since: str
|
|
79
|
+
until: str | None = None
|
|
80
|
+
cite: str | None = None
|
|
81
|
+
order: int = 100
|
|
82
|
+
detect_only: bool = False
|
|
83
|
+
applies_to: frozenset[str] = frozenset({"tool"})
|
|
84
|
+
parent: str | None = None
|
|
85
|
+
rulesets: frozenset[str] = frozenset()
|
|
86
|
+
planemo_linters: frozenset[str] = frozenset()
|
|
File without changes
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Render a cross-tier GTR rule glossary as GitHub-flavored markdown.
|
|
2
|
+
|
|
3
|
+
A pure, dependency-free helper: it turns ``(RuleMeta, tier)`` pairs into the
|
|
4
|
+
rows of a ``| Rule | Tier | What it does |`` table, sorted by code. It emits the
|
|
5
|
+
table only — the caller owns any surrounding heading and intro prose, which is
|
|
6
|
+
context-specific (the fmt stat page frames it differently than a standalone rule
|
|
7
|
+
registry would).
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import re
|
|
13
|
+
from collections.abc import Iterable
|
|
14
|
+
|
|
15
|
+
from galaxy_tool_refactor_rules.meta import RuleMeta
|
|
16
|
+
|
|
17
|
+
# Element names in a summary (``<param>``, ``<foo/>``) would be parsed as HTML
|
|
18
|
+
# tags inside a markdown table cell and rendered as nothing; wrap them in
|
|
19
|
+
# backticks so they survive as literal text.
|
|
20
|
+
_ANGLE_TOKEN = re.compile(r"(<[^>]+>)")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _backtick_xml_tokens(text: str) -> str:
|
|
24
|
+
"""Backtick-wrap angle-bracket tokens so GitHub renders them literally."""
|
|
25
|
+
return _ANGLE_TOKEN.sub(r"`\1`", text)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def render_rule_reference_table(entries: Iterable[tuple[RuleMeta, str]]) -> list[str]:
|
|
29
|
+
"""Render ``(meta, tier)`` pairs as a markdown reference table.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
entries: ``(RuleMeta, tier_label)`` pairs, where ``tier_label`` names the
|
|
33
|
+
owning tier (e.g. ``"fmt"`` or ``"codemod"``).
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
Markdown lines: a header row, the separator, then one row per rule
|
|
37
|
+
ordered by ``meta.code``.
|
|
38
|
+
"""
|
|
39
|
+
rows = sorted(entries, key=lambda entry: entry[0].code)
|
|
40
|
+
lines = [
|
|
41
|
+
"| Rule | Tier | What it does |",
|
|
42
|
+
"|---|---|---|",
|
|
43
|
+
]
|
|
44
|
+
lines.extend(
|
|
45
|
+
f"| {meta.code} | {tier} | {_backtick_xml_tokens(meta.summary)} |"
|
|
46
|
+
for meta, tier in rows
|
|
47
|
+
)
|
|
48
|
+
return lines
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""The named rule-sets — the maintainer-facing vocabulary for grouping rules.
|
|
2
|
+
|
|
3
|
+
A *ruleset* is a named, described bucket of rules. **Membership is declared per
|
|
4
|
+
rule** (``RuleMeta.rulesets``): a maintainer marks a rule's set(s) right on the
|
|
5
|
+
rule. This module is the authoritative catalog of the ruleset *names and
|
|
6
|
+
descriptions* — the one property that belongs to the set itself, not to any
|
|
7
|
+
member. The registry tier (3.6) derives ``name -> {codes}`` by grouping rules by
|
|
8
|
+
their declared membership, and the CLI ``--ruleset`` flag selects the **union** of
|
|
9
|
+
the named sets.
|
|
10
|
+
|
|
11
|
+
Dependency-free, like the rest of tier 0.5 — ruleset names are plain strings, so a
|
|
12
|
+
rule in any tier can declare membership (``RuleMeta(..., rulesets=frozenset({...}))``)
|
|
13
|
+
without importing anything heavier. Adding a ruleset is a developer task (a new
|
|
14
|
+
``Ruleset`` here + tagging the member rules); there are no user-defined rulesets.
|
|
15
|
+
|
|
16
|
+
The catalog also defines the subset relationships informally via the seeded
|
|
17
|
+
membership: ``cosmetic`` ⊆ ``default`` == ``iuc`` ⊆ ``strict`` today. ``default``
|
|
18
|
+
is the set applied when the user names no ruleset; it reproduces the historical
|
|
19
|
+
default ``format`` behaviour.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
from dataclasses import dataclass
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass(frozen=True)
|
|
28
|
+
class Ruleset:
|
|
29
|
+
"""A named rule-set: a selectable bucket of rules.
|
|
30
|
+
|
|
31
|
+
Attributes:
|
|
32
|
+
name: The selection key (e.g. ``"default"``) — exactly as it appears in
|
|
33
|
+
``RuleMeta.rulesets`` and on the CLI ``--ruleset`` flag.
|
|
34
|
+
description: A one-line human-readable summary.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
name: str
|
|
38
|
+
description: str
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
DEFAULT_RULESET = "default"
|
|
42
|
+
"""The ruleset selected when the user names none (the no-argument ``format`` set)."""
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
_CATALOG: tuple[Ruleset, ...] = (
|
|
46
|
+
Ruleset(
|
|
47
|
+
name="cosmetic",
|
|
48
|
+
description="Cosmetic whitespace only (indent, blank lines, shorthand).",
|
|
49
|
+
),
|
|
50
|
+
Ruleset(
|
|
51
|
+
name="default",
|
|
52
|
+
description="The opinionated canonical formatter: structural repair + "
|
|
53
|
+
"attribute/element order + CDATA/quoting/help-RST fixes + cosmetic "
|
|
54
|
+
"formatting (the default).",
|
|
55
|
+
),
|
|
56
|
+
Ruleset(
|
|
57
|
+
name="iuc",
|
|
58
|
+
description="Mirrors 'default' today; reserved for IUC-specific "
|
|
59
|
+
"divergence.",
|
|
60
|
+
),
|
|
61
|
+
Ruleset(
|
|
62
|
+
name="strict",
|
|
63
|
+
description="Everything in 'default' plus the advisory best-practice "
|
|
64
|
+
"checks (report-only).",
|
|
65
|
+
),
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def rulesets_catalog() -> tuple[Ruleset, ...]:
|
|
70
|
+
"""Return every defined ruleset, in display order."""
|
|
71
|
+
return _CATALOG
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def ruleset_names() -> tuple[str, ...]:
|
|
75
|
+
"""Return the defined ruleset names, in display order."""
|
|
76
|
+
return tuple(ruleset.name for ruleset in _CATALOG)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def ruleset_description(name: str, /) -> str | None:
|
|
80
|
+
"""Return the one-line description for *name*, or ``None`` if undefined."""
|
|
81
|
+
for ruleset in _CATALOG:
|
|
82
|
+
if ruleset.name == name:
|
|
83
|
+
return ruleset.description
|
|
84
|
+
return None
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""The ``Violation`` diagnostic descriptor shared across the refactor tiers.
|
|
2
|
+
|
|
3
|
+
A ``Violation`` is the per-occurrence report a detect (lint) phase produces:
|
|
4
|
+
which rule fired (``code``), where (``sourceline`` + ``xpath``), and a
|
|
5
|
+
human-readable ``message``. It is the read-only counterpart to the mutating
|
|
6
|
+
``Edit`` (tier 3) / ``Change`` (tier 2) types — those carry the fix; this carries
|
|
7
|
+
the finding. Both the formatter (tier 3) and codemod (tier 2) detect phases, the
|
|
8
|
+
``check`` CLI (tier 4), and the advisory check library surface diagnostics as
|
|
9
|
+
``Violation``s, so the type lives here next to ``RuleMeta`` where every tier can
|
|
10
|
+
reach it without depending on one another.
|
|
11
|
+
|
|
12
|
+
Like ``RuleMeta`` this is pure data — the location is a plain ``int`` line plus a
|
|
13
|
+
``str`` xpath, never an lxml handle — which keeps this package dependency-free
|
|
14
|
+
(no lxml, no tier 1/2/3 imports). See ``docs/decisions.md`` § D1 for the
|
|
15
|
+
shared-vocabulary rationale.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
from dataclasses import dataclass
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True)
|
|
24
|
+
class Violation:
|
|
25
|
+
"""A single detected rule occurrence in a tool XML document.
|
|
26
|
+
|
|
27
|
+
Attributes:
|
|
28
|
+
code: The rule's identifier (e.g. ``"GTR002"``); matches ``RuleMeta.code``.
|
|
29
|
+
sourceline: 1-based line of the offending element, or ``0`` when the
|
|
30
|
+
element was synthesised and has no source position.
|
|
31
|
+
xpath: Absolute xpath to the offending element (e.g.
|
|
32
|
+
``"/tool/inputs/param[1]"``).
|
|
33
|
+
message: One-line human-readable description of the finding.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
code: str
|
|
37
|
+
sourceline: int
|
|
38
|
+
xpath: str
|
|
39
|
+
message: str
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: galaxy-tool-refactor-rules
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Shared rule-metadata vocabulary for the galaxy-tool-refactor tiers (tier 0.5).
|
|
5
|
+
Author: Richard Burhans
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
|
|
10
|
+
# galaxy-tool-refactor-rules
|
|
11
|
+
|
|
12
|
+
Shared **rule-metadata vocabulary** for the Galaxy tool refactoring framework —
|
|
13
|
+
a small, dependency-free "tier 0.5" package consumed by both higher tiers:
|
|
14
|
+
|
|
15
|
+
| Tier | Layer | Package |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| 0.5 | **rule metadata** | `galaxy-tool-refactor-rules` *(this package)* |
|
|
18
|
+
| 1 | parsing & validation | `galaxy-tool-source` |
|
|
19
|
+
| 2 | structure | `galaxy-tool-codemod` |
|
|
20
|
+
| 3 | formatting | `galaxy-tool-fmt` |
|
|
21
|
+
| 3.5 | advisory checks | `galaxy-tool-lint` |
|
|
22
|
+
| 3.6 | rule registry / rulesets | `galaxy-tool-refactor-registry` |
|
|
23
|
+
| 4 | app / CLI | `galaxy-tool-refactor-cli` |
|
|
24
|
+
|
|
25
|
+
It provides:
|
|
26
|
+
|
|
27
|
+
- **`galaxy_tool_refactor_rules.meta.RuleMeta`** — a frozen dataclass describing
|
|
28
|
+
one GTR rule (`code`, `summary`, `since`, `until`, `cite`, `order`,
|
|
29
|
+
`detect_only`, `applies_to`). A tier-3 formatter `Rule`, a tier-2
|
|
30
|
+
`CodemodCommand`, and a tier-3.5 `CheckRule` each carry a
|
|
31
|
+
`meta: ClassVar[RuleMeta]`, so the tiers share one registry vocabulary.
|
|
32
|
+
- **`galaxy_tool_refactor_rules.violation.Violation`** — a frozen dataclass for a
|
|
33
|
+
detect-phase finding (`code`, `sourceline`, `xpath`, `message`); the pure,
|
|
34
|
+
lxml-free, read-only counterpart to the mutating tier-2 `Change` / tier-3 `Edit`.
|
|
35
|
+
- **`galaxy_tool_refactor_rules.reference.render_rule_reference_table`** — a pure
|
|
36
|
+
helper that renders `(RuleMeta, tier)` pairs as a GitHub-flavored markdown
|
|
37
|
+
glossary table.
|
|
38
|
+
|
|
39
|
+
## Why a separate package
|
|
40
|
+
|
|
41
|
+
The descriptor is the only thing the two tiers genuinely share — their
|
|
42
|
+
*behavioral* bases differ (fmt yields lxml edits; codemod walks a cursor), so
|
|
43
|
+
those stay in their own packages. Keeping `RuleMeta` here, with **zero runtime
|
|
44
|
+
dependencies**, lets both fmt and codemod depend on it without depending on each
|
|
45
|
+
other — preserving the tier independence documented in
|
|
46
|
+
`galaxy-tool-fmt/docs/decisions.md` §D10. The extraction was anticipated in
|
|
47
|
+
that package's §D1 ("a shared rule-engine package will be extracted only when a
|
|
48
|
+
second consumer materialises"); the codemod tier is that consumer.
|
|
49
|
+
|
|
50
|
+
## Install / test
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
uv sync # from the workspace root
|
|
54
|
+
uv run --package galaxy-tool-refactor-rules pytest galaxy-tool-refactor-rules/tests/
|
|
55
|
+
```
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
galaxy_tool_refactor_rules/__init__.py,sha256=Q0rYk_ovDHfo8JCPwqW1NK3gH599PSc6ArOfIITj2Go,535
|
|
2
|
+
galaxy_tool_refactor_rules/meta.py,sha256=IQ9dvG2bnqk4sanBopvPsZnLoXq8g6ZmNfmpRJRVqIQ,5015
|
|
3
|
+
galaxy_tool_refactor_rules/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
galaxy_tool_refactor_rules/reference.py,sha256=kfnBf1uYStiucwZZsqakMpnwjlCVnczRWmqwedlKcKA,1691
|
|
5
|
+
galaxy_tool_refactor_rules/rulesets.py,sha256=TzsBGU6pMxSeh9pYrEiYWvfj19o_pHyTtnqj1FxhmXY,2968
|
|
6
|
+
galaxy_tool_refactor_rules/violation.py,sha256=2HYnrxByc_6_8qSVwwo-EqIPzeJ8Y_rER-GjC6a0GFo,1610
|
|
7
|
+
galaxy_tool_refactor_rules-0.2.0.dist-info/METADATA,sha256=urxww9kbIu0D6U3Mu9tSGeGjZbiS1ho1BiVssOqfZvU,2440
|
|
8
|
+
galaxy_tool_refactor_rules-0.2.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
9
|
+
galaxy_tool_refactor_rules-0.2.0.dist-info/RECORD,,
|