fix: Confusion with Overriding input After Snakemake Modularization#3714
fix: Confusion with Overriding input After Snakemake Modularization#3714johanneskoester merged 21 commits intomainfrom
Conversation
1. use name to map dependence 2. restrict cases when rule can be overwritten
|
Caution Review failedThe pull request is closed. 📝 WalkthroughWalkthroughCentral refactor of module/modifier system: introduces WorkflowModifier.modules and context management, centralizes path logic with chainable PathModifier, changes name-modifier and overwrite handling, makes Rule.expand_input return job_depends, and adjusts Workflow rule creation/overwrite and modifier application ordering. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant User as Snakefile/User
participant MI as ModuleInfo
participant WM as WorkflowModifier
participant PM as PathModifier
participant WF as Workflow
participant R as Rule
User->>MI: use_rules(rules, name_modifier, ...)
MI->>WM: construct WorkflowModifier(is_module?, path_modifier, rule_whitelist)
WM->>PM: attach/chain PathModifier(s)
MI->>WM: enter context (WM.__enter__) to apply module scope
WM->>WF: request avail_rulename(orig_name)
WF->>WM: avail_rulename / modify_rulename
alt name allowed
WF->>WF: add_rule(name, allow_overwrite?)
WF->>R: instantiate Rule (module_globals set)
R->>R: expand_input(...) → (input,mapping,job_depends,incomplete)
R->>PM: modify_path(...) (chained)
WF->>WF: resolve job_depends via workflow.get_rule(...)
else name blocked / duplicate
WM->>WF: raise CreateRuleException / skip
end
MI->>WM: exit context (WM.__exit__) to restore parent scope
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Pre-merge checks (3 passed, 2 warnings)❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
tests/test_modules_all/module-test/Snakefile (1)
9-9: Intentional broken input is fine; add a clarifying comment.This “not exist” sentinel is clearly here to ensure the override path is exercised. Add a short comment to avoid future confusion during test maintenance.
rule a: - input: "not exist" + # Intentionally invalid; will be overridden by 'use rule a ... with:' + input: "not exist"
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (5)
src/snakemake/modules.py(1 hunks)src/snakemake/rules.py(5 hunks)src/snakemake/workflow.py(5 hunks)tests/test_modules_all/Snakefile(1 hunks)tests/test_modules_all/module-test/Snakefile(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
⚙️ CodeRabbit configuration file
**/*.py: Do not try to improve formatting.
Do not suggest type annotations for functions that are defined inside of functions or methods.
Do not suggest type annotation of theselfargument of methods.
Do not suggest type annotation of theclsargument of classmethods.
Do not suggest return type annotation if a function or method does not contain areturnstatement.
Files:
src/snakemake/modules.pysrc/snakemake/workflow.pysrc/snakemake/rules.py
🧠 Learnings (1)
📚 Learning: 2024-10-06T14:09:54.370Z
Learnt from: johanneskoester
PR: snakemake/snakemake#3117
File: tests/test_wrapper/Snakefile:11-11
Timestamp: 2024-10-06T14:09:54.370Z
Learning: Changes made within test cases, such as in `tests/test_wrapper/Snakefile`, are for testing purposes and do not require updates to the project documentation.
Applied to files:
tests/test_modules_all/Snakefiletests/test_modules_all/module-test/Snakefile
🧬 Code graph analysis (3)
src/snakemake/modules.py (1)
src/snakemake/workflow.py (1)
globals(551-552)
src/snakemake/workflow.py (3)
src/snakemake/rules.py (5)
Rule(77-1340)name(131-132)name(135-136)lineno(139-140)snakefile(143-144)src/snakemake/parser.py (4)
Rule(745-854)name(785-798)name(962-975)lineno(74-75)src/snakemake/exceptions.py (1)
CreateRuleException(526-527)
src/snakemake/rules.py (1)
src/snakemake/workflow.py (6)
name(2305-2310)snakefile(1564-1569)rule(1762-2044)get_rule(675-686)input(2053-2058)check(619-627)
🪛 Ruff (0.12.2)
src/snakemake/rules.py
15-15: typing.Union imported but unused
Remove unused import: typing.Union
(F401)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (55)
- GitHub Check: tests (7, windows-latest, py312)
- GitHub Check: tests (8, windows-latest, py312)
- GitHub Check: tests (10, windows-latest, py312)
- GitHub Check: tests (10, ubuntu-latest, py312)
- GitHub Check: tests (9, ubuntu-latest, py311)
- GitHub Check: tests (10, ubuntu-latest, py311)
- GitHub Check: tests (10, ubuntu-latest, py313)
- GitHub Check: tests (10, windows-latest, py313)
- GitHub Check: tests (7, ubuntu-latest, py313)
- GitHub Check: tests (9, windows-latest, py312)
- GitHub Check: tests (9, ubuntu-latest, py313)
- GitHub Check: tests (8, ubuntu-latest, py312)
- GitHub Check: tests (8, ubuntu-latest, py311)
- GitHub Check: tests (8, ubuntu-latest, py313)
- GitHub Check: tests (9, windows-latest, py313)
- GitHub Check: tests (6, ubuntu-latest, py312)
- GitHub Check: tests (8, windows-latest, py313)
- GitHub Check: tests (7, ubuntu-latest, py312)
- GitHub Check: tests (7, ubuntu-latest, py311)
- GitHub Check: tests (4, ubuntu-latest, py313)
- GitHub Check: tests (9, ubuntu-latest, py312)
- GitHub Check: tests (6, ubuntu-latest, py313)
- GitHub Check: tests (7, windows-latest, py313)
- GitHub Check: tests (6, windows-latest, py312)
- GitHub Check: tests (5, windows-latest, py312)
- GitHub Check: tests (6, windows-latest, py313)
- GitHub Check: tests (5, ubuntu-latest, py313)
- GitHub Check: tests (6, ubuntu-latest, py311)
- GitHub Check: tests (5, ubuntu-latest, py312)
- GitHub Check: tests (5, windows-latest, py313)
- GitHub Check: tests (3, windows-latest, py313)
- GitHub Check: tests (5, ubuntu-latest, py311)
- GitHub Check: tests (4, macos-latest, py313)
- GitHub Check: tests (4, macos-latest, py312)
- GitHub Check: tests (4, windows-latest, py313)
- GitHub Check: tests (4, windows-latest, py312)
- GitHub Check: tests (4, ubuntu-latest, py311)
- GitHub Check: tests (3, windows-latest, py312)
- GitHub Check: tests (3, ubuntu-latest, py313)
- GitHub Check: tests (2, macos-latest, py313)
- GitHub Check: tests (1, ubuntu-latest, py312)
- GitHub Check: tests (2, ubuntu-latest, py311)
- GitHub Check: tests (4, ubuntu-latest, py312)
- GitHub Check: tests (3, ubuntu-latest, py312)
- GitHub Check: tests (2, windows-latest, py312)
- GitHub Check: tests (2, windows-latest, py313)
- GitHub Check: tests (3, ubuntu-latest, py311)
- GitHub Check: tests (1, macos-latest, py313)
- GitHub Check: tests (2, ubuntu-latest, py313)
- GitHub Check: tests (1, ubuntu-latest, py311)
- GitHub Check: tests (2, ubuntu-latest, py312)
- GitHub Check: tests (1, ubuntu-latest, py313)
- GitHub Check: tests (1, macos-latest, py312)
- GitHub Check: tests (1, windows-latest, py312)
- GitHub Check: tests (1, windows-latest, py313)
🔇 Additional comments (8)
src/snakemake/modules.py (1)
165-183: Do not inherit parent namespace into child modifiers — LGTM.Dropping namespace inheritance prevents cross-namespace overwrites; aligns with the PR’s goal to constrain overwrites to the originating namespace.
tests/test_modules_all/Snakefile (1)
18-19: Good targeted override to reproduce #3713.This narrow “use rule a … with: input” correctly asserts the fix without broadening test scope.
src/snakemake/rules.py (4)
78-79: Constructor type annotation — LGTM.Clearer signature; no behavior change.
128-129: Explicit typing for module_globals — LGTM.Simple clarity improvement; consistent with usage.
464-467: Confirm rename safety:set_input()runs after rule renaming
Thedecoratefunction appliesruleinfo.nametorule.namebefore invokingrule.set_input(), so the storeditem.rule.nameis always the final name (snakemake.readthedocs.io).
898-916: expand_input return-shape change: audit call sites.Only one call site found in
src/snakemake/jobs.py(lines 251–256), where the unpacking already matches the new 4-tuple signature:( self.input, input_mapping, self.dependencies, self.incomplete_input_expand, ) = self.rule.expand_input(...)No other consumers of
expand_inputexist. Confirm that all downstream code usesself.dependenciesas the newjob_depends(mapping from file to Rule) rather than the old raw dependencies mapping.src/snakemake/workflow.py (2)
177-177: Typed rules registry — LGTM.Improves readability without behavior changes.
1804-1807: Rename mapping simplification — LGTM.Directly updating the mapping is cleaner and preserves order.
|
Now I notice that there is another bug: rule run:
shell: "cp {input.rds} {output.txt}"B.smk: module A:
snakefile: "A.smk"
use rule run from A as A_run with:
input: rds=config.get("b", "B.smk"),
log: "aaalog",C.smk: rule all:
input: "output_run.txt",
module B:
snakefile: "B.smk"
config: {"b": "invalid-file"}
use rule A_run from B as B_A_run with:
input: rds="README.md",run: It seems that the overwrite ruleinfo in C.smk (outer one) is lost... |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/snakemake/workflow.py (1)
1782-1794: Do not mutaterule.module_globals["__name__"]on overwrite.Setting it to None alters the shared module globals, breaking subsequent same-namespace overwrites and causing nested-import overrides to be lost (matches the issue this PR targets).
rule.is_checkpoint = checkpoint rule.module_globals = self.modifier.globals - if is_overwrite: - rule.module_globals["__name__"] = None
♻️ Duplicate comments (1)
src/snakemake/modules.py (1)
162-176: Fresh modifier state for top-level context is correct.Initializing new globals/rules/rule_proxies/modules and setting
__name__to the namespace matches the intended design.
🧹 Nitpick comments (3)
src/snakemake/rules.py (1)
34-49: Deduplicate import:flagis imported twice.Remove the second
flagfrom the import list to avoid F401/F811 noise.from snakemake.io import ( @@ - flag, get_flag_value, @@ - flag, is_callable, ReportObject, )src/snakemake/workflow.py (1)
648-667: Improve overwrite error to include origin namespace.Helpful for diagnosing why an overwrite is blocked.
- if not is_overwrite: - raise CreateRuleException( - f"The name {name} is already used by another rule", - lineno=lineno, - snakefile=snakefile, - ) + if not is_overwrite: + origin_ns = self.get_rule(name).module_globals["__name__"] + raise CreateRuleException( + f"The name {name} is already used by a rule from namespace {origin_ns!r}. " + f"Only same-namespace overwrites are allowed here.", + lineno=lineno, + snakefile=snakefile, + )src/snakemake/modules.py (1)
190-200: Type-ignore on PathModifier: optional cleanup.If feasible, align PathModifier signature or local types to drop
# type: ignore[reportArgumentType]. Non-blocking.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
src/snakemake/modules.py(4 hunks)src/snakemake/rules.py(4 hunks)src/snakemake/workflow.py(6 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
⚙️ CodeRabbit configuration file
**/*.py: Do not try to improve formatting.
Do not suggest type annotations for functions that are defined inside of functions or methods.
Do not suggest type annotation of theselfargument of methods.
Do not suggest type annotation of theclsargument of classmethods.
Do not suggest return type annotation if a function or method does not contain areturnstatement.
Files:
src/snakemake/rules.pysrc/snakemake/workflow.pysrc/snakemake/modules.py
🧠 Learnings (2)
📚 Learning: 2025-08-28T13:17:37.677Z
Learnt from: Hocnonsense
PR: snakemake/snakemake#3714
File: src/snakemake/workflow.py:637-665
Timestamp: 2025-08-28T13:17:37.677Z
Learning: In Snakemake, rule.module_globals["__name__"] always exists and defaults to "snakemake.workflow" when not modified, so direct dictionary access is safe without needing .get().
Applied to files:
src/snakemake/rules.pysrc/snakemake/workflow.pysrc/snakemake/modules.py
📚 Learning: 2025-08-28T13:15:55.211Z
Learnt from: Hocnonsense
PR: snakemake/snakemake#3714
File: src/snakemake/modules.py:195-195
Timestamp: 2025-08-28T13:15:55.211Z
Learning: In src/snakemake/modules.py, the line `self.globals["__name__"] = namespace` is intentionally designed to set __name__ to None when namespace is None (i.e., when not inside a module context, for top-level workflows). This is correct behavior and should not be guarded.
Applied to files:
src/snakemake/workflow.pysrc/snakemake/modules.py
🧬 Code graph analysis (3)
src/snakemake/rules.py (1)
src/snakemake/workflow.py (6)
name(2308-2313)snakefile(1567-1572)rule(1765-2047)get_rule(678-689)input(2056-2061)check(622-630)
src/snakemake/workflow.py (2)
src/snakemake/rules.py (5)
Rule(76-1339)name(130-131)name(134-135)lineno(138-139)snakefile(142-143)src/snakemake/exceptions.py (1)
CreateRuleException(526-527)
src/snakemake/modules.py (3)
src/snakemake/workflow.py (7)
modifier(542-543)globals(554-555)wildcard_constraints(550-551)rules(589-590)rules(769-770)modules(546-547)config(1756-1757)src/snakemake/common/__init__.py (1)
Rules(253-271)src/snakemake/path_modifier.py (2)
PathModifier(22-145)replace_prefix(69-99)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (56)
- GitHub Check: tests (10, ubuntu-latest, py311)
- GitHub Check: tests (10, ubuntu-latest, py313)
- GitHub Check: tests (10, ubuntu-latest, py312)
- GitHub Check: tests (9, windows-latest, py313)
- GitHub Check: tests (10, windows-latest, py313)
- GitHub Check: tests (10, windows-latest, py312)
- GitHub Check: tests (9, ubuntu-latest, py313)
- GitHub Check: tests (8, macos-latest, py312)
- GitHub Check: tests (9, windows-latest, py312)
- GitHub Check: tests (9, ubuntu-latest, py311)
- GitHub Check: tests (9, macos-latest, py313)
- GitHub Check: tests (9, ubuntu-latest, py312)
- GitHub Check: tests (8, windows-latest, py313)
- GitHub Check: tests (8, ubuntu-latest, py312)
- GitHub Check: tests (8, windows-latest, py312)
- GitHub Check: tests (4, ubuntu-latest, py312)
- GitHub Check: tests (8, ubuntu-latest, py313)
- GitHub Check: tests (8, ubuntu-latest, py311)
- GitHub Check: tests (5, ubuntu-latest, py312)
- GitHub Check: tests (7, ubuntu-latest, py313)
- GitHub Check: tests (6, windows-latest, py312)
- GitHub Check: tests (6, windows-latest, py313)
- GitHub Check: tests (7, windows-latest, py313)
- GitHub Check: tests (7, windows-latest, py312)
- GitHub Check: tests (6, ubuntu-latest, py312)
- GitHub Check: tests (7, ubuntu-latest, py312)
- GitHub Check: tests (6, ubuntu-latest, py311)
- GitHub Check: tests (7, ubuntu-latest, py311)
- GitHub Check: tests (5, windows-latest, py312)
- GitHub Check: tests (5, windows-latest, py313)
- GitHub Check: tests (6, ubuntu-latest, py313)
- GitHub Check: tests (4, windows-latest, py312)
- GitHub Check: tests (4, windows-latest, py313)
- GitHub Check: tests (5, ubuntu-latest, py311)
- GitHub Check: tests (5, ubuntu-latest, py313)
- GitHub Check: tests (4, ubuntu-latest, py311)
- GitHub Check: tests (4, ubuntu-latest, py313)
- GitHub Check: tests (3, windows-latest, py313)
- GitHub Check: tests (3, ubuntu-latest, py313)
- GitHub Check: tests (3, windows-latest, py312)
- GitHub Check: tests (3, ubuntu-latest, py311)
- GitHub Check: tests (3, ubuntu-latest, py312)
- GitHub Check: tests (2, ubuntu-latest, py311)
- GitHub Check: tests (1, ubuntu-latest, py313)
- GitHub Check: tests (2, windows-latest, py312)
- GitHub Check: tests (2, windows-latest, py313)
- GitHub Check: tests (2, ubuntu-latest, py313)
- GitHub Check: tests (1, windows-latest, py313)
- GitHub Check: tests (2, macos-latest, py313)
- GitHub Check: tests (1, windows-latest, py312)
- GitHub Check: tests (1, macos-latest, py313)
- GitHub Check: tests (1, ubuntu-latest, py312)
- GitHub Check: tests (2, ubuntu-latest, py312)
- GitHub Check: tests (1, macos-latest, py312)
- GitHub Check: tests (1, ubuntu-latest, py311)
- GitHub Check: apidocs
🔇 Additional comments (7)
src/snakemake/rules.py (2)
463-466: Good: store dependency by rule name, not object.Switching to
item.rule.nameavoids stale object refs across modules and eases (re)resolution viaworkflow.get_rule().
127-128: Initialize or assertmodule_globalsbefore any access.Attribute is only annotated here; it’s assigned later in workflow.rule(). If any code path accesses it earlier, it will raise. If guaranteed safe, add a brief comment; otherwise set a default.
- self.module_globals: dict + # Set by Workflow.rule() immediately after add_rule(); intentionally uninitialized here. + self.module_globals: dictsrc/snakemake/workflow.py (3)
177-177: Typed rules mapping: good.
OrderedDict[str, Rule]improves clarity for readers and type checkers.
545-548: Exposemodulesvia property: good.Makes module registry consistently reflect the current modifier context.
1765-1810: Name reassignment logic: safe; deletion and reinsertion are adjacent with no intervening lookups. No change requested.src/snakemake/modules.py (2)
55-55: Capture parent modifier explicitly: good.This makes intent clear and simplifies rule proxy inheritance.
82-106: Module use isolation + proxy inheritance: good.Creating a fresh modifier for the module and then inheriting proxies into the parent preserves isolation while exposing rule proxies as expected.
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/snakemake/path_modifier.py (1)
77-84: Robustness:path.startswith("..")may break for non-string paths.
pathcan be apathlib.PathorAnnotatedString. Callingstartswithdirectly can raise forPath. Convert tostrto be safe.Apply:
- or path.startswith("..") + or str(path).startswith("..")
🧹 Nitpick comments (1)
src/snakemake/path_modifier.py (1)
18-25: PathModifier chaining: constructor looks good, but consider guarding against accidental cycles.Since chaining now allows arbitrary composition, a defensive check (e.g., detect self in the inner chain) would prevent rare infinite-recursion bugs if a modifier is mistakenly reused.
Apply this minimal guard:
def __init__( self, replace_prefix: dict | None, prefix: str | None, workflow, inner_modifier: "PathModifier | None" = None, ): self.skip_properties: set = set() self.workflow = workflow - self.inner_modifier = inner_modifier + # Guard: prevent accidental self-references in chains + self.inner_modifier = inner_modifier if inner_modifier is not self else NoneAlso applies to: 27-27
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
src/snakemake/modules.py(6 hunks)src/snakemake/path_modifier.py(2 hunks)src/snakemake/ruleinfo.py(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/snakemake/ruleinfo.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
⚙️ CodeRabbit configuration file
**/*.py: Do not try to improve formatting.
Do not suggest type annotations for functions that are defined inside of functions or methods.
Do not suggest type annotation of theselfargument of methods.
Do not suggest type annotation of theclsargument of classmethods.
Do not suggest return type annotation if a function or method does not contain areturnstatement.
Files:
src/snakemake/path_modifier.pysrc/snakemake/modules.py
🧠 Learnings (2)
📚 Learning: 2025-08-28T13:15:55.232Z
Learnt from: Hocnonsense
PR: snakemake/snakemake#3714
File: src/snakemake/modules.py:195-195
Timestamp: 2025-08-28T13:15:55.232Z
Learning: In src/snakemake/modules.py, the line `self.globals["__name__"] = namespace` is intentionally designed to set __name__ to None when namespace is None (i.e., when not inside a module context, for top-level workflows). This is correct behavior and should not be guarded.
Applied to files:
src/snakemake/modules.py
📚 Learning: 2025-08-28T13:17:37.693Z
Learnt from: Hocnonsense
PR: snakemake/snakemake#3714
File: src/snakemake/workflow.py:637-665
Timestamp: 2025-08-28T13:17:37.693Z
Learning: In Snakemake, rule.module_globals["__name__"] always exists and defaults to "snakemake.workflow" when not modified, so direct dictionary access is safe without needing .get().
Applied to files:
src/snakemake/modules.py
🧬 Code graph analysis (2)
src/snakemake/path_modifier.py (1)
src/snakemake/io/__init__.py (7)
is_callable(310-311)is_callable(940-941)is_callable(1147-1152)is_flagged(955-958)AnnotatedString(929-949)flag(961-971)get_flag_value(1155-1160)
src/snakemake/modules.py (3)
src/snakemake/workflow.py (7)
modifier(542-543)globals(554-555)wildcard_constraints(550-551)rules(589-590)rules(769-770)config(1756-1757)modules(546-547)src/snakemake/path_modifier.py (2)
PathModifier(16-153)replace_prefix(71-75)src/snakemake/common/__init__.py (1)
Rules(253-271)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (55)
- GitHub Check: tests (10, windows-latest, py312)
- GitHub Check: tests (8, windows-latest, py313)
- GitHub Check: tests (10, windows-latest, py313)
- GitHub Check: tests (10, ubuntu-latest, py312)
- GitHub Check: tests (10, ubuntu-latest, py313)
- GitHub Check: tests (9, windows-latest, py313)
- GitHub Check: tests (9, ubuntu-latest, py312)
- GitHub Check: tests (10, ubuntu-latest, py311)
- GitHub Check: tests (9, ubuntu-latest, py311)
- GitHub Check: tests (9, ubuntu-latest, py313)
- GitHub Check: tests (9, windows-latest, py312)
- GitHub Check: tests (8, ubuntu-latest, py313)
- GitHub Check: tests (8, ubuntu-latest, py311)
- GitHub Check: tests (8, ubuntu-latest, py312)
- GitHub Check: tests (8, windows-latest, py312)
- GitHub Check: tests (7, windows-latest, py312)
- GitHub Check: tests (7, windows-latest, py313)
- GitHub Check: tests (7, ubuntu-latest, py311)
- GitHub Check: tests (7, ubuntu-latest, py312)
- GitHub Check: tests (7, ubuntu-latest, py313)
- GitHub Check: tests (6, ubuntu-latest, py313)
- GitHub Check: tests (5, windows-latest, py313)
- GitHub Check: tests (5, macos-latest, py312)
- GitHub Check: tests (6, windows-latest, py313)
- GitHub Check: tests (6, windows-latest, py312)
- GitHub Check: tests (5, ubuntu-latest, py313)
- GitHub Check: tests (6, ubuntu-latest, py311)
- GitHub Check: tests (5, windows-latest, py312)
- GitHub Check: tests (5, ubuntu-latest, py312)
- GitHub Check: tests (6, ubuntu-latest, py312)
- GitHub Check: tests (4, ubuntu-latest, py312)
- GitHub Check: tests (5, ubuntu-latest, py311)
- GitHub Check: tests (3, ubuntu-latest, py313)
- GitHub Check: tests (4, windows-latest, py313)
- GitHub Check: tests (4, windows-latest, py312)
- GitHub Check: tests (4, ubuntu-latest, py313)
- GitHub Check: tests (3, windows-latest, py312)
- GitHub Check: tests (3, windows-latest, py313)
- GitHub Check: tests (4, ubuntu-latest, py311)
- GitHub Check: tests (2, windows-latest, py312)
- GitHub Check: tests (2, windows-latest, py313)
- GitHub Check: tests (3, ubuntu-latest, py311)
- GitHub Check: tests (3, ubuntu-latest, py312)
- GitHub Check: tests (2, macos-latest, py313)
- GitHub Check: tests (2, macos-latest, py312)
- GitHub Check: tests (1, macos-latest, py313)
- GitHub Check: tests (2, ubuntu-latest, py313)
- GitHub Check: tests (2, ubuntu-latest, py312)
- GitHub Check: tests (2, ubuntu-latest, py311)
- GitHub Check: tests (1, macos-latest, py312)
- GitHub Check: tests (1, windows-latest, py312)
- GitHub Check: tests (1, ubuntu-latest, py311)
- GitHub Check: tests (1, ubuntu-latest, py312)
- GitHub Check: tests (1, ubuntu-latest, py313)
- GitHub Check: tests (1, windows-latest, py313)
🔇 Additional comments (7)
src/snakemake/path_modifier.py (1)
71-76: Chaining order is child-first then parent; confirm this matches expected override semantics.
replace_prefix()applies the current modifier first, then delegates to the parent viainner_modifier. This makes child mappings run before inherited mappings. That seems right (parent won’t re-map absolute paths), but please confirm with nested module overrides from #3713 that top-level overrides always prevail.Would you run the new tests with a case where the child maps to an absolute path and the parent has a non-absolute replace_prefix, ensuring the parent does not re-prefix the child result?
src/snakemake/modules.py (6)
55-56: Good: path modification now composes via a chained PathModifier.Passing the parent modifier’s
path_modifierinto the module-levelPathModifierenables deterministic, ordered composition and should address path handling in nested imports.Also applies to: 70-72
143-159: Constructor surface changed substantially; ensure all call sites are updated.
WorkflowModifier.__init__dropped several params and addedparent_modifier,path_modifier, andrule_proxies. Please verify there are no stale call sites (plugins, wrappers, or downstream libs) still using the old signature.Run a repo-wide search for
WorkflowModifier(and check for positional-arg construction that could be misaligned after this change.
160-181: Two init paths: clarify and test both branches.
- Branch A (no parent, globals is None) pulls from
workflow.vanilla_globalsand inheritsparent_modifier = workflow.modifier.- Branch B (no parent, globals provided) creates fresh
Rules()and a rootPathModifier.Edge cases to verify:
- That
rule_proxiesandpath_modifierare always non-None (covered when passed byModuleInfo.use_rules, but also validate direct construction paths).- That
self.globals["rules"]always points to the intendedRulesnamespace for the include being processed.Please add/confirm tests that exercise:
- use rule within a module (globals is None path),
- top-level module modifier (globals provided path).
182-190: Potential parent chain skip in nested “use rule … with” flow.Setting
self.parent_modifier = parent_modifier.parent_modifierintentionally skips one level. This changes ancestry for further resolutions (e.g., name/path modifiers). If that’s required to avoid double-application, great—otherwise it can cause surprising behavior for deeply nested use/with chains.Can you confirm with a 3-level nested import/use/with scenario that:
- name mapping and ruleinfo overrides are taken from the expected scope, and
- path modification chain still includes all intended ancestors exactly once?
196-196: Behavioral change:skip_configfilenow tied toconfig is not None.Previously this may have been controlled differently. Confirm that providing a
configdict toWorkflowModifierdisables configfile loading only where intended, and does not inadvertently suppress configfile usage for unrelated includes.Consider a test where a module receives
configwhile sibling includes rely on configfile, ensuring only the module’s include is affected.
226-228: LGTM: path modification now flows through the chained PathModifier.This centralizes path handling and should resolve the override propagation issues described in #3713.
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/snakemake/workflow.py (1)
633-658: Overwrite semantics are broken: no namespace check andis_overwriteis never set.Current logic:
- Allows overwrites when
allow_overwrite=Truewithout checking origin namespace.- Always returns
is_overwrite=False(even on overwrite), so callers can’t react.- Increments
rule_counton overwrite.This undermines the PR goal (“restrict when a rule can be overwritten”). Fix by: (a) gating overwrite to same namespace, (b) setting
is_overwrite=Truewhen replacing, and (c) not bumping counters or default target on overwrite.def add_rule( self, - name: str, + name: str, ... -): +): """ Add a rule. """ - if self.is_rule(name) and not allow_overwrite: - raise CreateRuleException( - f"The name {name} is already used by another rule", - lineno=lineno, - snakefile=snakefile, - ) - else: - is_overwrite = False - self.rule_count += 1 - if not self.default_target: - self.default_target = name + if self.is_rule(name): + if not allow_overwrite: + raise CreateRuleException( + f"The name {name} is already used by another rule", + lineno=lineno, + snakefile=snakefile, + ) + # Restrict overwrite to same namespace + origin_ns = self.get_rule(name).module_globals["__name__"] + if origin_ns != self.modifier.namespace: + raise CreateRuleException( + f"Cannot overwrite rule {name} from a different module " + f"(existing={origin_ns!r}, current={self.modifier.namespace!r})", + lineno=lineno, + snakefile=snakefile, + ) + is_overwrite = True + else: + is_overwrite = False + self.rule_count += 1 + if not self.default_target: + self.default_target = name rule = Rule(name, self, lineno=lineno, snakefile=snakefile) self._rules[rule.name] = rule self.modifier.rules.add(rule) return is_overwrite
♻️ Duplicate comments (2)
src/snakemake/workflow.py (1)
1777-1779: Do not mutatemodule_globals["__name__"]on overwrite.This was previously flagged; it can corrupt module identity and block subsequent same-namespace overwrites. Remove the mutation and rely on the namespace gate in
add_rule().- if is_overwrite: - rule.module_globals["__name__"] = None + # no mutation of module globals on overwritesrc/snakemake/modules.py (1)
193-193: LGTM: set name from namespace (intentional).
Setting__name__directly—allowing None outside a module—is by design per prior discussion.
🧹 Nitpick comments (1)
src/snakemake/workflow.py (1)
2388-2391: Guardavail_rulenamecall for None alias.
name_modifiercan be None in local inheritance; callingavail_rulename(None)may be unintended. Consider short-circuiting whenname_modifier is None.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (6)
src/snakemake/modules.py(5 hunks)src/snakemake/workflow.py(10 hunks)tests/test_module_nested/Snakefile(1 hunks)tests/test_module_nested/module_deep.smk(1 hunks)tests/test_module_nested/module_shallow.smk(2 hunks)tests/tests.py(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
⚙️ CodeRabbit configuration file
**/*.py: Do not try to improve formatting.
Do not suggest type annotations for functions that are defined inside of functions or methods.
Do not suggest type annotation of theselfargument of methods.
Do not suggest type annotation of theclsargument of classmethods.
Do not suggest return type annotation if a function or method does not contain areturnstatement.
Files:
tests/tests.pysrc/snakemake/modules.pysrc/snakemake/workflow.py
🧠 Learnings (3)
📚 Learning: 2024-10-06T14:09:54.370Z
Learnt from: johanneskoester
PR: snakemake/snakemake#3117
File: tests/test_wrapper/Snakefile:11-11
Timestamp: 2024-10-06T14:09:54.370Z
Learning: Changes made within test cases, such as in `tests/test_wrapper/Snakefile`, are for testing purposes and do not require updates to the project documentation.
Applied to files:
tests/tests.pytests/test_module_nested/Snakefile
📚 Learning: 2025-08-28T13:15:55.232Z
Learnt from: Hocnonsense
PR: snakemake/snakemake#3714
File: src/snakemake/modules.py:195-195
Timestamp: 2025-08-28T13:15:55.232Z
Learning: In src/snakemake/modules.py, the line `self.globals["__name__"] = namespace` is intentionally designed to set __name__ to None when namespace is None (i.e., when not inside a module context, for top-level workflows). This is correct behavior and should not be guarded.
Applied to files:
src/snakemake/modules.pysrc/snakemake/workflow.py
📚 Learning: 2025-08-28T13:17:37.693Z
Learnt from: Hocnonsense
PR: snakemake/snakemake#3714
File: src/snakemake/workflow.py:637-665
Timestamp: 2025-08-28T13:17:37.693Z
Learning: In Snakemake, rule.module_globals["__name__"] always exists and defaults to "snakemake.workflow" when not modified, so direct dictionary access is safe without needing .get().
Applied to files:
src/snakemake/modules.pysrc/snakemake/workflow.py
🧬 Code graph analysis (3)
tests/tests.py (2)
src/snakemake/workflow.py (3)
run(2300-2301)snakefile(1557-1562)config(1746-1747)tests/common.py (2)
run(152-496)dpath(33-36)
src/snakemake/modules.py (4)
src/snakemake/common/__init__.py (2)
Rules(253-271)_register_rule(259-260)src/snakemake/exceptions.py (1)
CreateRuleException(526-527)src/snakemake/path_modifier.py (2)
PathModifier(16-153)replace_prefix(71-75)src/snakemake/workflow.py (6)
rules(589-590)rules(759-760)modifier(542-543)snakefile(1557-1562)name(2293-2298)rule(1755-2032)
src/snakemake/workflow.py (3)
src/snakemake/rules.py (6)
Rule(76-1339)name(130-131)name(134-135)lineno(138-139)snakefile(142-143)add(1346-1350)src/snakemake/modules.py (1)
avail_rulename(239-249)src/snakemake/ruleinfo.py (1)
RuleInfo(14-111)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (42)
- GitHub Check: tests (10, windows-latest, py313)
- GitHub Check: tests (4, ubuntu-latest, py311)
- GitHub Check: tests (10, ubuntu-latest, py311)
- GitHub Check: tests (9, ubuntu-latest, py312)
- GitHub Check: tests (9, windows-latest, py313)
- GitHub Check: tests (8, ubuntu-latest, py313)
- GitHub Check: tests (10, ubuntu-latest, py313)
- GitHub Check: tests (8, windows-latest, py313)
- GitHub Check: tests (10, ubuntu-latest, py312)
- GitHub Check: tests (9, ubuntu-latest, py311)
- GitHub Check: tests (9, ubuntu-latest, py313)
- GitHub Check: tests (8, ubuntu-latest, py311)
- GitHub Check: tests (7, windows-latest, py313)
- GitHub Check: tests (6, macos-latest, py313)
- GitHub Check: tests (8, ubuntu-latest, py312)
- GitHub Check: tests (7, ubuntu-latest, py312)
- GitHub Check: tests (7, windows-latest, py312)
- GitHub Check: tests (7, ubuntu-latest, py311)
- GitHub Check: tests (7, ubuntu-latest, py313)
- GitHub Check: tests (5, macos-latest, py313)
- GitHub Check: tests (6, windows-latest, py313)
- GitHub Check: tests (5, windows-latest, py312)
- GitHub Check: tests (6, ubuntu-latest, py311)
- GitHub Check: tests (5, ubuntu-latest, py311)
- GitHub Check: tests (6, ubuntu-latest, py312)
- GitHub Check: tests (5, windows-latest, py313)
- GitHub Check: tests (5, ubuntu-latest, py312)
- GitHub Check: tests (5, macos-latest, py312)
- GitHub Check: tests (6, ubuntu-latest, py313)
- GitHub Check: tests (5, ubuntu-latest, py313)
- GitHub Check: tests (4, windows-latest, py312)
- GitHub Check: tests (3, windows-latest, py312)
- GitHub Check: tests (4, macos-latest, py312)
- GitHub Check: tests (4, windows-latest, py313)
- GitHub Check: tests (3, ubuntu-latest, py313)
- GitHub Check: tests (3, macos-latest, py313)
- GitHub Check: tests (3, ubuntu-latest, py311)
- GitHub Check: tests (2, windows-latest, py313)
- GitHub Check: tests (3, ubuntu-latest, py312)
- GitHub Check: tests (3, windows-latest, py313)
- GitHub Check: tests (1, ubuntu-latest, py313)
- GitHub Check: tests (2, windows-latest, py312)
🔇 Additional comments (24)
tests/tests.py (2)
1580-1588: Confirm targeting a log file is the intended failure mode.
targets={"aaalog"}points to a log, not an output. In dry-run, DAG resolution typically doesn’t considerlog:as producible targets, so failure may stem from “no rule to produce” rather than overwrite semantics. Please confirm this is the expected failure reason. If not, target a rule or an actual output.
1589-1596: Ignore the suggestion to targetB_A_runhere—this test invokesmodule_shallow.smk, where the imported rule is aliased asA_run, notB_A_run.Likely an incorrect or invalid review comment.
tests/test_module_nested/Snakefile (2)
1-7: LGTM: module config/replace_prefix setup.The
replace_prefix: {"b-/A/": ""}composition looks intentional for the nested module path adjustments.
10-16: Verify that the input override matches the aliased rule’s interface.
with: input: rds="Snakefile"assumes the aliasA_run(defined in the shallow module) exposesrds. This is fine if the prior alias sets thatinput. Please confirm this chain is guaranteed at parse time.tests/test_module_nested/module_shallow.smk (1)
11-15: Double alias relies on overwrite semantics; ensure tests assert this behavior.Defining
A_runtwice is a good probe for “same-module overwrite only.” Make sure the engine only permits this overwrite within the same namespace, and that tests will fail if cross-namespace overwrites slip through.Also applies to: 17-21
tests/test_module_nested/module_deep.smk (1)
4-9: LGTM: rule graph for run → run2 and alias run3.Clear outputs and simple shell. Good assertions.
Also applies to: 11-18, 20-22, 25-27
src/snakemake/workflow.py (3)
177-177: LGTM: typed _rules initialization.Explicit
OrderedDict[str, Rule]improves clarity.
545-548: LGTM: expose modules via modifier.Property passthrough keeps state centralized in
WorkflowModifier.
1757-1766: Fallback name generation may regress uniqueness; consider prior counter.Using
len(self._rules)+1can reuse names if an overwrite occurs before decoration. Consider reverting to the monotonicself.rule_countfor unnamed rules.- orig_name = name or str(len(self._rules) + 1) + orig_name = name or str(self.rule_count + 1)src/snakemake/modules.py (15)
9-9: LGTM: typing imports.
12-12: LGTM: exception import added.
18-30: LGTM: simplified name-modifier helper.
Behavior matches prior intent; wildcard vs literal handled cleanly.
50-53: LGTM: parent modifier capture and per-module rule proxy.
Capturingworkflow.modifierhere is correct for nesting; dedicatedRules()namespace is appropriate.
66-69: LGTM: path modifier chaining.
Composes module path changes over parent modifier.
126-133: LGTM: rule whitelist resolution.
Wildcard constraints validated; returning None for “use rule *” is fine.
166-182: LGTM: constructor signature and typing.
The added types and parameters align with the refactor.
205-215: Double-check parent chain: usingparent_modifier.parent_modifierskips one level.
This flattens the chain and can change how name/path modifiers compose across nesteduse rule ... with:. Validate against the nested import/override reproducer (A <- B <- C) to ensure outer overrides are applied exactly once and not lost.If issues arise, consider setting
self.parent_modifier = parent_modifierand relying on_modify_rulenameto avoid double application.
219-219: LGTM: skip_configfile toggled via config presence.
233-238: LGTM: inherit rule proxies with local name modifier.
Ensures parent exposes the child’s rules under the locally requested names.
239-250: LGTM: whitelist/exclude gating and delegated availability.
Recursive name modification before delegating upward is correct.
252-256: LGTM: rulename modification composition.
257-260: LGTM: internal modifier application.
262-264: LGTM: path modification routed via PathModifier.
135-160: Overwrite gating logic correctly restricted to matching wildcard imports
The revised implementation only permits overwrites when a prior wildcard import produced the same modified name and tracks per-rule duplicates.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/snakemake/workflow.py (2)
632-658: add_rule() never signals overwrites and may mis-handle replacements.
- is_overwrite is always False, so overwrite-specific flows never trigger.
- Overwrites don’t differentiate from first-time adds; default_target may be (re)assigned on overwrite.
- Replacing an existing key without popping first can change order semantics unexpectedly if not intended.
Apply:
def add_rule( self, name: str, lineno=None, snakefile=None, checkpoint=False, allow_overwrite=False, ): @@ - if self.is_rule(name) and not allow_overwrite: + existed = self.is_rule(name) + if existed and not allow_overwrite: raise CreateRuleException( f"The name {name} is already used by another rule", lineno=lineno, snakefile=snakefile, ) - else: - is_overwrite = False - self.rule_count += 1 - if not self.default_target: - self.default_target = name + is_overwrite = existed + self.rule_count += 1 + if not self.default_target and not is_overwrite: + self.default_target = name rule = Rule(name, self, lineno=lineno, snakefile=snakefile) - self._rules[rule.name] = rule + if is_overwrite: + # ensure replacement while keeping new insertion position explicit + self._rules.pop(rule.name, None) + self._rules[rule.name] = rule self.modifier.rules.add(rule) return is_overwrite
2388-2399: Wrong selector used for local ‘use rule’ gating.avail_rulename() expects the original rule name but receives name_modifier (alias), causing unintended skips.
- if not self.modifier.avail_rulename(name_modifier): + if not self.modifier.avail_rulename(rules[0]): # The parent use rule statement is specific for a different particular rule # hence this local use rule statement can be skipped. return
♻️ Duplicate comments (2)
src/snakemake/workflow.py (2)
1777-1779: Do not mutate module_globals["name"] on overwrite.Overwriting shouldn’t alter the namespace identity stored in globals. Remove this block.
- if is_overwrite: - rule.module_globals["__name__"] = None
1791-1795: Missing collision check when renaming a rule.Renaming can clobber an existing rule silently. Add the same guard used in add_rule().
- if ruleinfo.name: - del self._rules[name] - name = rule.name = ruleinfo.name - self._rules[name] = rule + if ruleinfo.name: + new_name = ruleinfo.name + if new_name != name and self.is_rule(new_name): + raise CreateRuleException( + f"The name {new_name} is already used by another rule", + lineno=lineno, + snakefile=snakefile, + ) + del self._rules[name] + name = rule.name = new_name + self._rules[name] = rule
🧹 Nitpick comments (1)
src/snakemake/workflow.py (1)
1757-1766: Minor: unnamed rule numbering should use rule_count for stability.len(self._rules) can fluctuate with overwrites/renames; rule_count is monotonic.
- orig_name = name or str(len(self._rules) + 1) + orig_name = name or str(self.rule_count + 1)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
src/snakemake/modules.py(5 hunks)src/snakemake/workflow.py(11 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
⚙️ CodeRabbit configuration file
**/*.py: Do not try to improve formatting.
Do not suggest type annotations for functions that are defined inside of functions or methods.
Do not suggest type annotation of theselfargument of methods.
Do not suggest type annotation of theclsargument of classmethods.
Do not suggest return type annotation if a function or method does not contain areturnstatement.
Files:
src/snakemake/modules.pysrc/snakemake/workflow.py
🧠 Learnings (3)
📚 Learning: 2025-08-28T13:15:55.232Z
Learnt from: Hocnonsense
PR: snakemake/snakemake#3714
File: src/snakemake/modules.py:195-195
Timestamp: 2025-08-28T13:15:55.232Z
Learning: In src/snakemake/modules.py, the line `self.globals["__name__"] = namespace` is intentionally designed to set __name__ to None when namespace is None (i.e., when not inside a module context, for top-level workflows). This is correct behavior and should not be guarded.
Applied to files:
src/snakemake/modules.pysrc/snakemake/workflow.py
📚 Learning: 2025-08-28T13:17:37.693Z
Learnt from: Hocnonsense
PR: snakemake/snakemake#3714
File: src/snakemake/workflow.py:637-665
Timestamp: 2025-08-28T13:17:37.693Z
Learning: In Snakemake, rule.module_globals["__name__"] always exists and defaults to "snakemake.workflow" when not modified, so direct dictionary access is safe without needing .get().
Applied to files:
src/snakemake/modules.pysrc/snakemake/workflow.py
📚 Learning: 2025-08-29T11:51:44.598Z
Learnt from: Hocnonsense
PR: snakemake/snakemake#3714
File: src/snakemake/modules.py:72-99
Timestamp: 2025-08-29T11:51:44.598Z
Learning: In Snakemake's module system, rule overrides are only permitted when a rule was previously imported via wildcard (`rule_whitelist is None`) and is now being imported again with specific modifications. The check_overwrite method should return True only for specific rule imports that were previously imported via wildcard, not for any non-wildcard imports.
Applied to files:
src/snakemake/modules.pysrc/snakemake/workflow.py
🧬 Code graph analysis (2)
src/snakemake/modules.py (4)
src/snakemake/common/__init__.py (2)
Rules(253-271)_register_rule(259-260)src/snakemake/exceptions.py (1)
CreateRuleException(526-527)src/snakemake/path_modifier.py (2)
PathModifier(16-153)replace_prefix(71-75)src/snakemake/workflow.py (9)
rules(589-590)rules(759-760)modifier(542-543)snakefile(1557-1562)name(2293-2298)rule(1755-2032)globals(554-555)wildcard_constraints(550-551)modules(546-547)
src/snakemake/workflow.py (4)
src/snakemake/rules.py (3)
Rule(76-1339)name(130-131)name(134-135)src/snakemake/exceptions.py (1)
CreateRuleException(526-527)src/snakemake/modules.py (2)
avail_rulename(237-247)get_name_modifier_func(18-29)src/snakemake/ruleinfo.py (1)
RuleInfo(14-111)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (56)
- GitHub Check: tests (9, ubuntu-latest, py312)
- GitHub Check: tests (9, ubuntu-latest, py313)
- GitHub Check: tests (10, windows-latest, py313)
- GitHub Check: tests (10, ubuntu-latest, py311)
- GitHub Check: tests (10, windows-latest, py312)
- GitHub Check: tests (10, ubuntu-latest, py313)
- GitHub Check: tests (9, windows-latest, py313)
- GitHub Check: tests (10, ubuntu-latest, py312)
- GitHub Check: tests (8, windows-latest, py312)
- GitHub Check: tests (8, windows-latest, py313)
- GitHub Check: tests (9, macos-latest, py312)
- GitHub Check: tests (9, windows-latest, py312)
- GitHub Check: tests (5, windows-latest, py312)
- GitHub Check: tests (9, ubuntu-latest, py311)
- GitHub Check: tests (8, ubuntu-latest, py311)
- GitHub Check: tests (6, ubuntu-latest, py313)
- GitHub Check: tests (7, windows-latest, py313)
- GitHub Check: tests (7, ubuntu-latest, py313)
- GitHub Check: tests (4, windows-latest, py312)
- GitHub Check: tests (8, ubuntu-latest, py313)
- GitHub Check: tests (6, windows-latest, py312)
- GitHub Check: tests (6, windows-latest, py313)
- GitHub Check: tests (7, ubuntu-latest, py311)
- GitHub Check: tests (5, windows-latest, py313)
- GitHub Check: tests (7, ubuntu-latest, py312)
- GitHub Check: tests (8, ubuntu-latest, py312)
- GitHub Check: tests (7, windows-latest, py312)
- GitHub Check: tests (6, ubuntu-latest, py311)
- GitHub Check: tests (6, ubuntu-latest, py312)
- GitHub Check: tests (5, ubuntu-latest, py311)
- GitHub Check: tests (2, windows-latest, py313)
- GitHub Check: tests (5, ubuntu-latest, py312)
- GitHub Check: tests (5, ubuntu-latest, py313)
- GitHub Check: tests (4, ubuntu-latest, py313)
- GitHub Check: tests (3, ubuntu-latest, py312)
- GitHub Check: tests (3, macos-latest, py313)
- GitHub Check: tests (4, windows-latest, py313)
- GitHub Check: tests (4, ubuntu-latest, py311)
- GitHub Check: tests (3, ubuntu-latest, py313)
- GitHub Check: tests (3, windows-latest, py312)
- GitHub Check: tests (4, ubuntu-latest, py312)
- GitHub Check: tests (3, windows-latest, py313)
- GitHub Check: tests (1, macos-latest, py313)
- GitHub Check: tests (1, ubuntu-latest, py312)
- GitHub Check: tests (2, windows-latest, py312)
- GitHub Check: tests (2, ubuntu-latest, py312)
- GitHub Check: tests (2, macos-latest, py312)
- GitHub Check: tests (3, ubuntu-latest, py311)
- GitHub Check: tests (2, ubuntu-latest, py311)
- GitHub Check: tests (2, ubuntu-latest, py313)
- GitHub Check: tests (1, macos-latest, py312)
- GitHub Check: tests (1, ubuntu-latest, py311)
- GitHub Check: tests (1, ubuntu-latest, py313)
- GitHub Check: tests (1, windows-latest, py313)
- GitHub Check: tests (1, windows-latest, py312)
- GitHub Check: apidocs
🔇 Additional comments (9)
src/snakemake/workflow.py (3)
177-177: LGTM: explicit OrderedDict typing for _rules.Clearer intent and preserves rule insertion order.
545-548: LGTM: expose modules via current modifier.Public accessor aligns with modifier-stack model.
1600-1604: LGTM: duplicate include suppression only for non-module context.Prevents blocking legitimate repeated module uses.
src/snakemake/modules.py (6)
18-30: LGTM: simpler name-modifier resolver.Covers wildcard and single-rule alias cases cleanly.
50-68: LGTM: parent-aware PathModifier chaining.Ensures consistent prefix replacement across nested modules.
70-100: LGTM: use_rules wiring and explicit allow_rule_overwrite.Typed params and passing path_modifier/rule_proxies look correct.
183-202: LGTM: module vs. local modifier initialization.State propagation (globals/rules/modules/path_modifier) is coherent; skip_configfile semantics preserved.
231-236: LGTM: inherit_rule_proxies applies local name modifier.Keeps proxies consistent across nested modifiers.
237-247: LGTM: avail_rulename respects whitelist/exclude and parent chain.Correctly short-circuits disallowed rules and propagates to parent.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/snakemake/modules.py (1)
1-284: Enable postponed annotation evaluation
Add afrom __future__ import annotationsat the top ofsrc/snakemake/modules.pyto postpone evaluation of annotations and avoid a runtime NameError on the unquoted forward reference toWorkflowModifierat line 50.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
src/snakemake/modules.py(5 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
⚙️ CodeRabbit configuration file
**/*.py: Do not try to improve formatting.
Do not suggest type annotations for functions that are defined inside of functions or methods.
Do not suggest type annotation of theselfargument of methods.
Do not suggest type annotation of theclsargument of classmethods.
Do not suggest return type annotation if a function or method does not contain areturnstatement.
Files:
src/snakemake/modules.py
🧠 Learnings (3)
📚 Learning: 2025-08-28T13:15:55.232Z
Learnt from: Hocnonsense
PR: snakemake/snakemake#3714
File: src/snakemake/modules.py:195-195
Timestamp: 2025-08-28T13:15:55.232Z
Learning: In src/snakemake/modules.py, the line `self.globals["__name__"] = namespace` is intentionally designed to set __name__ to None when namespace is None (i.e., when not inside a module context, for top-level workflows). This is correct behavior and should not be guarded.
Applied to files:
src/snakemake/modules.py
📚 Learning: 2025-08-28T13:17:37.693Z
Learnt from: Hocnonsense
PR: snakemake/snakemake#3714
File: src/snakemake/workflow.py:637-665
Timestamp: 2025-08-28T13:17:37.693Z
Learning: In Snakemake, rule.module_globals["__name__"] always exists and defaults to "snakemake.workflow" when not modified, so direct dictionary access is safe without needing .get().
Applied to files:
src/snakemake/modules.py
📚 Learning: 2025-08-29T11:51:44.598Z
Learnt from: Hocnonsense
PR: snakemake/snakemake#3714
File: src/snakemake/modules.py:72-99
Timestamp: 2025-08-29T11:51:44.598Z
Learning: In Snakemake's module system, rule overrides are only permitted when a rule was previously imported via wildcard (`rule_whitelist is None`) and is now being imported again with specific modifications. The check_overwrite method should return True only for specific rule imports that were previously imported via wildcard, not for any non-wildcard imports.
Applied to files:
src/snakemake/modules.py
🧬 Code graph analysis (1)
src/snakemake/modules.py (4)
src/snakemake/common/__init__.py (2)
Rules(253-271)_register_rule(259-260)src/snakemake/exceptions.py (1)
CreateRuleException(526-527)src/snakemake/path_modifier.py (2)
PathModifier(16-153)replace_prefix(71-75)src/snakemake/workflow.py (10)
rules(589-590)rules(759-760)modifier(542-543)snakefile(1557-1562)config(1746-1747)name(2293-2298)rule(1755-2032)globals(554-555)wildcard_constraints(550-551)modules(546-547)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (55)
- GitHub Check: tests (10, windows-latest, py313)
- GitHub Check: tests (10, windows-latest, py312)
- GitHub Check: tests (10, ubuntu-latest, py313)
- GitHub Check: tests (9, ubuntu-latest, py312)
- GitHub Check: tests (8, ubuntu-latest, py313)
- GitHub Check: tests (10, ubuntu-latest, py312)
- GitHub Check: tests (9, ubuntu-latest, py313)
- GitHub Check: tests (10, ubuntu-latest, py311)
- GitHub Check: tests (8, windows-latest, py313)
- GitHub Check: tests (9, windows-latest, py313)
- GitHub Check: tests (9, windows-latest, py312)
- GitHub Check: tests (8, windows-latest, py312)
- GitHub Check: tests (8, ubuntu-latest, py312)
- GitHub Check: tests (9, ubuntu-latest, py311)
- GitHub Check: tests (7, windows-latest, py312)
- GitHub Check: tests (8, ubuntu-latest, py311)
- GitHub Check: tests (6, windows-latest, py313)
- GitHub Check: tests (6, ubuntu-latest, py312)
- GitHub Check: tests (7, ubuntu-latest, py313)
- GitHub Check: tests (7, ubuntu-latest, py311)
- GitHub Check: tests (7, ubuntu-latest, py312)
- GitHub Check: tests (7, windows-latest, py313)
- GitHub Check: tests (5, ubuntu-latest, py312)
- GitHub Check: tests (6, ubuntu-latest, py313)
- GitHub Check: tests (6, ubuntu-latest, py311)
- GitHub Check: tests (5, windows-latest, py313)
- GitHub Check: tests (3, windows-latest, py313)
- GitHub Check: tests (5, ubuntu-latest, py313)
- GitHub Check: tests (2, ubuntu-latest, py312)
- GitHub Check: tests (6, windows-latest, py312)
- GitHub Check: tests (4, macos-latest, py313)
- GitHub Check: tests (4, ubuntu-latest, py311)
- GitHub Check: tests (5, windows-latest, py312)
- GitHub Check: tests (5, ubuntu-latest, py311)
- GitHub Check: tests (4, ubuntu-latest, py312)
- GitHub Check: tests (4, ubuntu-latest, py313)
- GitHub Check: tests (4, windows-latest, py312)
- GitHub Check: tests (3, macos-latest, py313)
- GitHub Check: tests (4, windows-latest, py313)
- GitHub Check: tests (2, windows-latest, py312)
- GitHub Check: tests (3, ubuntu-latest, py313)
- GitHub Check: tests (3, ubuntu-latest, py311)
- GitHub Check: tests (3, windows-latest, py312)
- GitHub Check: tests (3, ubuntu-latest, py312)
- GitHub Check: tests (2, windows-latest, py313)
- GitHub Check: tests (2, macos-latest, py312)
- GitHub Check: tests (1, macos-latest, py313)
- GitHub Check: tests (2, ubuntu-latest, py313)
- GitHub Check: tests (2, ubuntu-latest, py311)
- GitHub Check: tests (1, ubuntu-latest, py313)
- GitHub Check: tests (1, windows-latest, py313)
- GitHub Check: tests (1, windows-latest, py312)
- GitHub Check: tests (1, macos-latest, py312)
- GitHub Check: tests (1, ubuntu-latest, py312)
- GitHub Check: tests (1, ubuntu-latest, py311)
🔇 Additional comments (9)
src/snakemake/modules.py (9)
18-30: Name-modifier handling looks correct.Wildcard replacement and the single-rule constraint without a wildcard are implemented as expected.
66-68: PathModifier chaining LGTM.Chaining to
self.parent_modifier.path_modifiercorrectly composes prefix replacement across module layers.
72-99: Good: centralizing rule import via WorkflowModifier with explicit whitelist/excludes.Propagation of
rule_whitelist, local/resolved name modifiers, andpath_modifieris coherent.
185-206: Module vs. local modifier initialization reads correctly.
- Inheriting globals/checkpoints for modules and reusing parent state for local “use rule … as … with” is consistent.
- OR-ing
allow_rule_overwritewith the parent preserves nested-override intent.
221-221: Semantics change acknowledged: skip_configfile derived from presence of config.Deriving
skip_configfilefromconfig is not Noneis reasonable and avoids another constructor arg.
235-240: Rule proxy inheritance is correct with local renaming.Applying
local_rulename_modifierduring proxy registration prevents double-renaming upstream.
241-251: Whitelist/exclude gating before delegation is correct.Early return None to skip non-selected rules, then delegate modified names to the parent.
277-284: Namespace materialization LGTM.Exporting the modifier’s globals under
self.namespaceintoworkflow.globalsis consistent with the module model.
50-53: Fix forward-reference type annotation that will NameError at runtime.At class definition time,
WorkflowModifierisn’t defined yet. Withoutfrom __future__ import annotations,self.parent_modifier: WorkflowModifier = ...evaluates immediately and raises. Quote the annotation (or add the future import project-wide).Apply:
- self.parent_modifier: WorkflowModifier = self.workflow.modifier + self.parent_modifier: "WorkflowModifier" = self.workflow.modifierOptionally, consider adding
from __future__ import annotationsat the top of this file instead.Likely an incorrect or invalid review comment.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
src/snakemake/modules.py (1)
135-162: Overwrite gating logic looks correct.
- Wildcard import records modifier and returns False.
- Specific import only allows overwrite when every rule matches a prior wildcard-imported name; duplicate overwrites raise CreateRuleException.
🧹 Nitpick comments (1)
src/snakemake/modules.py (1)
50-53: Nit: rename for clarity.Consider renaming wildcards_modifier_overwrited → wildcards_modifier_overwritten (grammar/readability).
Minimal rename (all occurrences in this file):
- self.wildcards_modifier_overwrited = {} + self.wildcards_modifier_overwritten = {} @@ - self.wildcards_modifier_overwrited[rulename_modifier] = set() + self.wildcards_modifier_overwritten[rulename_modifier] = set() @@ - for modified in self.wildcards_modifier_overwrited: + for modified in self.wildcards_modifier_overwritten: @@ - if rule in self.wildcards_modifier_overwrited[modified]: + if rule in self.wildcards_modifier_overwritten[modified]: @@ - self.wildcards_modifier_overwrited[modified].add(rule) + self.wildcards_modifier_overwritten[modified].add(rule)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
src/snakemake/modules.py(5 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
⚙️ CodeRabbit configuration file
**/*.py: Do not try to improve formatting.
Do not suggest type annotations for functions that are defined inside of functions or methods.
Do not suggest type annotation of theselfargument of methods.
Do not suggest type annotation of theclsargument of classmethods.
Do not suggest return type annotation if a function or method does not contain areturnstatement.
Files:
src/snakemake/modules.py
🧠 Learnings (3)
📚 Learning: 2025-08-28T13:15:55.232Z
Learnt from: Hocnonsense
PR: snakemake/snakemake#3714
File: src/snakemake/modules.py:195-195
Timestamp: 2025-08-28T13:15:55.232Z
Learning: In src/snakemake/modules.py, the line `self.globals["__name__"] = namespace` is intentionally designed to set __name__ to None when namespace is None (i.e., when not inside a module context, for top-level workflows). This is correct behavior and should not be guarded.
Applied to files:
src/snakemake/modules.py
📚 Learning: 2025-08-28T13:17:37.693Z
Learnt from: Hocnonsense
PR: snakemake/snakemake#3714
File: src/snakemake/workflow.py:637-665
Timestamp: 2025-08-28T13:17:37.693Z
Learning: In Snakemake, rule.module_globals["__name__"] always exists and defaults to "snakemake.workflow" when not modified, so direct dictionary access is safe without needing .get().
Applied to files:
src/snakemake/modules.py
📚 Learning: 2025-08-29T11:51:44.598Z
Learnt from: Hocnonsense
PR: snakemake/snakemake#3714
File: src/snakemake/modules.py:72-99
Timestamp: 2025-08-29T11:51:44.598Z
Learning: In Snakemake's module system, rule overrides are only permitted when a rule was previously imported via wildcard (`rule_whitelist is None`) and is now being imported again with specific modifications. The check_overwrite method should return True only for specific rule imports that were previously imported via wildcard, not for any non-wildcard imports.
Applied to files:
src/snakemake/modules.py
🧬 Code graph analysis (1)
src/snakemake/modules.py (4)
src/snakemake/common/__init__.py (2)
Rules(253-271)_register_rule(259-260)src/snakemake/exceptions.py (1)
CreateRuleException(526-527)src/snakemake/path_modifier.py (2)
PathModifier(16-153)replace_prefix(71-75)src/snakemake/workflow.py (8)
rules(589-590)rules(759-760)modifier(542-543)snakefile(1557-1562)config(1746-1747)name(2293-2298)rule(1755-2032)globals(554-555)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (56)
- GitHub Check: tests (2, ubuntu-latest, py313)
- GitHub Check: tests (1, ubuntu-latest, py311)
- GitHub Check: tests (10, ubuntu-latest, py312)
- GitHub Check: tests (10, windows-latest, py313)
- GitHub Check: tests (9, windows-latest, py313)
- GitHub Check: tests (9, ubuntu-latest, py313)
- GitHub Check: tests (10, ubuntu-latest, py311)
- GitHub Check: tests (10, ubuntu-latest, py313)
- GitHub Check: tests (10, windows-latest, py312)
- GitHub Check: tests (9, ubuntu-latest, py312)
- GitHub Check: tests (9, windows-latest, py312)
- GitHub Check: tests (6, ubuntu-latest, py313)
- GitHub Check: tests (7, windows-latest, py312)
- GitHub Check: tests (8, windows-latest, py313)
- GitHub Check: tests (7, ubuntu-latest, py311)
- GitHub Check: tests (8, ubuntu-latest, py311)
- GitHub Check: tests (8, ubuntu-latest, py313)
- GitHub Check: tests (6, ubuntu-latest, py312)
- GitHub Check: tests (9, ubuntu-latest, py311)
- GitHub Check: tests (8, windows-latest, py312)
- GitHub Check: tests (7, ubuntu-latest, py313)
- GitHub Check: tests (8, ubuntu-latest, py312)
- GitHub Check: tests (4, ubuntu-latest, py312)
- GitHub Check: tests (7, ubuntu-latest, py312)
- GitHub Check: tests (7, windows-latest, py313)
- GitHub Check: tests (6, windows-latest, py312)
- GitHub Check: tests (6, macos-latest, py312)
- GitHub Check: tests (5, windows-latest, py313)
- GitHub Check: tests (6, windows-latest, py313)
- GitHub Check: tests (6, ubuntu-latest, py311)
- GitHub Check: tests (5, windows-latest, py312)
- GitHub Check: tests (5, ubuntu-latest, py312)
- GitHub Check: tests (4, ubuntu-latest, py313)
- GitHub Check: tests (5, ubuntu-latest, py311)
- GitHub Check: tests (5, ubuntu-latest, py313)
- GitHub Check: tests (4, windows-latest, py312)
- GitHub Check: tests (4, windows-latest, py313)
- GitHub Check: tests (1, windows-latest, py312)
- GitHub Check: tests (4, ubuntu-latest, py311)
- GitHub Check: tests (3, windows-latest, py313)
- GitHub Check: tests (3, macos-latest, py312)
- GitHub Check: tests (3, macos-latest, py313)
- GitHub Check: tests (3, windows-latest, py312)
- GitHub Check: tests (2, ubuntu-latest, py312)
- GitHub Check: tests (3, ubuntu-latest, py313)
- GitHub Check: tests (2, windows-latest, py312)
- GitHub Check: tests (3, ubuntu-latest, py311)
- GitHub Check: tests (3, ubuntu-latest, py312)
- GitHub Check: tests (2, ubuntu-latest, py311)
- GitHub Check: tests (1, macos-latest, py313)
- GitHub Check: tests (1, macos-latest, py312)
- GitHub Check: tests (1, ubuntu-latest, py313)
- GitHub Check: tests (2, windows-latest, py313)
- GitHub Check: tests (1, windows-latest, py313)
- GitHub Check: tests (1, ubuntu-latest, py312)
- GitHub Check: apidocs
🔇 Additional comments (11)
src/snakemake/modules.py (11)
9-12: LGTM on imports.New imports (Set, CreateRuleException) align with usage below.
66-68: LGTM: path modifier chaining.Passing parent path_modifier into a new PathModifier correctly composes prefix replacement across module boundaries.
72-99: LGTM: use_rules wiring.Whitelist, local/resolved name modifiers, overwrite flag, and proxy inheritance look consistent with workflow.py expectations.
126-133: LGTM: whitelist computation.Wildcard detection with “*” → None is correct and validated.
185-205: LGTM: module modifier init is robust.Fallbacks for rule_proxies/path_modifier avoid earlier None pitfalls; parent allow_rule_overwrite propagation is correct; name handling is intentional per prior discussion.
206-216: LGTM: non-module modifier inherits parent state.State propagation (globals, proxies, modules, path_modifier, overwrite flag) is consistent.
221-221: LGTM: skip_configfile derivation.Using presence of config to infer skip_configfile aligns with constructor semantics.
235-240: LGTM: proxy inheritance with local renaming.Using child local_rulename_modifier when registering upstream preserves visible names.
241-251: LGTM: availability check cascades correctly.Whitelist/exclude evaluated at each layer, then delegated upward with name modification.
254-258: LGTM: rulename modification cascade.Local modification then delegation to parent is correct.
267-271: LGTM: wrapper URI tag replacement.Skips URLs; uses compiled pattern; behavior matches docs.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
tests/test_module_nested/module_shallow.smk (2)
20-24: Nit: avoid tuple-izing log by trailing comma.
A trailing comma makes "aaalog" a 1-tuple; Snakemake accepts it, but a plain string is clearer.use rule run from A as A_run with: input: rds=config.get("bb", ".snakemake"), log: - "aaalog", + "aaalog"
33-37: Update stale comment to match the assertion.
The comment still references deep_work; the assertion checks A_run. Align them for clarity.-# rules.deep_work has to work even if the rule is renamed in a parent module -# The rulename itself can be already modified. +# rules.A_run must be accessible even if the rule was renamed in a parent module. +# The rulename may already be modified via wildcard export. assert hasattr( - rules, "A_run" -), f"bug: rule cannot be accessed as A_run: {dir(rules)}" + rules, "A_run" +), f"bug: rule cannot be accessed as A_run: {dir(rules)}"
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
tests/test_module_nested/Snakefile(1 hunks)tests/test_module_nested/module_shallow.smk(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- tests/test_module_nested/Snakefile
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (57)
- GitHub Check: tests (10, ubuntu-latest, py311)
- GitHub Check: tests (10, ubuntu-latest, py313)
- GitHub Check: tests (9, ubuntu-latest, py313)
- GitHub Check: tests (9, windows-latest, py312)
- GitHub Check: tests (10, windows-latest, py312)
- GitHub Check: tests (9, ubuntu-latest, py311)
- GitHub Check: tests (10, ubuntu-latest, py312)
- GitHub Check: tests (9, ubuntu-latest, py312)
- GitHub Check: tests (8, windows-latest, py313)
- GitHub Check: tests (8, ubuntu-latest, py311)
- GitHub Check: tests (8, ubuntu-latest, py312)
- GitHub Check: tests (9, windows-latest, py313)
- GitHub Check: tests (10, windows-latest, py313)
- GitHub Check: tests (6, windows-latest, py312)
- GitHub Check: tests (8, windows-latest, py312)
- GitHub Check: tests (7, macos-latest, py312)
- GitHub Check: tests (8, ubuntu-latest, py313)
- GitHub Check: tests (2, ubuntu-latest, py313)
- GitHub Check: tests (6, windows-latest, py313)
- GitHub Check: tests (7, windows-latest, py313)
- GitHub Check: tests (7, ubuntu-latest, py312)
- GitHub Check: tests (7, windows-latest, py312)
- GitHub Check: tests (6, ubuntu-latest, py311)
- GitHub Check: tests (4, windows-latest, py313)
- GitHub Check: tests (6, ubuntu-latest, py312)
- GitHub Check: tests (5, windows-latest, py313)
- GitHub Check: tests (4, ubuntu-latest, py313)
- GitHub Check: tests (7, ubuntu-latest, py311)
- GitHub Check: tests (7, ubuntu-latest, py313)
- GitHub Check: tests (5, ubuntu-latest, py313)
- GitHub Check: tests (5, ubuntu-latest, py312)
- GitHub Check: tests (5, ubuntu-latest, py311)
- GitHub Check: tests (6, ubuntu-latest, py313)
- GitHub Check: tests (5, windows-latest, py312)
- GitHub Check: tests (3, windows-latest, py312)
- GitHub Check: tests (2, windows-latest, py312)
- GitHub Check: tests (1, windows-latest, py312)
- GitHub Check: tests (3, ubuntu-latest, py311)
- GitHub Check: tests (4, ubuntu-latest, py312)
- GitHub Check: tests (4, ubuntu-latest, py311)
- GitHub Check: tests (4, windows-latest, py312)
- GitHub Check: tests (2, macos-latest, py313)
- GitHub Check: tests (2, windows-latest, py313)
- GitHub Check: tests (3, ubuntu-latest, py312)
- GitHub Check: tests (3, ubuntu-latest, py313)
- GitHub Check: tests (3, macos-latest, py312)
- GitHub Check: tests (3, windows-latest, py313)
- GitHub Check: tests (1, ubuntu-latest, py311)
- GitHub Check: tests (1, macos-latest, py312)
- GitHub Check: tests (2, ubuntu-latest, py311)
- GitHub Check: tests (1, macos-latest, py313)
- GitHub Check: tests (2, ubuntu-latest, py312)
- GitHub Check: tests (1, ubuntu-latest, py312)
- GitHub Check: tests (1, windows-latest, py313)
- GitHub Check: tests (1, ubuntu-latest, py313)
- GitHub Check: formatting
- GitHub Check: apidocs
🔇 Additional comments (3)
tests/test_module_nested/module_shallow.smk (3)
1-1: LGTM: deterministic shell for tests.
Using bash explicitly is fine for the test suite.
14-18: Double “use … as A_run” may trip the new overwrite restrictions.
You export A_run via the wildcard (A_*) and then use rule run as A_run again to set threads. Ensure the new “restrict overwrite” logic treats this as an additive modifier of the same origin rule, not an illegal overwrite.
4-8: No module alias collisions detected Verified thatmodule A:appears only once in tests/test_module_nested (module_shallow.smk), so theb-prefix is safe.
|
Dear Professor @johanneskoester, I believe this PR is ready to be merged -- would you mind having a look when you get a chance? Thanks again for your time and for maintaining this repository! Best regards, |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/snakemake/workflow.py (1)
641-665: Fix overwrite semantics in add_rule (is_overwrite, counters, stale rule cleanup).
is_overwriteis never set to True, so overwrite-specific paths never trigger.rule_countanddefault_targetshould not be modified on overwrite.- Replacing the rule object without removing the old one from
modifier.rulescan leave stale references.Apply:
@@ - if self.is_rule(name) and not allow_overwrite: + existed = self.is_rule(name) + if existed and not allow_overwrite: raise CreateRuleException( f"The name {name} is already used by another rule", lineno=lineno, snakefile=snakefile, ) - else: - is_overwrite = False - self.rule_count += 1 - if not self.default_target: - self.default_target = name + is_overwrite = existed + if not is_overwrite: + self.rule_count += 1 + if self.default_target is None: + self.default_target = name + else: + # avoid stale references to the old rule object + old_rule = self._rules[name] + try: + self.modifier.rules.discard(old_rule) + except AttributeError: + try: + self.modifier.rules.remove(old_rule) + except Exception: + pass @@ rule = Rule(name, self, lineno=lineno, snakefile=snakefile) self._rules[rule.name] = rule self.modifier.rules.add(rule) return is_overwrite
♻️ Duplicate comments (2)
src/snakemake/workflow.py (2)
1790-1792: Do not mutate rule.module_globals["name"] on overwrite.Overwriting a rule should not clobber the module’s
__name__; this can break subsequent same-namespace overrides and pollute exported state.- if is_overwrite: - rule.module_globals["__name__"] = None + # Overwrite permission is enforced via add_rule() and the modifier; leave module __name__ intact.
1805-1808: Guard rule rename against collisions.Renaming without checking collisions can clobber an existing rule.
- if ruleinfo.name: - del self._rules[name] - name = rule.name = ruleinfo.name - self._rules[name] = rule + if ruleinfo.name: + new_name = ruleinfo.name + if new_name != name and self.is_rule(new_name): + raise CreateRuleException( + f"The name {new_name} is already used by another rule", + lineno=lineno, + snakefile=snakefile, + ) + del self._rules[name] + name = rule.name = new_name + self._rules[name] = rule
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
src/snakemake/workflow.py(11 hunks)tests/tests.py(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- tests/tests.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
⚙️ CodeRabbit configuration file
**/*.py: Do not try to improve formatting.
Do not suggest type annotations for functions that are defined inside of functions or methods.
Do not suggest type annotation of theselfargument of methods.
Do not suggest type annotation of theclsargument of classmethods.
Do not suggest return type annotation if a function or method does not contain areturnstatement.
Files:
src/snakemake/workflow.py
🧠 Learnings (3)
📚 Learning: 2025-08-29T11:51:44.625Z
Learnt from: Hocnonsense
PR: snakemake/snakemake#3714
File: src/snakemake/modules.py:72-99
Timestamp: 2025-08-29T11:51:44.625Z
Learning: In Snakemake's module system, rule overrides are only permitted when a rule was previously imported via wildcard (`rule_whitelist is None`) and is now being imported again with specific modifications. The check_overwrite method should return True only for specific rule imports that were previously imported via wildcard, not for any non-wildcard imports.
Applied to files:
src/snakemake/workflow.py
📚 Learning: 2025-08-28T13:17:37.693Z
Learnt from: Hocnonsense
PR: snakemake/snakemake#3714
File: src/snakemake/workflow.py:637-665
Timestamp: 2025-08-28T13:17:37.693Z
Learning: In Snakemake, rule.module_globals["__name__"] always exists and defaults to "snakemake.workflow" when not modified, so direct dictionary access is safe without needing .get().
Applied to files:
src/snakemake/workflow.py
📚 Learning: 2025-08-28T13:15:55.232Z
Learnt from: Hocnonsense
PR: snakemake/snakemake#3714
File: src/snakemake/modules.py:195-195
Timestamp: 2025-08-28T13:15:55.232Z
Learning: In src/snakemake/modules.py, the line `self.globals["__name__"] = namespace` is intentionally designed to set __name__ to None when namespace is None (i.e., when not inside a module context, for top-level workflows). This is correct behavior and should not be guarded.
Applied to files:
src/snakemake/workflow.py
🧬 Code graph analysis (1)
src/snakemake/workflow.py (3)
src/snakemake/rules.py (6)
Rule(76-1339)name(130-131)name(134-135)lineno(138-139)snakefile(142-143)add(1346-1350)src/snakemake/modules.py (2)
avail_rulename(241-251)get_name_modifier_func(18-29)src/snakemake/ruleinfo.py (1)
RuleInfo(14-111)
🪛 Ruff (0.12.2)
src/snakemake/workflow.py
651-655: Avoid specifying long messages outside the exception class
(TRY003)
🔇 Additional comments (9)
src/snakemake/workflow.py (9)
180-180: LGTM: explicit typing of _rules.Clearer intent; preserves insertion order semantics.
1770-1774: LGTM: orig_name handling.Keeps legacy unnamed-rule numbering intact.
1776-1779: LGTM: rulename availability gate.Early-return to ignore non-matching local inheritance is appropriate.
1781-1786: Couples to add_rule() fix.Once
add_rule()setsis_overwritecorrectly (see above), downstream logic here will behave as intended.After applying the
add_rule()patch, please run the module overwrite tests to confirm behavior.
1793-1793: LGTM: annotate decorate(ruleinfo: RuleInfo).Harmless and clarifies intent.
2340-2344: LGTM: userule signature with explicit typing.Clearer API surface; no behavioral concerns.
2413-2419: LGTM: WorkflowModifier usage for local inheritance.Passing
is_module=Falseand a resolved name modifier aligns with the new modifier model.
552-555: Confirmed: no direct assignments toWorkflow.modulesexist; resolving.
2402-2404: Verify avail_rulename(None) handling in local inheritance
In src/snakemake/workflow.py (around line 2401),name_modifier: str | Nonecan beNone, soself.modifier.avail_rulename(name_modifier)will evaluate to falsy and invoke the earlyreturn, silently skipping the localuse ruleblock. Confirm that callingavail_rulename(None)here is intentional—otherwise add a guard to require a concrete alias (or adjustavail_rulenameto treatNoneas “always available”).
johanneskoester
left a comment
There was a problem hiding this comment.
THanks a lot. The changes are quite extensive. Can you provide an outline of what you changed where and why?
|
Thanks! Here's an outline of the changes I made, organized by purpose and file: 1: Support recursive module modification via
|
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (2)
src/snakemake/workflow.py (2)
1614-1617: Duplicate include check by object identity (not path).This was discussed earlier; switching to a stable key (path/URI) avoids missed duplicates. Fine to defer to a separate PR as noted.
1823-1826: Guard against renaming collisions when applying ruleinfo.name.Renaming currently replaces the dict key without checking for an existing different rule of the same name.
Apply:
- if ruleinfo.name: - del self._rules[name] - name = rule.name = ruleinfo.name - self._rules[name] = rule + if ruleinfo.name: + new_name = ruleinfo.name + if new_name != name and self.is_rule(new_name): + raise CreateRuleException( + f"The name {new_name} is already used by another rule", + lineno=lineno, + snakefile=snakefile, + ) + del self._rules[name] + name = rule.name = new_name + self._rules[name] = rule
🧹 Nitpick comments (4)
src/snakemake/workflow.py (1)
652-665: Overwriting a rule should evict the previous Rule object from modifier.rules.When
allow_overwrite=True, the old Rule remains inself.modifier.rules(set now contains both objects). Prefer discarding the old one to avoid duplicate processing in places that iterate this set.Apply:
- rule = Rule(name, self, lineno=lineno, snakefile=snakefile) - self._rules[rule.name] = rule - self.modifier.rules.add(rule) + prev = self._rules.get(name) if allow_overwrite else None + rule = Rule(name, self, lineno=lineno, snakefile=snakefile) + self._rules[name] = rule + if prev is not None: + self.modifier.rules.discard(prev) + self.modifier.rules.add(rule)Also minor lint nit on long error string (TRY003); optional to keep.
src/snakemake/modules.py (3)
18-31: Name-modifier validation is good; tiny linter nit for constant renamer.Use an underscore to appease linters about the unused lambda param.
Apply:
- return lambda rulename: name_modifier + return lambda _rulename: name_modifier
51-54: Nit: rename ‘wildcards_modifier_overwrited’ for clarity.“overwritten” reads better and avoids confusion.
Apply (example):
- self.wildcards_modifier_overwrited = {} + self.wildcards_modifier_overwritten = {} @@ - if not rule_whitelist: # all will be used, exclude rules do not matter - self.wildcards_modifier_overwrited[rulename_modifier] = set() + if not rule_whitelist: # wildcard import: record and disallow overwrite now + self.wildcards_modifier_overwritten[rulename_modifier] = set() return False @@ - for modified in self.wildcards_modifier_overwrited: + for modified in self.wildcards_modifier_overwritten: if (modified(rule) if modified else rule) == modified_this: matched.add(rule) - if rule in self.wildcards_modifier_overwrited[modified]: + if rule in self.wildcards_modifier_overwritten[modified]: raise CreateRuleException( f"The rule {rule} is imported with same name modifier and modified more than once", snakefile=self.snakefile, ) - self.wildcards_modifier_overwrited[modified].add(rule) + self.wildcards_modifier_overwritten[modified].add(rule)Also applies to: 144-158
136-163: Overwrite-check logic — LGTM; minor message nit.Algorithm matches the intended “specific import can overwrite prior wildcard import” semantics. Optional: shorten the exception message to satisfy TRY003.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
src/snakemake/modules.py(5 hunks)src/snakemake/rules.py(4 hunks)src/snakemake/workflow.py(10 hunks)tests/tests.py(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- src/snakemake/rules.py
- tests/tests.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
⚙️ CodeRabbit configuration file
**/*.py: Do not try to improve formatting.
Do not suggest type annotations for functions that are defined inside of functions or methods.
Do not suggest type annotation of theselfargument of methods.
Do not suggest type annotation of theclsargument of classmethods.
Do not suggest return type annotation if a function or method does not contain areturnstatement.
Files:
src/snakemake/modules.pysrc/snakemake/workflow.py
🧠 Learnings (4)
📚 Learning: 2025-08-28T13:15:55.232Z
Learnt from: Hocnonsense
PR: snakemake/snakemake#3714
File: src/snakemake/modules.py:195-195
Timestamp: 2025-08-28T13:15:55.232Z
Learning: In src/snakemake/modules.py, the line `self.globals["__name__"] = namespace` is intentionally designed to set __name__ to None when namespace is None (i.e., when not inside a module context, for top-level workflows). This is correct behavior and should not be guarded.
Applied to files:
src/snakemake/modules.pysrc/snakemake/workflow.py
📚 Learning: 2025-08-28T13:17:37.693Z
Learnt from: Hocnonsense
PR: snakemake/snakemake#3714
File: src/snakemake/workflow.py:637-665
Timestamp: 2025-08-28T13:17:37.693Z
Learning: In Snakemake, rule.module_globals["__name__"] always exists and defaults to "snakemake.workflow" when not modified, so direct dictionary access is safe without needing .get().
Applied to files:
src/snakemake/modules.pysrc/snakemake/workflow.py
📚 Learning: 2025-08-29T11:51:44.625Z
Learnt from: Hocnonsense
PR: snakemake/snakemake#3714
File: src/snakemake/modules.py:72-99
Timestamp: 2025-08-29T11:51:44.625Z
Learning: In Snakemake's module system, rule overrides are only permitted when a rule was previously imported via wildcard (`rule_whitelist is None`) and is now being imported again with specific modifications. The check_overwrite method should return True only for specific rule imports that were previously imported via wildcard, not for any non-wildcard imports.
Applied to files:
src/snakemake/modules.pysrc/snakemake/workflow.py
📚 Learning: 2024-11-12T12:08:20.342Z
Learnt from: johanneskoester
PR: snakemake/snakemake#3148
File: snakemake/dag.py:1332-1336
Timestamp: 2024-11-12T12:08:20.342Z
Learning: In `snakemake/dag.py`, when code is outdated and will disappear upon resolving merge conflicts, avoid making code review suggestions on that code.
Applied to files:
src/snakemake/workflow.py
🧬 Code graph analysis (2)
src/snakemake/modules.py (4)
src/snakemake/common/__init__.py (2)
Rules(253-271)_register_rule(259-260)src/snakemake/exceptions.py (1)
CreateRuleException(526-527)src/snakemake/path_modifier.py (2)
PathModifier(16-153)replace_prefix(71-75)src/snakemake/workflow.py (6)
rules(598-599)rules(767-768)modifier(551-552)snakefile(1571-1576)name(2326-2331)rule(1789-2065)
src/snakemake/workflow.py (2)
src/snakemake/rules.py (5)
Rule(76-1347)name(131-132)name(135-136)lineno(139-140)snakefile(143-144)src/snakemake/modules.py (2)
avail_rulename(242-252)get_name_modifier_func(18-30)
🪛 Ruff (0.12.2)
src/snakemake/modules.py
27-29: Avoid specifying long messages outside the exception class
(TRY003)
30-30: Unused lambda argument: rulename
(ARG005)
154-157: Avoid specifying long messages outside the exception class
(TRY003)
src/snakemake/workflow.py
653-657: Avoid specifying long messages outside the exception class
(TRY003)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (55)
- GitHub Check: tests (10, ubuntu-latest, py313)
- GitHub Check: tests (9, ubuntu-latest, py312)
- GitHub Check: tests (9, windows-2022, py313)
- GitHub Check: tests (10, ubuntu-latest, py311)
- GitHub Check: tests (10, windows-2022, py312)
- GitHub Check: tests (10, ubuntu-latest, py312)
- GitHub Check: tests (10, windows-2022, py313)
- GitHub Check: tests (9, windows-2022, py312)
- GitHub Check: tests (9, ubuntu-latest, py313)
- GitHub Check: tests (9, ubuntu-latest, py311)
- GitHub Check: tests (8, ubuntu-latest, py313)
- GitHub Check: tests (8, windows-2022, py313)
- GitHub Check: tests (8, windows-2022, py312)
- GitHub Check: tests (8, ubuntu-latest, py312)
- GitHub Check: tests (8, ubuntu-latest, py311)
- GitHub Check: tests (7, windows-2022, py312)
- GitHub Check: tests (7, windows-2022, py313)
- GitHub Check: tests (6, ubuntu-latest, py313)
- GitHub Check: tests (7, ubuntu-latest, py312)
- GitHub Check: tests (7, ubuntu-latest, py313)
- GitHub Check: tests (7, ubuntu-latest, py311)
- GitHub Check: tests (6, windows-2022, py313)
- GitHub Check: tests (5, windows-2022, py313)
- GitHub Check: tests (6, windows-2022, py312)
- GitHub Check: tests (6, ubuntu-latest, py312)
- GitHub Check: tests (6, ubuntu-latest, py311)
- GitHub Check: tests (5, windows-2022, py312)
- GitHub Check: tests (5, ubuntu-latest, py313)
- GitHub Check: tests (5, ubuntu-latest, py311)
- GitHub Check: tests (4, windows-2022, py313)
- GitHub Check: tests (5, ubuntu-latest, py312)
- GitHub Check: tests (4, macos-latest, py312)
- GitHub Check: tests (4, windows-2022, py312)
- GitHub Check: tests (4, ubuntu-latest, py313)
- GitHub Check: tests (4, ubuntu-latest, py312)
- GitHub Check: tests (4, ubuntu-latest, py311)
- GitHub Check: tests (3, windows-2022, py313)
- GitHub Check: tests (3, ubuntu-latest, py313)
- GitHub Check: tests (3, windows-2022, py312)
- GitHub Check: tests (3, ubuntu-latest, py311)
- GitHub Check: tests (3, ubuntu-latest, py312)
- GitHub Check: tests (2, macos-latest, py313)
- GitHub Check: tests (2, ubuntu-latest, py311)
- GitHub Check: tests (2, macos-latest, py312)
- GitHub Check: tests (2, ubuntu-latest, py313)
- GitHub Check: tests (2, windows-2022, py313)
- GitHub Check: tests (1, macos-latest, py312)
- GitHub Check: tests (1, windows-2022, py312)
- GitHub Check: tests (2, windows-2022, py312)
- GitHub Check: tests (2, ubuntu-latest, py312)
- GitHub Check: tests (1, macos-latest, py313)
- GitHub Check: tests (1, windows-2022, py313)
- GitHub Check: tests (1, ubuntu-latest, py312)
- GitHub Check: tests (1, ubuntu-latest, py313)
- GitHub Check: tests (1, ubuntu-latest, py311)
🔇 Additional comments (8)
src/snakemake/workflow.py (4)
182-182: Type-annotated rules map looks good.No issues with making
_rulesanOrderedDict[str, Rule].
554-557: Expose modules via Workflow.property — LGTM.Returning
self.modifier.modulesis consistent with the new modifier model.
1792-1807: Rule name selection and overwrite wiring — LGTM.
avail_rulename()gating andallow_rule_overwritepropagation are consistent.
2361-2365: Typed parameters for userule — LGTM.The updated signature improves clarity without changing behavior.
src/snakemake/modules.py (4)
73-101: Module use_rules wiring — LGTM.Whitelist, name-modifier resolution, and
WorkflowModifierconstruction look consistent.
186-206: WorkflowModifier (module branch) initialization — LGTM.Parent propagation and globals/namespace materialization are coherent with the new model.
207-217: WorkflowModifier (local branch) state sharing — LGTM.Correctly inherits from parent for local ‘use rule’ blocks.
242-253: avail_rulename chaining — LGTM.Whitelist/exclude filtering and parent delegation are sound.
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
docs/snakefiles/modularization.rst (2)
319-320: Grammar: “its” not “it’s”.Possessive pronoun required here.
-And finally, we overwrite the input directive of the rule ``bwa_mem`` such that the raw data is taken from the place where our workflow configures it via it's config file. +And finally, we overwrite the input directive of the rule ``bwa_mem`` such that the raw data is taken from the place where our workflow configures it via its config file.
329-341: Brand capitalization: GitHub/GitLab.Use official names.
-Currently, Github +Currently, GitHub @@ -and Gitlab are supported: +and GitLab are supported:
🧹 Nitpick comments (4)
src/snakemake/modules.py (2)
18-31: Silence ARG005 by using a throwaway arg in the constant renamer.The constant renamer’s lambda doesn’t use its parameter; switch to
_to satisfy linters without changing behavior.- return lambda rulename: name_modifier.replace("*", rulename) + return lambda rulename: name_modifier.replace("*", rulename) @@ - return lambda rulename: name_modifier + return lambda _: name_modifier
53-54: Rename “overwrited” → “overwritten” for clarity.Minor wording fix; keeps code intent unchanged.
- self.wildcards_modifier_overwrited: Dict[Callable | None, Set[str]] = {} + self.wildcards_modifier_overwritten: Dict[Callable | None, Set[str]] = {} @@ - if not rule_whitelist: # all will be used, exclude rules do not matter - self.wildcards_modifier_overwrited[rulename_modifier] = set() + if not rule_whitelist: # all will be used, exclude rules do not matter + self.wildcards_modifier_overwritten[rulename_modifier] = set() return False @@ - for modified in self.wildcards_modifier_overwrited: + for modified in self.wildcards_modifier_overwritten: if (modified(rule) if modified else rule) == modified_this: - if rule in self.wildcards_modifier_overwrited[modified]: + if rule in self.wildcards_modifier_overwritten[modified]: raise CreateRuleException( f"The rule {rule} is imported with same name modifier and modified more than once", snakefile=self.snakefile, ) matched.add(rule) - self.wildcards_modifier_overwrited[modified].add(rule) + self.wildcards_modifier_overwritten[modified].add(rule)Also applies to: 145-159
docs/snakefiles/modularization.rst (2)
247-271: Clarify dynamic “as” alias phrasing to avoid confusion.Make explicit that the alias after
asmust be given via a variable (not a literal) and may include*. This avoids misreading “module_name*” as a literal.-In particular, it is not possible to modify the alias name in the ``use rule`` statement (e.g., writing directly ``use rule * from module as module_*`` is not allowed for dynamic modules). +In particular, you cannot use a literal alias there (e.g., ``use rule * from module as module_*`` is not allowed). Instead, the alias after ``as`` must be provided via a variable (e.g., ``as alias*`` where ``alias`` is a variable).
120-139: Note about constant alias with multi/wildcard imports.Consider adding a short note that
asmust include*when importing multiple rules or using a wildcard, to mirror the implementation guard... note:: When importing multiple rules or using a wildcard (``use rule *``), the alias given after ``as`` must include ``*`` (e.g., ``as other_*``). A constant alias without ``*`` is only allowed when importing a single, specific rule.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
docs/snakefiles/modularization.rst(6 hunks)docs/snakefiles/rules.rst(1 hunks)src/snakemake/modules.py(5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- docs/snakefiles/rules.rst
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
⚙️ CodeRabbit configuration file
**/*.py: Do not try to improve formatting.
Do not suggest type annotations for functions that are defined inside of functions or methods.
Do not suggest type annotation of theselfargument of methods.
Do not suggest type annotation of theclsargument of classmethods.
Do not suggest return type annotation if a function or method does not contain areturnstatement.
Files:
src/snakemake/modules.py
🧠 Learnings (4)
📚 Learning: 2025-08-29T11:51:44.625Z
Learnt from: Hocnonsense
PR: snakemake/snakemake#3714
File: src/snakemake/modules.py:72-99
Timestamp: 2025-08-29T11:51:44.625Z
Learning: In Snakemake's module system, rule overrides are only permitted when a rule was previously imported via wildcard (`rule_whitelist is None`) and is now being imported again with specific modifications. The check_overwrite method should return True only for specific rule imports that were previously imported via wildcard, not for any non-wildcard imports.
Applied to files:
docs/snakefiles/modularization.rstsrc/snakemake/modules.py
📚 Learning: 2025-01-23T08:17:08.873Z
Learnt from: johanneskoester
PR: snakemake/snakemake#3263
File: snakemake/sourcecache.py:195-196
Timestamp: 2025-01-23T08:17:08.873Z
Learning: Including authentication tokens in Git repository URLs is accepted in the Snakemake codebase when using the HostedGitRepo class, as secure authentication handling through GitPython's credential helpers is not feasible in this context.
Applied to files:
docs/snakefiles/modularization.rst
📚 Learning: 2025-08-28T13:15:55.232Z
Learnt from: Hocnonsense
PR: snakemake/snakemake#3714
File: src/snakemake/modules.py:195-195
Timestamp: 2025-08-28T13:15:55.232Z
Learning: In src/snakemake/modules.py, the line `self.globals["__name__"] = namespace` is intentionally designed to set __name__ to None when namespace is None (i.e., when not inside a module context, for top-level workflows). This is correct behavior and should not be guarded.
Applied to files:
src/snakemake/modules.py
📚 Learning: 2025-08-28T13:17:37.693Z
Learnt from: Hocnonsense
PR: snakemake/snakemake#3714
File: src/snakemake/workflow.py:637-665
Timestamp: 2025-08-28T13:17:37.693Z
Learning: In Snakemake, rule.module_globals["__name__"] always exists and defaults to "snakemake.workflow" when not modified, so direct dictionary access is safe without needing .get().
Applied to files:
src/snakemake/modules.py
🧬 Code graph analysis (1)
src/snakemake/modules.py (4)
src/snakemake/common/__init__.py (2)
Rules(254-272)_register_rule(260-261)src/snakemake/exceptions.py (1)
CreateRuleException(526-527)src/snakemake/path_modifier.py (2)
PathModifier(16-153)replace_prefix(71-75)src/snakemake/workflow.py (5)
rules(598-599)rules(767-768)modifier(551-552)name(2326-2331)rule(1789-2065)
🪛 Ruff (0.12.2)
src/snakemake/modules.py
27-29: Avoid specifying long messages outside the exception class
(TRY003)
30-30: Unused lambda argument: rulename
(ARG005)
153-156: Avoid specifying long messages outside the exception class
(TRY003)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (56)
- GitHub Check: tests (1, ubuntu-latest, py313)
- GitHub Check: tests (10, windows-2022, py313)
- GitHub Check: tests (9, ubuntu-latest, py311)
- GitHub Check: tests (10, ubuntu-latest, py313)
- GitHub Check: tests (9, windows-2022, py313)
- GitHub Check: tests (8, windows-2022, py313)
- GitHub Check: tests (10, windows-2022, py312)
- GitHub Check: tests (9, ubuntu-latest, py313)
- GitHub Check: tests (9, ubuntu-latest, py312)
- GitHub Check: tests (10, ubuntu-latest, py311)
- GitHub Check: tests (10, ubuntu-latest, py312)
- GitHub Check: tests (8, ubuntu-latest, py313)
- GitHub Check: tests (7, windows-2022, py312)
- GitHub Check: tests (9, windows-2022, py312)
- GitHub Check: tests (7, ubuntu-latest, py311)
- GitHub Check: tests (7, windows-2022, py313)
- GitHub Check: tests (8, ubuntu-latest, py311)
- GitHub Check: tests (7, ubuntu-latest, py313)
- GitHub Check: tests (8, windows-2022, py312)
- GitHub Check: tests (6, ubuntu-latest, py313)
- GitHub Check: tests (5, ubuntu-latest, py313)
- GitHub Check: tests (8, ubuntu-latest, py312)
- GitHub Check: tests (6, windows-2022, py312)
- GitHub Check: tests (5, windows-2022, py313)
- GitHub Check: tests (5, windows-2022, py312)
- GitHub Check: tests (6, windows-2022, py313)
- GitHub Check: tests (7, ubuntu-latest, py312)
- GitHub Check: tests (6, ubuntu-latest, py311)
- GitHub Check: tests (5, ubuntu-latest, py312)
- GitHub Check: tests (6, ubuntu-latest, py312)
- GitHub Check: tests (5, ubuntu-latest, py311)
- GitHub Check: tests (4, windows-2022, py313)
- GitHub Check: tests (4, ubuntu-latest, py311)
- GitHub Check: tests (4, ubuntu-latest, py312)
- GitHub Check: tests (4, windows-2022, py312)
- GitHub Check: tests (4, ubuntu-latest, py313)
- GitHub Check: tests (1, windows-2022, py312)
- GitHub Check: tests (2, windows-2022, py313)
- GitHub Check: tests (3, ubuntu-latest, py312)
- GitHub Check: tests (3, windows-2022, py313)
- GitHub Check: tests (3, macos-latest, py313)
- GitHub Check: tests (2, windows-2022, py312)
- GitHub Check: tests (3, ubuntu-latest, py313)
- GitHub Check: tests (3, windows-2022, py312)
- GitHub Check: tests (2, macos-latest, py313)
- GitHub Check: tests (1, ubuntu-latest, py312)
- GitHub Check: tests (2, ubuntu-latest, py313)
- GitHub Check: tests (2, macos-latest, py312)
- GitHub Check: tests (3, ubuntu-latest, py311)
- GitHub Check: tests (2, ubuntu-latest, py312)
- GitHub Check: tests (1, macos-latest, py312)
- GitHub Check: tests (2, ubuntu-latest, py311)
- GitHub Check: tests (1, ubuntu-latest, py311)
- GitHub Check: tests (1, windows-2022, py313)
- GitHub Check: tests (1, macos-latest, py313)
- GitHub Check: apidocs
🔇 Additional comments (4)
src/snakemake/modules.py (2)
136-163: Overwrite gating logic matches intended semantics.Allows overwriting only when a specific import targets rules previously imported via wildcard (same effective final name) and prevents multiple modifications of the same rule/name-modifier pair. Looks good.
242-259: Recursive name-availability check is correct.Whitelist/exclude filtering before delegating to the parent preserves module-level constraints and ensures collisions are resolved at the outermost scope. LGTM.
docs/snakefiles/modularization.rst (2)
192-196: Import-time override semantics read clearly and match implementation.Selective replacement for params keywords, full overwrite for other directives, execution step preserved. Good.
197-238: Overwrite precondition is well-documented.Requirement to previously import via
use rule *and prohibition of re-modifying under the same final name is explicit and aligned with code. LGTM.
|
Thanks for the suggestion! I've added a section in the docs to explain the behavior with examples, as discussed. |
|
Fantastic work, as always!! |
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
🤖 I have created a release *beep* *boop* --- ## [9.11.4](v9.11.3...v9.11.4) (2025-09-19) ### Bug Fixes * Confusion with Overriding input After Snakemake Modularization ([#3714](#3714)) ([bd94dc1](bd94dc1)) * properly handle temp files in group jobs that are not needed outside of the group (do not upload them to storage and delete them early) ([#3730](#3730)) ([3ffd8e1](3ffd8e1)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
|
Apologies for the trouble this may have introduced. |
…hat cause #3751 failed (#3753) will fix #3751 and fix #2838 and fix #3741 This is caused by a more strict filter of unwanted rule applyed in #3714 Here, when `use rule delly from dna_seq_varlociraptor as calling_delly with:`, all rules will be ignored, including the rule `star_align` referred in "v3.13.3/meta/bio/star_arriba/test/Snakefile", line 49": ```python rule arriba: input: bam=rules.star_align.output.aln, ``` * [x] The PR contains a test case for the changes or the changes are already covered by an existing test case. * [ ] The documentation (`docs/`) is updated to reflect the changes or this is not necessary (e.g. if the change does neither modify the language nor the behavior or functionalities of Snakemake). <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Tests** * Expanded nested-module test to add an intermediate rule and verify sequential handoff between consecutive rules, reproducing and guarding against a previously observed filtering issue. * **Chores** * Improved rule registration and decorator behavior to better handle dynamic rule names, module-level globals, and checkpoint registration. * **Developer-facing** * Updated rule-addition interface and related internals; callers that programmatically register rules may need minor adjustments. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
…nakemake#3714) will fix snakemake#3713 ## 1: Support recursive module modification via `WorkflowModifier` To make workflows recursively apply modifiers like path prefix and rule name changes across nested modules, I introduced the following changes: - src/snakemake/ruleinfo.py: - Made `RuleInfo` recursively apply `parent_modifier` to adjust paths and rule names. - src/snakemake/path_modifier.py: - Added `inner_modifier` to support chained path transformations in nested modules. - src/snakemake/modules.py: - Modified `get_name_modifier_func` and `WorkflowModifier.modify_rulename` to support recursive rule renaming. - Replaced `WorkflowModifier.skip_rule` with `avail_rulename` to ensure that `rule_whitelist` and `rule_exclude_list` are respected at every module level. ## 2: Make multiple use statements with specific_rule more reliable and strict My understanding is that `rule.name` should be unique in a workflow. Therefore, a second `use rule from ... with ...` should only be allowed if the rule was previously imported with the same name via a wildcard (`use * from ...`) and is now being refined. - to clarify: - A given rule from a module can be used **multiple times** in the same workflow, as long as each usage assigns it a **unique name**. - However, using the **same rule name** more than once is **not allowed**, to prevent accidental overwrites, just like any rule rule defined **out of** module **cannot** be overwrite. - That’s the restriction behind the “only once” statement -- it applies per final rule name, not per source rule. - src/snakemake/rules.py: - The old logic stored rule dependencies as objects. If a rule was modified afterward, those changes wouldn't propagate. - Now, rule dependencies are resolved dynamically by name, ensuring correct updated rule with parameters modified. - src/snakemake/modules.py: - Stricter checks for rules overwriting. `ModuleInfo.use_rules` now `allow_rule_overwrite` for specific_rule only if it was previously imported via `use * from ...` and not already customized. - src/snakemake/workflow.py: - Adjusted to work with the new `WorkflowModifier`. ## Other changes: - Simplify `WorkflowModifier.__init__`. The module’s `__name__` is now assigned as `WorkflowModifier.namespace`, making workflow development and debugging easier. ### QC * [x] The PR contains a test case for the changes or the changes are already covered by an existing test case. * [x] The documentation (`docs/`) is updated to reflect the changes or this is not necessary (e.g. if the change does neither modify the language nor the behavior or functionalities of Snakemake). <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Multi-stage path modifiers, a public module registry, and improved name/path/wrapper modifier lifecycle for more predictable module imports. * **Bug Fixes** * Prevents unintended duplicate rule creation from wildcard/name modifications and enforces explicit overwrite semantics; tighter path-replacement guards and more reliable dependency resolution. * **Tests** * Updated fixtures for renamed, aliased and nested modules/rules to reflect modifier behavior changes. * **Documentation** * Expanded module-import and rule-customization guidance with examples and conflict-resolution advice. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Johannes Köster <johannes.koester@tu-dortmund.de> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
🤖 I have created a release *beep* *boop* --- ## [9.11.4](snakemake/snakemake@v9.11.3...v9.11.4) (2025-09-19) ### Bug Fixes * Confusion with Overriding input After Snakemake Modularization ([snakemake#3714](snakemake#3714)) ([bd94dc1](snakemake@bd94dc1)) * properly handle temp files in group jobs that are not needed outside of the group (do not upload them to storage and delete them early) ([snakemake#3730](snakemake#3730)) ([3ffd8e1](snakemake@3ffd8e1)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
…hat cause snakemake#3751 failed (snakemake#3753) will fix snakemake#3751 and fix snakemake#2838 and fix snakemake#3741 This is caused by a more strict filter of unwanted rule applyed in snakemake#3714 Here, when `use rule delly from dna_seq_varlociraptor as calling_delly with:`, all rules will be ignored, including the rule `star_align` referred in "v3.13.3/meta/bio/star_arriba/test/Snakefile", line 49": ```python rule arriba: input: bam=rules.star_align.output.aln, ``` * [x] The PR contains a test case for the changes or the changes are already covered by an existing test case. * [ ] The documentation (`docs/`) is updated to reflect the changes or this is not necessary (e.g. if the change does neither modify the language nor the behavior or functionalities of Snakemake). <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Tests** * Expanded nested-module test to add an intermediate rule and verify sequential handoff between consecutive rules, reproducing and guarding against a previously observed filtering issue. * **Chores** * Improved rule registration and decorator behavior to better handle dynamic rule names, module-level globals, and checkpoint registration. * **Developer-facing** * Updated rule-addition interface and related internals; callers that programmatically register rules may need minor adjustments. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
will fix #3713
1: Support recursive module modification via
WorkflowModifierTo make workflows recursively apply modifiers like path prefix and rule name changes across nested modules, I introduced the following changes:
RuleInforecursively applyparent_modifierto adjust paths and rule names.inner_modifierto support chained path transformations in nested modules.get_name_modifier_funcandWorkflowModifier.modify_rulenameto support recursive rule renaming.WorkflowModifier.skip_rulewithavail_rulenameto ensure thatrule_whitelistandrule_exclude_listare respected at every module level.2: Make multiple use statements with specific_rule more reliable and strict
My understanding is that
rule.nameshould be unique in a workflow. Therefore, a seconduse rule from ... with ...should only be allowed if the rule was previously imported with the same name via a wildcard (use * from ...) and is now being refined.to clarify:
src/snakemake/rules.py:
src/snakemake/modules.py:
ModuleInfo.use_rulesnowallow_rule_overwritefor specific_rule only if it was previously imported viause * from ...and not already customized.src/snakemake/workflow.py:
WorkflowModifier.Other changes:
WorkflowModifier.__init__. The module’s__name__is now assigned asWorkflowModifier.namespace, making workflow development and debugging easier.QC
docs/) is updated to reflect the changes or this is not necessary (e.g. if the change does neither modify the language nor the behavior or functionalities of Snakemake).Summary by CodeRabbit
New Features
Bug Fixes
Tests
Documentation