API Reference
This section documents the Python API for ggblab, automatically generated from docstrings.
Main Interface
ggblab: Interactive geometric scene construction with Python and GeoGebra.
This package provides a JupyterLab extension that opens a GeoGebra applet and enables bidirectional communication between Python and GeoGebra through a dual-channel architecture (IPython Comm + Unix socket/TCP WebSocket).
- Main Components:
GeoGebra: Primary interface for controlling GeoGebra applets
ggb_comm: Communication layer (IPython Comm + out-of-band socket)
ggb_construction: GeoGebra file (.ggb) loader and saver
ggb_parser: Dependency graph parser for GeoGebra constructions
Example
>>> from ggblab import GeoGebra
>>> ggb = await GeoGebra().init()
>>> await ggb.command("A=(0,0)")
>>> value = await ggb.function("getValue", ["A"])
- Note:
Heavy I/O and parsing implementations have been moved to the optional package ggblab_extra. If you need DataFrame-based construction I/O or the full parser implementation, install and import ggblab_extra. This package keeps lightweight shims for backward compatibility which will be deprecated and removed in a future major release.
The public API has been split between a compact core (this package) and an
optional collection of helpers in ggblab_extra. Callers that rely on
the extras should install that package; otherwise consumers should prefer
the minimal APIs provided here. Deprecated shims exist to ease migration and
will emit DeprecationWarning when used.
GeoGebra Control
- class ggblab.ggbapplet.GeoGebra[source]
Bases:
objectMain interface for controlling GeoGebra applets from Python.
This class implements a singleton pattern to ensure only one GeoGebra instance per kernel session. It provides async methods for sending commands and calling GeoGebra API functions.
The communication uses a dual-channel architecture: - IPython Comm: Primary control channel - Unix socket/TCP WebSocket: Out-of-band response delivery during cell execution
Semantic Validation: - check_syntax: Validates command strings can be tokenized - check_semantics: Validates referenced objects exist in applet - Future: Type checking, scope/visibility validation
- file
GeoGebra file (.ggb) loader and saver
- Type:
ggb_file
- construction
Backward compatibility alias for file attribute
- parser
Dependency graph parser with command learning
- kernel_id
Current Jupyter kernel ID
- Type:
str
- app
ipylab frontend interface
- Type:
JupyterFrontEnd
- check_syntax
Enable syntax validation (default: False)
- Type:
bool
- check_semantics
Enable semantic validation (default: False)
- Type:
bool
- _applet_objects
Cached object names from applet (updated by command/function)
- Type:
set
Note
The parser attribute lives in this package and provides tokenization and command-cache features used for syntax/semantics checks.
- Note:
Heavy I/O and convenience helpers (DataFrame construction, persistence helpers such as
ConstructionIO.save_dataframe, and richer parser implementations) have been moved to the optionalggblab_extrapackage. Installggblab_extrato access those features; the core package keeps lightweight shims and will emit DeprecationWarning when using deprecated helpers.
Example
>>> ggb = GeoGebra() >>> await ggb.init() >>> await ggb.command("A=(0,0)") >>> result = await ggb.function("getValue", ["A"])
>>> # With validation >>> ggb.check_syntax = True >>> ggb.check_semantics = True >>> await ggb.command("Circle(A, B)")
- async command(c)[source]
Execute a GeoGebra command with optional validation.
- Parameters:
c (str) – GeoGebra command string (e.g., “A=(0,0)”, “Circle(A, 2)”).
- Returns:
Response from GeoGebra (typically includes object label).
- Return type:
dict
- Raises:
GeoGebraSyntaxError – If syntax check is enabled and command has syntax errors.
GeoGebraSemanticsError – If semantics check is enabled and validation fails.
GeoGebraAppletError – If GeoGebra applet produces error events during execution.
Example
>>> await ggb.command("A=(0,0)") >>> await ggb.command("B=(3,4)") >>> await ggb.command("Circle(A, Distance(A, B))")
>>> # With validation >>> ggb.check_syntax = True >>> ggb.check_semantics = True >>> await ggb.command("Circle(A, B)") # Validates syntax and references
>>> # Error handling >>> try: ... await ggb.command("Unbalanced(") ... except GeoGebraAppletError as e: ... print(f"Applet error: {e.error_message}")
- async function(f, args=None)[source]
Call a GeoGebra API function.
- Parameters:
f (str) – GeoGebra API function name (e.g., “getValue”, “getXML”).
args (list, optional) – Function arguments. Defaults to None.
- Returns:
Function return value from GeoGebra.
- Return type:
Any
Example
>>> value = await ggb.function("getValue", ["A"]) >>> xml = await ggb.function("getXML", ["A"]) >>> all_objs = await ggb.function("getAllObjectNames")
- async init()[source]
Initialize the GeoGebra widget and communication channels.
This method: 1. Starts the out-of-band socket server (Unix socket on POSIX, TCP WebSocket on Windows) 2. Registers the IPython Comm target (‘ggblab-comm’) 3. Opens the GeoGebra widget panel via ipylab with communication settings 4. Initializes the object cache
The widget is launched programmatically to pass kernel-specific settings (Comm target, socket path) before initialization, avoiding the limitations of fixed arguments from Launcher/Command Palette.
- Returns:
Self reference for method chaining.
- Return type:
Example
>>> ggb = await GeoGebra().init() >>> # GeoGebra panel opens in split-right position
Communication Layer
- class ggblab.comm.ggb_comm[source]
Bases:
objectDual-channel communication layer for kernel↔widget messaging.
Implements a combination of IPython Comm (primary) and out-of-band socket (Unix domain socket on POSIX, TCP WebSocket on Windows) to enable message delivery during cell execution when IPython Comm is blocked.
IPython Comm cannot receive messages while a notebook cell is executing, which breaks interactive workflows. The out-of-band socket solves this by providing a secondary channel for GeoGebra responses.
- Architecture:
IPython Comm: Command dispatch, event notifications, heartbeat
Out-of-band socket: Response delivery during cell execution
Comm target is fixed at ‘ggblab-comm’ because multiplexing via multiple targets would not solve the IPython Comm receive limitation.
- target_comm
IPython Comm object
- target_name
Comm target name (‘ggblab-comm’)
- Type:
str
- server_handle
WebSocket server handle
- server_thread
Background thread running the socket server
- clients
Currently connected WebSocket clients
- Type:
set
- socketPath
Unix domain socket path (POSIX)
- Type:
str
- wsPort
TCP port number (Windows)
- Type:
int
- pending_futures
Mapping of message-id to Future for awaiting responses
- Type:
dict
- recv_events
Event queue for frontend notifications
- Type:
queue.Queue
- See:
docs/architecture.md for detailed communication architecture.
Note
This module focuses on communication primitives. Higher-level construction I/O and analysis helpers are provided in the optional
ggblab_extrapackage; the core keeps communication and minimal shims only.- Future improvement:
Consider integrating the out-of-band server with Jupyter’s Tornado/ioloop to avoid cross-thread asyncio interactions. This would simplify event-loop boundaries but has non-trivial implementation cost, so it’s deferred for future work.
- async client_handle(client_id)[source]
Handle messages from a connected websocket client.
Routes command responses into pending_futures and event messages into recv_events.
- handle_recv(msg)[source]
Handle a message received via IPython Comm (command response).
Event-type messages are routed via the out-of-band socket; this method processes response messages delivered over IPython Comm.
- logs = []
- mid = None
- pending_futures = {}
- recv_events = <queue.Queue object>
- recv_msgs = {}
- register_target_cb(comm, msg)[source]
Register the IPython Comm connection callback and install message handlers.
- async send_recv(msg)[source]
Send a message via IPython Comm and wait for response via out-of-band socket.
This method: 1. Generates a unique message ID (UUID) 2. Sends the message via IPython Comm to the frontend 3. Waits for the response to arrive via the out-of-band socket 4. Raises GeoGebraAppletError if error events are received 5. Returns the response payload
The 3-second timeout is sufficient for interactive operations. For long-running operations, decompose into smaller steps.
- Parameters:
msg (dict or str) – Message to send (will be JSON-serialized).
- Returns:
Response payload from GeoGebra.
- Return type:
dict
- Raises:
asyncio.TimeoutError – If no response arrives within 3 seconds.
GeoGebraAppletError – If the applet produces error events.
Example
>>> response = await comm.send_recv({ ... "type": "command", ... "payload": "A=(0,0)" ... })
- async server()[source]
Run the out-of-band socket server.
Uses a Unix domain socket on POSIX systems and a TCP WebSocket otherwise.
- start()[source]
Start the out-of-band socket server in a background thread.
Creates a Unix domain socket (POSIX) or TCP WebSocket server (Windows) and runs it in a daemon thread. The server listens for GeoGebra responses.
- thread = None
- thread_lock = <unlocked _thread.lock object>
Construction File Handler
Dependency Parser
- class ggblab.parser.ggb_parser(cache_path=None, cache_enabled=True)[source]
Bases:
objectMinimal parser exposing only tokenize_with_commas and reconstruct_from_tokens.
This lightweight class preserves the original implementations of the two methods while removing other parser functionality. For richer parser features or DataFrame-based construction helpers, install
ggblab_extra. The core implementation intentionally keeps a compact surface area so that importingggblabremains lightweight.- reconstruct_from_tokens(parsed_tokens)[source]
Reconstruct the original command string from tokenized structured list.
Takes a nested list structure produced by tokenize_with_commas() and reconstructs the original command string with proper parentheses, commas, and spacing.
- Parameters:
parsed_tokens (list or str) – Tokenized structured list, or a single token as a string.
- Returns:
Reconstructed command string matching the original input structure.
- Return type:
str
- Raises:
ValueError – If parsed_tokens contains unexpected types.
Examples
>>> parser.reconstruct_from_tokens(['Circle', ['A', ',', '2']]) 'Circle(A, 2)'
>>> parser.reconstruct_from_tokens(['Distance', [['Point', ['1', ',', '2']], ',', 'B']]) 'Distance(Point(1, 2), B)'
Note
This function is the inverse of tokenize_with_commas(). It handles proper spacing around operators and parentheses.
The ‘register_expr’ parameter (commented out) was intended for register expressions, where applet-assigned labels could be replaced with construction-order-based abstract expressions like ‘${n}’, since GeoGebra may reassign object labels but construction order remains stable.
- tokenize_with_commas(cmd_string, extract_commands=False)[source]
Tokenize a GeoGebra command string into a structured list representation.
Parses a mathematical or GeoGebra-like command string and converts it into a nested list structure that preserves parentheses, brackets, and commas. This is useful for analyzing GeoGebra command syntax and extracting object dependencies.
=== COMMA PRESERVATION AND GEOGEBRA’S IMPLICIT MULTIPLICATION ===
This tokenizer preserves commas as explicit tokens for a critical reason: GeoGebra outputs commands with implicit multiplication operators omitted.
Example
Internal representation: Circle(2 * a, b) GeoGebra output: Circle(2a, b) <- Information loss!
The ‘*’ operator is completely omitted, destroying information. This is a one-way transformation: we can’t reliably reconstruct “2*a” from “2a” without external context (is it “2 times a” or “variable named 2a”?).
BUT: GeoGebra ALWAYS uses comma-separation for parameter lists. We exploit this invariant. By preserving commas in the token stream, we can: 1. Identify parameter boundaries (comma = separator) 2. Use whitespace/context to infer where implicit multiplication occurred
This is a workaround for GeoGebra’s poor design. So the question becomes:
BLAME GeoGebra for being a one-way encoder (lose the *? Why?)
PRAISE the developer who recognized the comma-separation invariant
Engineering lesson: deal with imperfect systems and find creative solutions. GeoGebra didn’t help us. We had to be smarter than it.
- Parameters:
cmd_string (str) – Input command string (e.g., “Circle(A, Distance(A, B))”).
extract_commands (bool, optional) – If True, also extract command name candidates (tokens preceding ‘(’ or ‘[‘). Returns a dict with ‘tokens’ and ‘commands’ keys. If False (default), returns only the token list for backward compatibility. Default: False
register_expr (#) – Future feature - if True, replace object references
${0} (# with abstract labels like)
${1}
on (etc. based)
protocol. (# generation order in the construction)
rename (# This is useful because GeoGebra applets may)
runtime (# objects at)
remains (but the generation order)
implemented. (# stable within a construction. Not yet)
- Returns:
If extract_commands=False (default): Nested list structure with tokens. Parentheses/brackets create nested lists; commas are preserved as ‘,’.
If extract_commands=True: Dict with keys: - ‘tokens’: Nested list structure (as above) - ‘commands’: Set of command name candidates (tokens preceding ‘(’ or ‘[‘)
- Return type:
list or dict
- Raises:
ValueError – If parentheses/brackets are mismatched.
Examples
>>> tokenize_with_commas("Circle(A, 2)") ['Circle', ['A', ',', '2']]
>>> tokenize_with_commas("Circle(A, 2)", extract_commands=True) {'tokens': ['Circle', ['A', ',', '2']], 'commands': {'Circle'}}
>>> tokenize_with_commas("Distance(Point(1, 2), B)") ['Distance', [['Point', ['1', ',', '2']], ',', 'B']]
>>> tokenize_with_commas("Distance(Point(1, 2), B)", extract_commands=True) {'tokens': ['Distance', [['Point', ['1', ',', '2']], ',', 'B']], 'commands': {'Distance', 'Point'}}
Note
Empty or non-string input returns an empty list (or empty dict if extract_commands=True) without raising an error.
Commas are INTENTIONALLY preserved as tokens to work around GeoGebra’s implicit multiplication. This is not a quirk; it’s the core design decision.
Future (register_expr parameter): When implemented, would enable stable object references by using construction order indices instead of runtime labels. Example output: [‘Circle’, [‘${0}’, ‘,’, ‘${1}’]] if register_expr=True and the objects were the 0th and 1st in the protocol.
Parser Utilities
Schema Loader
- class ggblab.schema.ggb_schema[source]
Bases:
objectGeoGebra XML schema loader and validator.
Manages the GeoGebra XML schema (XSD) for validating and parsing .ggb construction files. The schema is automatically downloaded from the official GeoGebra site and cached locally for offline use.
The schema enables: - XML validation of GeoGebra constructions - Conversion between XML and Python dictionaries - Type-safe parsing of construction elements
- url
URL of the GeoGebra common.xsd schema file
- Type:
str
- local_path
Local cache path for the downloaded schema
- Type:
str
- schema_content
Raw XSD content as string
- Type:
str
- schema
Compiled schema object for validation
- Type:
xmlschema.XMLSchema
Example
>>> schema = ggb_schema() >>> # Schema is loaded and ready for use >>> data_dict = schema.schema.to_dict(xml_string)
Note
The schema is downloaded once and cached in xsd/common.xsd. Delete the cache to force re-download on next instantiation.
Note
Heavy DataFrame-based validation or IR-driven workflows are provided by the optional
ggblab_extrapackage. The core schema loader is intended for low-level XML validation and parsing only.- local_path = 'xsd/common.xsd'
- url = 'http://www.geogebra.org/apps/xsd/common.xsd'
- ggblab.schema.cache_schema_locally(schema_url, local_file_path)[source]
Download and cache a schema file from URL.
Downloads an XML schema from the specified URL and saves it to a local file for offline use. If the file already exists, uses the cached version instead of re-downloading.
- Parameters:
schema_url (str) – URL of the schema file to download.
local_file_path (str) – Path where the schema should be cached.
- Returns:
Content of the schema file, or None if download fails.
- Return type:
str
Examples
>>> content = cache_schema_locally( ... 'http://example.com/schema.xsd', ... 'cache/schema.xsd' ... ) Using local cached file: cache/schema.xsd
Note
Future enhancement: Add logic to check file age or Last-Modified header to refresh stale cached schemas.