galaxy-tool-codemod 0.2.0__tar.gz

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.
Files changed (101) hide show
  1. galaxy_tool_codemod-0.2.0/.gitignore +22 -0
  2. galaxy_tool_codemod-0.2.0/CLAUDE.md +155 -0
  3. galaxy_tool_codemod-0.2.0/LICENSE +21 -0
  4. galaxy_tool_codemod-0.2.0/PKG-INFO +134 -0
  5. galaxy_tool_codemod-0.2.0/PLAN.md +454 -0
  6. galaxy_tool_codemod-0.2.0/README.md +120 -0
  7. galaxy_tool_codemod-0.2.0/docs/architecture.md +315 -0
  8. galaxy_tool_codemod-0.2.0/docs/behavior-preserving-upgrade.md +163 -0
  9. galaxy_tool_codemod-0.2.0/docs/decisions.md +1845 -0
  10. galaxy_tool_codemod-0.2.0/docs/macro-aware-normalization.md +162 -0
  11. galaxy_tool_codemod-0.2.0/pyproject.toml +50 -0
  12. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/__init__.py +12 -0
  13. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/_version.py +21 -0
  14. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/canonical.py +109 -0
  15. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/catalog.py +90 -0
  16. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/certify.py +47 -0
  17. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/change.py +63 -0
  18. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/codemod.py +136 -0
  19. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/codemods/__init__.py +7 -0
  20. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/codemods/_attribute_ordering.py +24 -0
  21. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/codemods/_cdata.py +46 -0
  22. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/codemods/_coarse_detect.py +65 -0
  23. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/codemods/_interpreter.py +125 -0
  24. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/codemods/_runtime_gated.py +21 -0
  25. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/codemods/_validation_repair.py +32 -0
  26. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/codemods/convert_help_markdown.py +117 -0
  27. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/codemods/drop_redundant_param_name.py +88 -0
  28. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/codemods/fix_from_work_dir_whitespace.py +57 -0
  29. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/codemods/fix_interpreter.py +126 -0
  30. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/codemods/fix_output_format_input.py +150 -0
  31. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/codemods/fix_typos.py +184 -0
  32. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/codemods/normalize_boolean_values.py +138 -0
  33. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/codemods/reorder_param_attributes.py +61 -0
  34. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/codemods/reorder_tool_attributes.py +52 -0
  35. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/codemods/reorder_tool_children.py +68 -0
  36. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/codemods/repair_help_rst.py +71 -0
  37. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/codemods/replace_output_element.py +113 -0
  38. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/codemods/single_quote_command_vars.py +139 -0
  39. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/codemods/tokenize_version.py +93 -0
  40. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/codemods/trim_attribute_whitespace.py +84 -0
  41. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/codemods/update_profile.py +110 -0
  42. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/codemods/upgrade_19_01.py +96 -0
  43. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/codemods/upgrade_21_09.py +245 -0
  44. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/codemods/upgrade_24_0.py +98 -0
  45. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/codemods/upgrade_24_1.py +74 -0
  46. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/codemods/upgrade_25_1.py +48 -0
  47. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/codemods/wrap_command_cdata.py +42 -0
  48. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/codemods/wrap_help_cdata.py +42 -0
  49. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/cursor.py +281 -0
  50. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/datatype_format.py +63 -0
  51. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/eligibility.py +81 -0
  52. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/module.py +65 -0
  53. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/parse.py +47 -0
  54. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/profile_semantics.py +620 -0
  55. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/py.typed +0 -0
  56. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/runtime_fixes.py +77 -0
  57. galaxy_tool_codemod-0.2.0/src/galaxy_tool_codemod/upgrades.py +137 -0
  58. galaxy_tool_codemod-0.2.0/tests/conftest.py +31 -0
  59. galaxy_tool_codemod-0.2.0/tests/data/minimal_tool.xml +5 -0
  60. galaxy_tool_codemod-0.2.0/tests/data/regressions/PROVENANCE.md +18 -0
  61. galaxy_tool_codemod-0.2.0/tests/data/tool_with_params.xml +16 -0
  62. galaxy_tool_codemod-0.2.0/tests/test_attribute_ordering.py +42 -0
  63. galaxy_tool_codemod-0.2.0/tests/test_canonical.py +144 -0
  64. galaxy_tool_codemod-0.2.0/tests/test_catalog.py +66 -0
  65. galaxy_tool_codemod-0.2.0/tests/test_change.py +73 -0
  66. galaxy_tool_codemod-0.2.0/tests/test_coarse_detect.py +61 -0
  67. galaxy_tool_codemod-0.2.0/tests/test_codemod.py +206 -0
  68. galaxy_tool_codemod-0.2.0/tests/test_convert_help_markdown.py +132 -0
  69. galaxy_tool_codemod-0.2.0/tests/test_cursor.py +437 -0
  70. galaxy_tool_codemod-0.2.0/tests/test_datatype_format.py +57 -0
  71. galaxy_tool_codemod-0.2.0/tests/test_drop_redundant_param_name.py +86 -0
  72. galaxy_tool_codemod-0.2.0/tests/test_eligibility.py +124 -0
  73. galaxy_tool_codemod-0.2.0/tests/test_fix_from_work_dir_whitespace.py +76 -0
  74. galaxy_tool_codemod-0.2.0/tests/test_fix_interpreter.py +181 -0
  75. galaxy_tool_codemod-0.2.0/tests/test_fix_output_format_input.py +162 -0
  76. galaxy_tool_codemod-0.2.0/tests/test_fix_typos.py +146 -0
  77. galaxy_tool_codemod-0.2.0/tests/test_interpreter.py +122 -0
  78. galaxy_tool_codemod-0.2.0/tests/test_module.py +75 -0
  79. galaxy_tool_codemod-0.2.0/tests/test_normalize_boolean_values.py +105 -0
  80. galaxy_tool_codemod-0.2.0/tests/test_parse.py +82 -0
  81. galaxy_tool_codemod-0.2.0/tests/test_profile_semantics.py +376 -0
  82. galaxy_tool_codemod-0.2.0/tests/test_regressions.py +139 -0
  83. galaxy_tool_codemod-0.2.0/tests/test_reorder_param_attributes.py +145 -0
  84. galaxy_tool_codemod-0.2.0/tests/test_reorder_tool_attributes.py +76 -0
  85. galaxy_tool_codemod-0.2.0/tests/test_reorder_tool_children.py +125 -0
  86. galaxy_tool_codemod-0.2.0/tests/test_repair_help_rst.py +77 -0
  87. galaxy_tool_codemod-0.2.0/tests/test_replace_output_element.py +128 -0
  88. galaxy_tool_codemod-0.2.0/tests/test_runtime_fixes.py +49 -0
  89. galaxy_tool_codemod-0.2.0/tests/test_single_quote_command_vars.py +191 -0
  90. galaxy_tool_codemod-0.2.0/tests/test_smoke.py +10 -0
  91. galaxy_tool_codemod-0.2.0/tests/test_tokenize_version.py +121 -0
  92. galaxy_tool_codemod-0.2.0/tests/test_trim_attribute_whitespace.py +73 -0
  93. galaxy_tool_codemod-0.2.0/tests/test_update_profile.py +146 -0
  94. galaxy_tool_codemod-0.2.0/tests/test_upgrade_19_01.py +130 -0
  95. galaxy_tool_codemod-0.2.0/tests/test_upgrade_21_09.py +217 -0
  96. galaxy_tool_codemod-0.2.0/tests/test_upgrade_24_0.py +163 -0
  97. galaxy_tool_codemod-0.2.0/tests/test_upgrade_24_1.py +165 -0
  98. galaxy_tool_codemod-0.2.0/tests/test_upgrade_25_1.py +63 -0
  99. galaxy_tool_codemod-0.2.0/tests/test_upgrades.py +133 -0
  100. galaxy_tool_codemod-0.2.0/tests/test_wrap_command_cdata.py +88 -0
  101. galaxy_tool_codemod-0.2.0/tests/test_wrap_help_cdata.py +78 -0
@@ -0,0 +1,22 @@
1
+ # Build / dist artifacts
2
+ build/
3
+ dist/
4
+ *.egg-info/
5
+
6
+ # Python bytecode
7
+ __pycache__/
8
+ *.pyc
9
+
10
+ # Tooling caches
11
+ .mypy_cache/
12
+ .pytest_cache/
13
+ .ruff_cache/
14
+
15
+ # uv
16
+ .venv/
17
+ uv.lock
18
+
19
+ # Editor
20
+ .idea/
21
+ .vscode/
22
+ *.swp
@@ -0,0 +1,155 @@
1
+ # CLAUDE.md
2
+
3
+ Guidance for Claude Code working in this repository.
4
+
5
+ ## Project
6
+
7
+ `galaxy-tool-codemod` is the **structure** tier of the Galaxy tool
8
+ refactoring framework — one of seven tiers (this package depends only on tier 1
9
+ and the shared tier-0.5 rules metadata):
10
+
11
+ | Tier | Layer | Package | What it owns |
12
+ |---|---|---|---|
13
+ | 0.5 | **rule metadata** | `galaxy-tool-refactor-rules` | shared `RuleMeta` + `Violation` |
14
+ | 1 | **parsing & validation** | `galaxy-tool-source` | parsing, profile-aware XSD validation, typed views |
15
+ | 2 | **structure** | `galaxy-tool-codemod` *(this repo)* | structural mutations (attribute order, element shape) |
16
+ | 3 | **formatting** | `galaxy-tool-fmt` | whitespace / indentation / shorthand; the only tier that serialises canonical output XML |
17
+ | 3.5 | **advisory checks** | `galaxy-tool-lint` | detect-only IUC best-practice checks |
18
+ | 3.6 | **rule registry / rulesets** | `galaxy-tool-refactor-registry` | unified rule registry + rulesets; library-first facade |
19
+ | 4 | **app / CLI** | `galaxy-tool-refactor-cli` | composes the tiers via the facade (`format`/`upgrade`/`check`) |
20
+
21
+ This package supplies the **structural-refactor framework**: a
22
+ ``CodemodCommand`` **detect-primitive** base with tag-PascalCase dispatch
23
+ (``detect_Param``, ``detect_Tool``, …) whose ``apply`` is derived from
24
+ ``detect`` (each rule has a non-mutating detect phase + a fix phase), an
25
+ ``lxml``-backed ``Cursor``
26
+ with typed mutation primitives (``set_attribute``, ``delete_attribute``,
27
+ ``rename_attribute``, ``rename_tag``, ``reorder_attributes``,
28
+ ``reorder_children``, ``remove``, ``add_child``, ``attribute_names``,
29
+ ``set_text`` — the token-aware ``@PROFILE@`` rewrite, §21), a
30
+ ``Module`` wrapper (over a tier-1 ``ToolDocument``), a ``parse_module`` entry
31
+ point (plus ``MacroModule`` / ``parse_macro_module`` — the macro-file
32
+ counterparts wrapping a tier-1 ``MacroDocument``; ``Cursor`` is generic, so its
33
+ mutators work on a ``<macros>`` tree unchanged. The ``CodemodCommand`` base
34
+ stays tool-only until a macro-subject codemod needs it — see ``docs/decisions.md``
35
+ §20), and the bundled codemods exposed via two ordered pipeline
36
+ contracts in ``canonical.py``:
37
+
38
+ - ``canonical_codemods()`` = ``FixTypos`` → ``NormalizeBooleanValues`` →
39
+ ``RepairHelpRst`` → ``TrimAttributeWhitespace`` → ``ReplaceOutputElement`` →
40
+ ``DropRedundantParamName`` → ``ReorderParamAttributes`` →
41
+ ``ReorderToolAttributes`` → ``ReorderToolChildren`` → ``WrapCommandCdata`` →
42
+ ``WrapHelpCdata`` → ``SingleQuoteCommandVars`` (the safe canonical/format
43
+ pipeline; ``TrimAttributeWhitespace`` / ``ReplaceOutputElement`` /
44
+ ``DropRedundantParamName`` = GTR035.1/GTR036/GTR037, the planemo-parity fixes
45
+ (GTR035 partitioned: the name-trim residual is the GTR035.2 advisory, §33);
46
+ ``ReorderToolChildren`` = GTR013, IUC #52 element order, validity-safe because
47
+ ``<tool>`` is ``xs:all``; ``WrapCommandCdata`` / ``WrapHelpCdata`` =
48
+ GTR018.1/GTR019.1, IUC #34/#42, wrap a pure-text ``<command>``/``<help>`` body in
49
+ CDATA — behaviour-preserving, ``docs/decisions.md`` §29;
50
+ ``SingleQuoteCommandVars`` = GTR020.1, IUC #36, single-quote the *provably*-
51
+ single-valued Cheetah vars in ``<command>`` — behaviour-preserving but the first
52
+ canonical codemod to shift default-``format`` bytes vs the pre-partition output,
53
+ ``docs/decisions.md`` §30; ``RepairHelpRst`` = GTR089.1, repair
54
+ deterministically-fixable invalid ``<help>`` reStructuredText behind a
55
+ render-equivalence gate, ``docs/decisions.md`` §37. The five
56
+ ``Wrap…``/``SingleQuote…``/``RepairHelpRst``/``TrimAttributeWhitespace`` codemods
57
+ are the fixable ``.1`` half of a partition practice — their advisory ``.2`` residual lives in the check tier;
58
+ registry ``docs/decisions.md`` D10).
59
+ - ``AUTO_UPGRADE_CODEMODS`` = ``FixTypos`` → ``NormalizeBooleanValues`` →
60
+ ``UpgradeToLatest`` (the opt-in profile-upgrade pipeline).
61
+
62
+ ``ConvertHelpToMarkdown`` (GTR092, ``docs/decisions.md`` §38) is in neither
63
+ pipeline: the first of the catalog's **opt-in-command-only** codemods (no ruleset; applied
64
+ solely by the app's ``convert-help``) — the RST → Markdown ``<help>`` conversion
65
+ behind tier-1 ``rst_markdown``'s render-equivalence gate + the profile ≥ 24.2 XSD
66
+ gate. Its sibling ``TokenizeVersion`` (GTR094, §43) is the second:
67
+ the @TOOL_VERSION@/@VERSION_SUFFIX@ extraction behind the expansion-equality
68
+ gate, applied solely by ``tokenize-version``.
69
+
70
+ ``FixTypos``, ``NormalizeBooleanValues`` (GTR017 — boolean-case repair, the
71
+ ``True``/``False`` → ``true``/``false`` fix ``FixTypos`` cannot reach), and
72
+ ``UpgradeToLatest`` (which loops ``UpdateProfile`` + single-step ``upgrade_vN``
73
+ codemods from ``upgrades.py``) are validation-driven and override ``apply``. The upgrade registry is grown
74
+ empirically from ``corpus_check codemod`` discovery sweeps; see
75
+ ``docs/decisions.md`` §11–14, §16 for the canonical/upgrade split, and
76
+ §17–18 for the element-order codemod (GTR013) + the `codemod` sweep's
77
+ `--source combined` default.
78
+
79
+ **Tier independence:** this package does not depend on fmt. The
80
+ orchestration — running these pipelines and writing output through fmt's
81
+ serializer — lives in the tier-3.6 registry facade
82
+ (``galaxy-tool-refactor-registry``), which consumes ``canonical_codemods()``
83
+ (its ``run`` / the app's ``format``) and ``AUTO_UPGRADE_CODEMODS`` (its
84
+ ``upgrade``); the tier-4 app CLI (``galaxy-tool-refactor-cli``) is a thin
85
+ front-end over that facade. fmt's own CLI is cosmetic-only and does not
86
+ consume these contracts (see ``galaxy-tool-fmt/docs/decisions.md``
87
+ §D12).
88
+
89
+ The architecture rationale lives in `docs/architecture.md` (a working
90
+ copy forked from `galaxy-tool-source/docs/codemod-architecture.md` —
91
+ predates the M1-M3.5 implementation; the current shape is recorded in
92
+ `docs/decisions.md`). Milestone status and remaining work are in
93
+ `PLAN.md`.
94
+
95
+ ## Coding standards
96
+
97
+ Hand-written code follows **dignified-python**, vendored at the workspace root
98
+ `.claude/skills/dignified-python/`:
99
+
100
+ - LBYL over `try/except`. Exceptions only at the CLI error boundary
101
+ (chained `from e`) and at third-party API boundaries with no LBYL form.
102
+ - `pathlib.Path` with explicit `encoding="utf-8"` on text I/O.
103
+ - Keyword-only arguments after the first.
104
+ - Absolute imports, no re-exports, no `__all__`.
105
+ - No import-time side effects (`@cache` for module state).
106
+
107
+ `optimized-python` (`.claude/skills/optimized-python/`) is a secondary
108
+ reference; **dignified-python governs on conflict**.
109
+
110
+ ## Workflow
111
+
112
+ - **Test-driven development.** New code lands tests-first (failing test,
113
+ then minimum code to pass). One test module per source module under
114
+ `tests/`.
115
+ - **Plan-driven**: major changes get a written plan (either under
116
+ `~/.claude/plans/` for agent state, or in `PLAN.md` for repo-scoped
117
+ plans) before implementation.
118
+ - **Empirical claims must be backed by data.** Use the workspace corpus
119
+ artifacts (`../docs/corpus_data/`), `../scripts/measure.py`, and the
120
+ `corpus_check.py codemod` subcommand when answering questions about
121
+ real-world tool XML.
122
+ - **Decisions are recorded** in `docs/decisions.md` once they land
123
+ (mirror the parent's `docs/decisions.md` conventions: §-numbered,
124
+ each entry citing date and a reproduction command when relevant).
125
+ - See `galaxy-tool-source/docs/decisions.md` §9 for the three-tier
126
+ rationale.
127
+
128
+ ## Commands
129
+
130
+ Run these from the **workspace root** (`galaxy-tool-refactor/`):
131
+
132
+ - `uv sync` — install dependencies
133
+ - `uv run --package galaxy-tool-codemod pytest galaxy-tool-codemod/tests/` — run tests
134
+ - `uv run ruff check galaxy-tool-codemod/src galaxy-tool-codemod/tests` — lint
135
+ - `uv run mypy --config-file galaxy-tool-codemod/pyproject.toml galaxy-tool-codemod/src` — type-check (strict)
136
+ - `uv run python -m scripts.corpus_check codemod <dotted.module>:<ClassName>` — sweep a codemod across the corpus, retain failures as fixtures
137
+ - e.g. `uv run python -m scripts.corpus_check codemod galaxy_tool_codemod.codemods.reorder_param_attributes:ReorderParamAttributes`
138
+
139
+ ## Useful workspace references
140
+
141
+ - `galaxy-tool-source/README.md` — tier-1 public API
142
+ - `galaxy-tool-source/docs/decisions.md` §3 (trivia contract), §6 (corpus
143
+ stats), §9 (three-tier vision)
144
+ - `galaxy-tool-source/docs/codemod-architecture.md` — the original tier-2 design
145
+ - `galaxy-tool-refactor-registry/src/galaxy_tool_refactor_registry/apply.py` —
146
+ the tier-3.6 facade that runs ``canonical_codemods()`` order (consumed by
147
+ ``run`` / the app's ``format``); `rulesets.py` derives the `default` ruleset from
148
+ per-rule ``RuleMeta.rulesets`` membership
149
+ - `canonical.py` — the public ``canonical_codemods()`` and
150
+ ``AUTO_UPGRADE_CODEMODS`` pipeline contracts the registry facade consumes
151
+ - `codemods/` — bundled codemod implementations (verb-noun module names)
152
+ - `eligibility.py` — corpus-sweep profile-selection policy
153
+ - `../docs/corpus_data/combined_corpus_data.json` — every swept Galaxy
154
+ tool, indexed for ad-hoc analysis
155
+ - `../scripts/measure.py` — master script for empirical corpus queries
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Richard Burhans
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,134 @@
1
+ Metadata-Version: 2.4
2
+ Name: galaxy-tool-codemod
3
+ Version: 0.2.0
4
+ Summary: Structural-refactor framework for Galaxy tool XML — tier 2 of the galaxy-tool-source ecosystem.
5
+ Author: Richard Burhans
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Requires-Python: >=3.10
9
+ Requires-Dist: galaxy-tool-refactor-rules==0.2.0
10
+ Requires-Dist: galaxy-tool-source==0.2.0
11
+ Requires-Dist: lxml>=5
12
+ Requires-Dist: packaging>=23
13
+ Description-Content-Type: text/markdown
14
+
15
+ # galaxy-tool-codemod
16
+
17
+ A LibCST-shaped framework for structural refactors of Galaxy tool XML.
18
+ The **structure** tier of the Galaxy refactoring architecture:
19
+
20
+ | Tier | Layer | Package | Role |
21
+ |---|---|---|---|
22
+ | 0.5 | **rule metadata** | `galaxy-tool-refactor-rules` | shared `RuleMeta` descriptor |
23
+ | 1 | **parsing & validation** | `galaxy-tool-source` | parse · profile-aware validate · typed view |
24
+ | 2 | **structure** | **`galaxy-tool-codemod`** *(this repo)* | structural refactors |
25
+ | 3 | **formatting** | `galaxy-tool-fmt` | cosmetic `black`-like formatter |
26
+ | 3.5 | **advisory checks** | `galaxy-tool-lint` | detect-only checks |
27
+ | 3.6 | **rule registry / rulesets** | `galaxy-tool-refactor-registry` | unified rules + rulesets (facade) |
28
+ | 4 | **app / CLI** | `galaxy-tool-refactor-cli` | composes the tiers via the facade (`format` / `upgrade` / `check`) |
29
+
30
+ ## Status
31
+
32
+ M1–M3.5 shipped: framework primitives (`Module`, `Cursor`,
33
+ `CodemodCommand`), the `corpus_check.py codemod` sweep (retains failures
34
+ as regression fixtures), and two ordered pipeline contracts run by the
35
+ tier-4 app (`galaxy-tool-refactor-cli`):
36
+
37
+ - **`canonical_codemods()`** = `FixTypos → NormalizeBooleanValues → RepairHelpRst →
38
+ TrimAttributeWhitespace → ReplaceOutputElement → DropRedundantParamName →
39
+ ReorderParamAttributes → ReorderToolAttributes → ReorderToolChildren →
40
+ WrapCommandCdata → WrapHelpCdata → SingleQuoteCommandVars` — the safe
41
+ canonical/format pipeline (the app's `format` command), **derived** from the codemods
42
+ declaring the `"default"` ruleset and ordered by `meta.order` (no hardcoded tuple,
43
+ §36). Never changes `profile=`.
44
+ - **`AUTO_UPGRADE_CODEMODS`** = `FixTypos → NormalizeBooleanValues →
45
+ UpgradeToLatest` — the opt-in profile-upgrade pipeline (the app's `upgrade`
46
+ command).
47
+
48
+ The codemods:
49
+
50
+ - `FixTypos` — repair near-miss spelling typos so a
51
+ well-formed-but-globally-invalid tool validates (in both pipelines);
52
+ - `NormalizeBooleanValues` — rewrite Python-style boolean attribute values
53
+ (`True`/`Yes`/…) to canonical `xs:boolean` so a globally-invalid tool
54
+ validates; schema-type-aware and behaviour-preserving (in both pipelines);
55
+ - `UpgradeToLatest` — loop `UpdateProfile` (declare the newest profile the
56
+ tool validates at, bump-up-only) + single-step `upgrade_vN` codemods from
57
+ `upgrades.py` to bring a tool to the latest profile (`UpdateProfile` is a
58
+ building block run *inside* this loop, not a separate pipeline entry);
59
+ - `ReorderParamAttributes` / `ReorderToolAttributes` — IUC `<param>` and
60
+ documented `<tool>` attribute order;
61
+ - `ReorderToolChildren` — IUC `<tool>` child-element order (best-practice #52);
62
+ validity-safe because the `<tool>` content model is order-free (`xs:all`).
63
+
64
+ Every bundled codemod carries a `RuleMeta` GTR code; `catalog.coded_codemods()` is the
65
+ authoritative enumeration (the prose list above is illustrative — `format` also runs the
66
+ planemo-parity fixers GTR035–037 and the partition `.1` fixers GTR018.1/019.1/020.1 and
67
+ GTR089.1 `RepairHelpRst`). See `docs/decisions.md` §15–37.
68
+
69
+ The upgrade registry is grown empirically: the `corpus_check codemod`
70
+ sweep reports `STICKING POINT` versions still needing an `upgrade_vN`, and
71
+ each `upgrade_vN`'s advance count. Upgrades shipped: `Upgrade24_1`
72
+ (24.1 → 24.2), `Upgrade25_1` (25.1 → 26.0). See `docs/decisions.md` §11–14.
73
+
74
+ M4 (matcher language) and M5 (Cheetah reference resolver) are not yet
75
+ implemented — see `PLAN.md`.
76
+
77
+ ## Public API
78
+
79
+ ```python
80
+ from pathlib import Path
81
+
82
+ from galaxy_tool_codemod.parse import parse_module
83
+ from galaxy_tool_codemod.canonical import canonical_codemods
84
+
85
+ module = parse_module(Path("tool.xml"))
86
+ for codemod_cls in canonical_codemods():
87
+ codemod_cls().apply(module)
88
+ # module.document.tree now reflects the canonical structural form
89
+ ```
90
+
91
+ | Symbol | Purpose |
92
+ |---|---|
93
+ | `parse.parse_module(source)` | Entry point — accepts `Path \| bytes \| ToolDocument`. |
94
+ | `module.Module` | Frozen wrapper carrying `document`, `model`, `cursor`. |
95
+ | `cursor.Cursor` | lxml-backed view with read + typed mutation primitives. |
96
+ | `codemod.CodemodCommand` | Base for user-authored codemods (tag-PascalCase dispatch). |
97
+ | `codemods.fix_typos.FixTypos` | Repair near-miss typos until a globally-invalid tool validates (canonical, runs first). |
98
+ | `codemods.normalize_boolean_values.NormalizeBooleanValues` | Normalize Python-style boolean values (`True`/`Yes`/…) to `xs:boolean` until a globally-invalid tool validates (canonical, GTR017). |
99
+ | `codemods.repair_help_rst.RepairHelpRst` | Repair deterministically-fixable invalid `<help>` reStructuredText behind a render-equivalence gate (canonical, GTR089.1; the fix half of the GTR089 partition). |
100
+ | `upgrades.UpgradeToLatest` | In `AUTO_UPGRADE_CODEMODS`. Loop UpdateProfile + single-step upgrades to reach the latest profile. |
101
+ | `codemods.update_profile.UpdateProfile` | Declare the newest profile the tool validates at, bump-up-only. Building block run *inside* `UpgradeToLatest` — not itself a pipeline member. |
102
+ | `upgrades.UPGRADE_CODEMODS` | Registry: sticking version → its single-step upgrade codemod. |
103
+ | `codemods.upgrade_24_1.Upgrade24_1` | Single-step 24.1 → 24.2 (normalize `format` / `ftype`). |
104
+ | `codemods.upgrade_25_1.Upgrade25_1` | Single-step 25.1 → 26.0 (drop obsolete `<trackster_conf>`). |
105
+ | `codemods.reorder_param_attributes.ReorderParamAttributes` | IUC `<param>` attribute order. |
106
+ | `codemods.reorder_tool_attributes.ReorderToolAttributes` | Documented `<tool>` attribute prefix. |
107
+ | `codemods.reorder_tool_children.ReorderToolChildren` | IUC `<tool>` child-element order (#52). |
108
+ | `canonical.canonical_codemods()` | Ordered canonical/format pipeline (the app's `format` command). |
109
+ | `canonical.AUTO_UPGRADE_CODEMODS` | Ordered opt-in upgrade pipeline (the app's `upgrade` command). |
110
+ | `catalog.coded_codemods` | Every GTR-coded codemod, sorted by code (for the rule registry). |
111
+ | `eligibility.corpus_test_profile` | Codemod-sweep validation-profile policy (sweep default). |
112
+
113
+ ## Setup
114
+
115
+ From the workspace root:
116
+
117
+ ```sh
118
+ uv sync
119
+ uv run --package galaxy-tool-codemod pytest galaxy-tool-codemod/tests/
120
+ ```
121
+
122
+ ## Relationship to fmt and the app
123
+
124
+ Neither `galaxy-tool-fmt`'s library nor its CLI depends on this
125
+ package — fmt is cosmetic-only. Orchestration lives one tier up: the
126
+ `galaxy-tool-refactor` app CLI (`galaxy-tool-refactor-cli`) hard-depends
127
+ on this package and on fmt, runs `canonical_codemods()` (its `format`
128
+ command) or `AUTO_UPGRADE_CODEMODS` (its `upgrade` command), and writes
129
+ the result through fmt's serializer.
130
+
131
+ ## Coding standards
132
+
133
+ Hand-written code follows **dignified-python** (vendored at
134
+ `.claude/skills/dignified-python/`). See `CLAUDE.md`.