Working with Paths#
Use slash-separated path strings to navigate, reference, and create notebook tree nodes; paths avoid long chains of indexing when structures are deeply nested.
See also
Accessing Items with Index for duplicate-name and first-match lookup behavior.
from labapi import TraversalError
# Index access (chained)
page = notebook["Experiments"]["2024"]["Results"]
# Path-based (equivalent)
page = notebook.traverse("Experiments/2024/Results")
try:
notebook.traverse("Experiments/2024/Results/Figure 1")
except TraversalError:
# An intermediate segment exists but is not a directory
...
Traversing the Tree#
The traverse() method is available on any tree node
and accepts a slash-separated path string.
folder = notebook.traverse("Experiments")
page = notebook.traverse("Experiments/2024/Results")
Note
If duplicate sibling names appear in a path segment, traverse()
selects the first match for that segment. For deterministic selection,
index from the parent container with Index.Id (see Accessing Items with Index).
Absolute vs Relative Paths#
Paths starting with / are absolute - they are resolved from the notebook root regardless
of where you call traverse.
# Relative: resolved from `folder`
page = folder.traverse("2024/Results")
# Absolute: always resolved from the notebook root
page = folder.traverse("/Experiments/2024/Results")
Escaping Path Separators#
Use a backslash to keep / as part of a path segment instead of treating it
as a separator. This escaping is supported by all methods that accept path
strings, including traverse(), create(), dir(), and page().
# Literal name "Figure/1"
figure = notebook.traverse(r"Experiments/Figure\/1")
# Literal name "Reports/2024"
page = notebook.page(r"Reports\/2024")
Programmatic Escaping#
If you are building paths from variables that might contain slashes, use
escape() to escape the segments:
from labapi import NotebookPath
raw_name = "Results / Final"
# Escapes / and \ to prevent accidental traversal
safe_name = NotebookPath.escape(raw_name)[0]
# Now safe to use in a path string
page = notebook.page(f"Experiments/{safe_name}")
Use unescape() to remove escapes from a
segment string:
print(NotebookPath.unescape(r"Results\/Final")) # "Results/Final"
Note
To list children with the same name, call
container[Index.Name: "Results"]; then use Index.Id to select one
match (see Accessing Items with Index).
Warning
Nodes with the literal name ".." cannot be accessed via traverse,
as .. is reserved for parent navigation.
Enumerating Descendants#
The enumeration methods return relative path strings for descendants through the requested depth, which lets you list or search tree paths without traversing each path separately.
# All descendants (directories and pages) through depth 2
notebook.enumerate_all(depth=2)
# e.g. ["Experiments", "Experiments/2024", "Protocols"]
# Only directories
notebook.enumerate_dirs(depth=2)
# Only pages
notebook.enumerate_pages(depth=2)
depth defaults to 1 (immediate children only). Increase depth to include more
levels; each directory visited adds one API request.
# Enumerate up to 3 levels deep
all_paths = notebook.enumerate_all(depth=3)
for path in all_paths:
node = notebook.traverse(path)
print(path, "->", node.id)
Note
Use enumerate_nodes() to get
(path, node) pairs directly instead of re-traversing, since first-match
name lookup can pick the wrong node when sibling names repeat.
Creating Nodes with Paths#
The create() method accepts a path string (or
NotebookPath) as the name argument. When a multi-segment path is
provided, you must pass parents=True to allow intermediate directories to be created
automatically.
from labapi import NotebookPage, NotebookDirectory
# Create a page at a nested path; intermediate directories are created as needed
page = notebook.create(NotebookPage, "Experiments/2024/Results", parents=True)
# Without parents=True, create() raises ValueError for any multi-segment path,
# even when the intermediate directories already exist
page = notebook.create(NotebookPage, "Experiments/2024/Results") # raises ValueError
See Creating Pages and Entries for details on the if_exists parameter and other creation options.
Convenience Methods: dir() and page()#
For common “ensure this path exists” workflows, use the convenience methods
dir() and
page().
These methods are shorthand for create()
with:
parents=True(create missing intermediate directories), andif_exists=InsertBehavior.Retain(return an existing matching node instead of raising).
from labapi import NotebookDirectory, NotebookPage, InsertBehavior
# Equivalent calls:
reports_dir = notebook.dir("Experiments/2024/Reports")
reports_dir = notebook.create(
NotebookDirectory,
"Experiments/2024/Reports",
parents=True,
if_exists=InsertBehavior.Retain,
)
summary_page = notebook.page("Experiments/2024/Summary")
summary_page = notebook.create(
NotebookPage,
"Experiments/2024/Summary",
parents=True,
if_exists=InsertBehavior.Retain,
)
When to Use Them#
Use dir() and page() when missing directories or pages should be
created and existing matches should be returned.
# Safe to run multiple times; existing nodes are returned.
notebook.dir("Experiments/2024").page("Results")
notebook.page("Experiments/2024/Raw Data")
For read-only navigation, use traverse(). Use dir() or page()
only when creating a missing node is acceptable.
# Navigate to an existing directory (or create it if needed)
reports = notebook.dir("Experiments/2024/Reports")
# Navigate to an existing page (or create it if needed)
summary = notebook.page("Experiments/2024/Summary")
The NotebookPath Class#
NotebookPath represents parsed notebook paths. Use it to compose,
resolve, compare, or stringify paths before calling traverse() or create().
Constructing a path from a node:
from labapi import NotebookPath
path = NotebookPath(folder)
print(path) # /Experiments/2024
print(path.name) # 2024 (last segment)
print(path.parts) # ('Experiments',) (all but last)
print(path.is_absolute()) # True
Constructing from a string:
abs_path = NotebookPath("/Experiments/2024")
rel_path = NotebookPath("2024/Results")
Combining paths with ``/``:
base = NotebookPath(notebook) # /
path = base / "Experiments" / "2024" # /Experiments/2024
Resolving relative paths:
rel = NotebookPath("Results")
abs_path = rel.resolve(NotebookPath(folder)) # /Experiments/2024/Results
Getting a relative path between two nodes:
page_path = NotebookPath(page) # /Experiments/2024/Results
rel = page_path.relative_to(folder) # Results (relative to /Experiments/2024)
Checking containment:
page_path.is_relative_to(folder) # True if page is inside folder
page_path.is_relative_to(notebook) # True (everything is inside the notebook)