Created
July 23, 2025 14:14
-
-
Save sudocurse/714ed0f054699d838fba0452ec790339 to your computer and use it in GitHub Desktop.
forwarding touchdesigner's running REPL
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import code | |
import socket | |
import sys | |
''' | |
Forwarding a shell out of TD's textport | |
1. clean any leftover ports | |
cleanup_ports_simple([8889, 8890, 8891]) | |
2. create a new server and keep the socket around in a list so you can clean it up later | |
servers = [] # list of sockets | |
servers.append(interact(8889)) | |
this will block in TD. | |
3. connect from your shell. i'd recommend using `socat` or at least rlwrap your telnet/netcat connection. | |
Here's an example session: | |
``` | |
socat READLINE TCP:localhost:8889 8147ms | |
Python 3.11.1 (heads/3.11-Derivative-dirty:0d650c50f2, Jan 26 2023, 17:30:19) [Clang 14.0.0 (clang-1400.0.29.202)] on darwin | |
Type "help", "copyright", "credits" or "license" for more information. | |
>>> op('/project1') | |
type:containerCOMP path:/project1 | |
>>> op('/project1').name | |
'project1' | |
>>> for child in op('project1').findChildren(): | |
... print(child) | |
... | |
/project1/box1 | |
/project1/cam1 | |
/project1/cam1/file1 | |
/project1/geo1 | |
/project1/geo1/in1 | |
/project1/geo2 | |
/project1/geo2/in1 | |
/project1/res1 | |
/project1/comp1 | |
/project1/geo11 | |
/project1/geo11/in1 | |
/project1/geo12 | |
/project1/geo12/in1 | |
/project1/geo13 | |
/project1/geo13/in1 | |
/project1/info1 | |
/project1/math1 | |
/project1/math2 | |
/project1/math3 | |
/project1/null1 | |
... | |
``` | |
4. when you're done, to free up the port (so you don't get "Address is already in use" errors): | |
cleanup_server_simple(8889) | |
this is the same as the cleanup_ports function. | |
if you have a bunch lying around you can just throw in the whole servers list | |
cleanup_servers(servers) | |
''' | |
def cleanup_server_simple(port): | |
"""Try to bind to a specific port to force cleanup""" | |
try: | |
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | |
s.bind(('localhost', port)) | |
s.close() | |
print(f"Port {port} is now free") | |
except OSError as e: | |
print(f"Port {port}: {e}") | |
def cleanup_ports_simple(ports=[8888, 8889, 8890, 8891, 8892]): | |
for port in ports: | |
cleanup_server_simple(port) | |
def cleanup_servers(servers): | |
for server in servers[:]: | |
try: | |
server.close() | |
servers.remove(server) | |
print(f"Closed and removed server") | |
except Exception as e: | |
print(f"Error closing server: {e}") | |
servers = [] | |
def start_server(port=8889): | |
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | |
server.bind(('localhost', port)) | |
server.listen(1) | |
print(f"Python console available at: socat localhost {port}") # rlwrap'ed telnet might also be usable | |
return server | |
def wait_for_connection(server): | |
conn, addr = server.accept() | |
print(f"Connection from {addr}") | |
return conn | |
def setup_io_redirect(conn): | |
socket_file = conn.makefile('rw') | |
orig_stdin = sys.stdin | |
orig_stdout = sys.stdout | |
orig_stderr = sys.stderr | |
sys.stdin = socket_file | |
sys.stdout = socket_file | |
sys.stderr = socket_file | |
return socket_file, (orig_stdin, orig_stdout, orig_stderr) | |
def restore_io(socket_file, originals): | |
orig_stdin, orig_stdout, orig_stderr = originals | |
sys.stdin = orig_stdin | |
sys.stdout = orig_stdout | |
sys.stderr = orig_stderr | |
socket_file.close() | |
def interact(port=8889): | |
server = start_server(port) | |
conn = wait_for_connection(server) | |
socket_file, originals = setup_io_redirect(conn) | |
try: | |
code.interact(local=globals()) | |
finally: | |
restore_io(socket_file, originals) | |
conn.close() | |
return server | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
cheat sheet for some ultra-basic TD python operations
link to the comp class methods