Scoping¶
The Problem¶
When multiple structures are loaded, a command like /rep cartoon protein is ambiguous — which structure's protein should be shown as cartoon?
The Solution: Automatic Scoping¶
codemol maintains an active scope — the name of the currently active structure. When a scope is set, dispatch() automatically wraps selection arguments:
# Without scope:
cmd.show("cartoon", "polymer.protein")
# With scope = "1AKE":
cmd.show("cartoon", "model 1AKE and (polymer.protein)")
The user doesn't need to type model 1AKE and (...) — it's injected automatically.
How It Works¶
_scope_arg()¶
In tool_loader.py, _scope_arg() wraps a resolved selection with the model filter:
def _scope_arg(raw: str, resolved: str, scope: str) -> str:
"""Wrap selection: 'model X and (selection)'."""
return f"model {scope} and ({resolved})"
Selection Detection¶
Not every argument should be scoped — numbers, filenames, and names should pass through. _is_selection() determines if an argument is a selection expression by checking for:
- Alias matches (
protein,ligand) - Selection keywords (
chain,resi,within, etc.) - Chain/residue patterns (
A/45/CA) - Logical operators (
and,or,not)
_is_selection("protein") # True — known alias
_is_selection("chain A") # True — selection keyword
_is_selection("3.5") # False — number
_is_selection("my_figure.png") # False — filename
Tools That Skip Scoping¶
Some tools handle scoping themselves or should always run globally:
/io load— loads new structures, no scope to apply/io clear— clears everything/analysis align— takes two explicit structure names
Scope Lifecycle¶
graph TD
A[Load structure A] -->|scope = A| B[Commands target A]
B --> C[Load structure B]
C -->|scope = B| D[Commands target B]
D --> E[Click A in sidebar]
E -->|scope = A| F[Commands target A again]
F --> G[/io clear]
G -->|scope = None| H[No scoping]
- First load — scope set to loaded structure
- Additional loads — scope switches to newest structure
- Sidebar click — scope changes to clicked structure via
activate() - Clear — scope reset to
None
Default Arguments and Scoping¶
When a tool has a default argument like selection="all" and the user calls it without arguments, dispatch() replaces the default with the scope:
# Tool definition:
def run(cmd, selection: str = "all") -> str: ...
# User types: /surface area (no args)
# Without scope: run(cmd, "all")
# With scope "1AKE": run(cmd, "model 1AKE")
This ensures that even commands without explicit selections respect the active structure.