API Reference

class notcl.TclTool(cwd: Path | str | None = None, interact: bool = False, log_commands: bool = True, log_retvals: bool = False, log_fancy: bool = True, debug_tcl: bool = False, debug_py: bool = False, abort_on_error: bool = True)

Base class for wrapping Tcl-based tools as Python interfaces.

TclTool spawns a Tcl tool as a subprocess and communicates with it via named pipes. Subclasses must implement the cmdline() method to specify how to invoke the tool.

Example:

from notcl import TclTool

class Tclsh(TclTool):
    def cmdline(self):
        return ["tclsh", self.script_name()]

with Tclsh() as t:
    result = t.expr(3, "*", 5)
    print(result)  # 15

The tool is used as a context manager. On entry, the subprocess is spawned and a communication channel is established. On exit, the tool is terminated (unless interact=True).

All Tcl commands and procedures become callable as methods on the TclToolInterface object yielded by the context manager. Return values are wrapped in TclRemoteObjRef objects that preserve opaque handles when passed back to Tcl.

__init__(cwd: Path | str | None = None, interact: bool = False, log_commands: bool = True, log_retvals: bool = False, log_fancy: bool = True, debug_tcl: bool = False, debug_py: bool = False, abort_on_error: bool = True)

Initialize the TclTool.

Parameters:
  • cwd – Working directory for the Tcl tool subprocess. Defaults to the current working directory.

  • interact – If True, keep the Tcl tool open for interactive use after the with block exits. The Python script waits for manual termination. Default: False.

  • log_commands – Print all Tcl commands sent to the tool to stdout. Default: True.

  • log_retvals – Print all return values received from Tcl to stdout. Default: False.

  • log_fancy – Use ANSI color codes in log output. Default: True.

  • debug_tcl – Enable detailed debug output from the Tcl side. Default: False.

  • debug_py – Enable detailed debug output from the Python side. Default: False.

  • abort_on_error – Terminate the child process when a Tcl error occurs, even if interact=True. Default: True.

abstract cmdline() list[str]

Return the command line for invoking the Tcl tool.

Subclasses must implement this method to specify how to start the tool. The returned list is passed to subprocess.Popen.

Returns:

List of strings where the first element is the executable name or path, and subsequent elements are command line arguments. The tool must be configured to execute the script returned by self.script_name() at startup.

Example:

def cmdline(self):
    return ["tclsh", self.script_name()]
script_name() str

Return the path to the NoTcl communication script.

This method should be called from cmdline() to get the path to the notcl.tcl script that the tool must execute at startup.

Returns:

Path to the notcl.tcl script when called during cmdline() execution. Returns a placeholder string at other times.

Note

The path is only valid while cmdline() is being executed. Do not cache or store this value.

eval(cmd: str) TclRemoteObjRef

Evaluate a Tcl command string directly.

This is a low-level method. Prefer using the method call syntax on TclToolInterface (e.g., t.expr(3, “*”, 5)) for most use cases.

Parameters:

cmd – Tcl command string to evaluate.

Returns:

TclRemoteObjRef wrapping the return value.

Raises:

TclError – If the Tcl command fails.

class notcl.tcltool.TclToolInterface(tcl_tool: TclTool)

Interface for calling Tcl commands as Python methods.

TclToolInterface is yielded when entering a TclTool context. All Tcl commands and procedures become callable as methods on this object.

Method calls are automatically translated to Tcl command invocations, with Python arguments converted to Tcl-friendly strings. Return values are wrapped in TclRemoteObjRef objects.

Example:

with Tclsh() as t:
    # t is a TclToolInterface object
    result = t.expr(3, "*", 5)  # Calls Tcl command: expr {3} {*} {5}
    print(result)  # 15
__getattr__(name)

Return a callable that invokes the Tcl command with the given name.

Any attribute access becomes a method call to the corresponding Tcl command or procedure. Arguments are automatically converted using tclobj.encode().

Parameters:

name – Name of the Tcl command to invoke.

Returns:

A callable that accepts *args and **kwargs, converts them to Tcl syntax, and returns a TclRemoteObjRef.

Example:

result = t.expr(3, "*", 5)  # Invokes: expr {3} {*} {5}
items = t.lreverse([1, 2, 3])  # Invokes: lreverse {1 2 3}
__call__(cmd: str) TclRemoteObjRef

Evaluate a raw Tcl command string.

Calling the TclToolInterface object directly allows evaluation of arbitrary Tcl strings without argument conversion.

Parameters:

cmd – Tcl command string to evaluate.

Returns:

TclRemoteObjRef wrapping the return value.

Raises:

TclError – If the Tcl command fails.

Example:

result = t("expr 3 * 5")  # Direct string evaluation
result = t.expr(3, "*", 5)  # Equivalent method call
class notcl.tclobj.TclRemoteObjRef(tool, cmd_idx: int, value: str, cmd: str)

Wrapper for Tcl command return values that preserves opaque handles.

All Tcl command return values are wrapped in TclRemoteObjRef objects. When passed back to Tcl, NoTcl uses a reference to the original Tcl object rather than its string representation, preserving the internal representation and memory address of opaque handles.

TclRemoteObjRef supports conversion to Python types via str(), int(), and float(). For object-oriented Tcl APIs, methods can be called on the TclRemoteObjRef (see __getattr__).

Example:

result = t.expr(3, "*", 5)
print(str(result))  # "15"
print(int(result))  # 15

handle = t.create_object()
t.modify_object(handle)  # Uses Tcl reference, not string
ref_str()

Get the Tcl reference string for this object.

Returns:

A Tcl variable reference string (e.g., “$cmd_results(123)”) that can be used in raw Tcl command strings to reference this object.

Example:

handle = t.create_object()
t(f"puts {handle.ref_str()}")  # Explicitly use Tcl reference
proc_call(name, args, kwargs)

Wrapper for TclTool.proc_call for using TclRemoteObjRefs in an object-oriented way. Internally invoked; do not use directly.

class notcl.TclError(text: str)

Exception raised when a Tcl command fails.

When a Tcl command returns an error (non-zero error code), NoTcl raises a TclError with the Tcl error message. The TclTool remains usable after catching the exception.

text

The Tcl error message.

Example:

from notcl import TclError

with Tclsh() as t:
    try:
        t.expr("invalid syntax")
    except TclError as e:
        print(f"Tcl error: {e}")
    # Tool is still usable
    result = t.expr(1, "+", 1)
class notcl.ChildProcessEarlyExit

Exception raised when the Tcl subprocess terminates unexpectedly.

This exception is raised when the Tcl child process exits before the TclTool context manager exits normally. This can happen if:

  • The Tcl process crashes

  • The Tcl process calls exit explicitly

  • The Tcl process is killed by a signal

NoTcl detects early termination via a sentinel named pipe that becomes readable (EOF) when the child process dies.

Example:

from notcl import ChildProcessEarlyExit

try:
    with Tclsh() as t:
        t.exit(0)  # Tcl process terminates
        t.expr(1, "+", 1)  # Never reached
except ChildProcessEarlyExit:
    print("Tcl process exited early")