attach_instance Visual Guide
How to connect RoutingClass instances into hierarchies.
Core Concept
attach_instance lives on RoutingClass (not on Router). Every RoutingClass
owns exactly one router (self.route). Attaching does two things:
Sets the parent-child relationship (
child._routing_parent = self)Links the child’s router into the parent’s router (
parent.route._children[alias] = child.route, viainclude())
graph LR
subgraph "RoutingClass (parent)"
P["self"]
PR["route (Router)"]
P --> PR
end
subgraph "RoutingClass (child)"
C["child"]
CR["route (Router)"]
C --> CR
end
P -- "_routing_parent" --> C
PR -- "_children['sales']" --> CR
style P fill:#e1f5fe
style C fill:#fff3e0
Scenario 1: Attaching a Child
Parent and child each own one router. The child’s router is linked under the alias.
graph TB
subgraph "Parent"
PA["route (Router)"]
PA --- E1["health • entry"]
PA --- E2["status • entry"]
PA === S1["[sales]"]
end
subgraph "Child (vendite)"
CA["route (Router)"]
CA --- E3["ordini • entry"]
CA --- E4["fatture • entry"]
end
S1 --> CA
style PA fill:#bbdefb
style CA fill:#ffe0b2
style S1 fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px
Syntax:
self.attach_instance(vendite, name="sales")
Access paths:
self.route.node("health")() # local entry
self.route.node("sales/ordini")() # child entry
self.route.node("sales/fatture")() # child entry
Rule: name= is the alias in the parent’s router. This is the only calling
style — every RoutingClass has exactly one router, so there is nothing else to map.
Scenario 2: Child with Its Own Children
An attached child brings its whole sub-tree along.
graph TB
subgraph "Parent"
PA["route (Router)"]
PA --- E1["health • entry"]
PA === S1["[sales]"]
end
subgraph "Child (vendite)"
CA["route (Router)"]
CA --- E3["ordini • entry"]
CA --- E4["fatture • entry"]
CA === SS["[statistiche]"]
end
subgraph "Grandchild"
GR["route (Router)"]
GR --- E6["mensili • entry"]
GR --- E7["annuali • entry"]
end
S1 --> CA
SS --> GR
style PA fill:#bbdefb
style CA fill:#ffe0b2
style S1 fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px
style SS fill:#c8e6c9
style GR fill:#f3e5f5
Syntax:
vendite.attach_instance(statistiche, name="statistiche")
self.attach_instance(vendite, name="sales")
Access paths:
self.route.node("sales/ordini")() # child entry
self.route.node("sales/statistiche/mensili")() # grandchild entry
Scenario 3: Multiple Surfaces — Composition
One class exposes one router. A service that needs several surfaces (public API,
admin, …) is split into one class per surface, composed under grouping
nodes. Section provides an empty grouping node without a dedicated class.
graph TB
subgraph "Application"
PA["route (Router)"]
PA --- E1["health • entry"]
PA === S1["[api]"]
PA === S2["[admin]"]
end
subgraph "Section (Public API)"
SA["route (Router)"]
SA === S3["[orders]"]
end
subgraph "Section (Admin area)"
SB["route (Router)"]
SB === S4["[orders]"]
end
subgraph "OrdersApi"
CA["route (Router)"]
CA --- E3["get_data • entry"]
end
subgraph "OrdersAdmin"
CB["route (Router)"]
CB --- E4["manage • entry"]
end
S1 --> SA
S2 --> SB
S3 --> CA
S4 --> CB
style PA fill:#bbdefb
style SA fill:#c5cae9
style SB fill:#c5cae9
style CA fill:#ffe0b2
style CB fill:#ffe0b2
style S1 fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px
style S2 fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px
Syntax:
from genro_routes import Section
api = Section("Public API")
admin = Section("Admin area")
self.attach_instance(api, name="api")
self.attach_instance(admin, name="admin")
api.attach_instance(OrdersApi(), name="orders")
admin.attach_instance(OrdersAdmin(), name="orders")
Access paths:
self.route.node("api/orders/get_data")() # public surface
self.route.node("admin/orders/manage")() # admin surface
Note: earlier versions supported multiple routers per class with a
router_*cross-mapping DSL inattach_instance. That feature was removed: composition (one class per surface,Sectionfor grouping) covers the same use cases with a single calling style.
Syntax Reference
attach_instance(child, name=...)
self.attach_instance(child, name="alias")
The child’s router is linked under
aliasin the parent’s routerSets
child._routing_parent = selfRaises
ValueErroron alias collision or if the child is already bound to another parent
Attach only (no routing)
self.attach_instance(child)
Sets
child._routing_parent = selfonlyNo routers are linked
Useful when you plan to link routers later or only need the parent chain for
ctxpropagation
include (on Router — low level)
Direct router-to-router or entry-alias linking.
Include a Router:
self._sys.route.include(swagger.route, name="swagger")
Links the source router as a child of this router
On the primary attachment (source has no parent yet) it sets
_routing_parenton the source’s owner and triggers plugin inheritanceSubsequent includes of the same router are navigational shortcuts only
Include a RouterNode (entry alias):
fatture.route.include(
pagamenti.route.node("collega_a_fattura"),
name="collega_pagamento",
)
Creates an alias: same handler, visible from two paths
No copy — the original MethodEntry is shared
nameis required for RouterNode sources
detach_instance (on Router)
self.route.detach_instance(child)
Removes every alias of
child’s router from this router’s_childrenClears
child._routing_parentStays on Router, not RoutingClass
Scenario 4: Entry Alias
The same handler declared in one service, visible in another’s tree.
graph TB
subgraph "Pagamenti"
PA["route (Router)"]
PA --- E1["lista_pagamenti • entry"]
PA --- E2["collega_a_fattura • entry"]
end
subgraph "Fatture"
FA["route (Router)"]
FA --- E3["lista_fatture • entry"]
FA -.- E4["collega_pagamento • alias"]
end
E4 -.->|"same handler"| E2
style PA fill:#bbdefb
style FA fill:#ffe0b2
style E4 fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px,stroke-dasharray: 5 5
Syntax:
fatture.route.include(
pagamenti.route.node("collega_a_fattura"),
name="collega_pagamento",
)
Access paths:
pagamenti.route.node("collega_a_fattura")(1, 2) # original
fatture.route.node("collega_pagamento")(1, 2) # alias — same handler
Decision Guide
flowchart TD
A["What do you need?"] --> B["Expose a child service\nunder an alias"]
A --> C["A pure grouping level\n(no handlers)"]
A --> D["Several surfaces\n(api, admin, ...)"]
A --> E["Make one entry visible\nfrom another tree"]
B --> F["attach_instance(child, name='alias')"]
C --> G["attach_instance(Section('...'), name='group')"]
D --> H["One RoutingClass per surface,\ncomposed with attach_instance"]
E --> I["router.include(node, name='alias')"]
style F fill:#c8e6c9,stroke:#2e7d32
style G fill:#c8e6c9,stroke:#2e7d32
style H fill:#fff3e0,stroke:#ef6c00
style I fill:#f3e5f5,stroke:#7b1fa2
Real-World Example
from genro_routes import RoutingClass, route
class AuthService(RoutingClass):
@route()
def login(self, username: str, password: str):
return {"token": "..."}
class UserService(RoutingClass):
@route()
def list_users(self):
return ["alice", "bob"]
class Application(RoutingClass):
def __init__(self):
self.route.plug("logging")
self.auth = AuthService()
self.users = UserService()
self.attach_instance(self.auth, name="auth")
self.attach_instance(self.users, name="users")
app = Application()
app.route.node("auth/login")("alice", "secret")
app.route.node("users/list_users")()