%there: SSH client#
Jupyter magic commands#
Commands are provided by the herethere.magic extension.
%load_ext herethere.magic
%connect-there#
Connect to remote interpreter via SSH.
Command takes a single optional argument: location of connection config.
If argument is not provided, values are loaded from the there.env file.
Config values can be overridden by environment variables with same names.
%env THERE_PORT=8022
%connect-there there.env
env: THERE_PORT=8022
there.env example#
# Hostname or address to connect to.
# Defaults to 127.0.0.1.
THERE_HOST=127.0.0.1
# Port number to connect to.
# Defaults to 8022.
THERE_PORT=8022
# Credentials
THERE_USERNAME=debug
THERE_PASSWORD=xxx
%there group of commands#
%there --help
Usage: there [OPTIONS] COMMAND [ARGS]...
Group of commands to run on remote side.
Options:
-b, --background Run in background
-l, --limit INTEGER RANGE Number of lines to show when in background mode
[1<=x<=1000]
-d, --delay FLOAT The time to wait in seconds before executing a
command
--help Show this message and exit.
Commands:
download Download files and directories.
get Evaluate expression on remote side and return its value.
log Listen for log records, send logging output to stdout.
shell Execute shell command on remote side.
upload Upload files and directories.
By default, %there executes Python code when no command is specified.
%%there#
Execute Python code on the remote side.
%%there
import this
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
%%there shell#
%there shell --help
Usage: there shell [OPTIONS]
Execute shell command on remote side.
Options:
--help Show this message and exit.
%%there shell
for i in 1 2 3
do
echo -n "$i"
done
123
Periodically run the top command in the background and show the last two lines of output:
%%there -bl 2 shell
while :; do
top -b | head -n 2
sleep 10
done
%there get#
Evaluate one Python expression on the remote side and return the result as a local Python value.
%%there
x = 10
value = %there get x + 1
value
11
Returned values are serialized with pickle. Nested values are supported when
every contained object is pickle-serializable, and any custom classes must be
available in the local environment. %there get is intended for small to
medium inspectable values such as counters, summaries, or configuration.
To avoid accidental large transfers, values whose pickle payload is larger
than 32 MiB are rejected by default.
%there upload#
%there upload --help
Usage: there upload [OPTIONS] LOCAL_PATH... [REMOTE_PATH]
Upload files and directories.
With one path, upload to the current remote SFTP directory. With multiple
paths, the last path is the remote destination.
Options:
--help Show this message and exit.
The SFTP root directory is set by the HERE_SFTP_ROOT value of the here-server
config. If unset, it defaults to the here-server process current directory.
This is not a process chroot or sandbox.
%there upload sample-note.txt sample-script.py sample-dir .
When uploading one file or directory, the remote destination defaults to the current SFTP directory:
%there upload sample-note.txt
%%there shell
find . -maxdepth 2 -type f | sort
./sample-dir/nested.txt
./sample-note.txt
./sample-script.py
%there download#
%there download --help
Usage: there download [OPTIONS] REMOTE_PATH... [LOCAL_PATH]
Download files and directories.
With one path, download to the current local directory. With multiple paths,
the last path is the local destination.
Options:
--help Show this message and exit.
Files and directories are downloaded from the same SFTP root used by %there upload.
%there download sample-note.txt ./downloaded-note.txt
from pathlib import Path
Path("downloaded-note.txt").read_text()
'hello from the notebook\n'
When downloading one file or directory, the local destination defaults to the current local directory. Directories use the same command:
%there download sample-dir ./downloaded-sample-dir
Path("downloaded-sample-dir/nested.txt").read_text()
'nested sample file\n'
For generated outputs that are too large for %there get, save a file under
the SFTP root and fetch it:
%%there
import csv
rows = [
{"sensor": "greenhouse-1", "metric": "temperature_c", "value": 21.8},
{"sensor": "greenhouse-1", "metric": "humidity_pct", "value": 58.2},
{"sensor": "greenhouse-2", "metric": "temperature_c", "value": 20.9},
{"sensor": "greenhouse-2", "metric": "humidity_pct", "value": 61.4},
]
with open("sample-data.csv", "w", newline="") as file:
writer = csv.DictWriter(file, fieldnames=["sensor", "metric", "value"])
writer.writeheader()
writer.writerows(rows)
%there download sample-data.csv ./sample-data.csv
Path("sample-data.csv").read_text()
'sensor,metric,value\ngreenhouse-1,temperature_c,21.8\ngreenhouse-1,humidity_pct,58.2\ngreenhouse-2,temperature_c,20.9\ngreenhouse-2,humidity_pct,61.4\n'
%there log#
%there log --help
Usage: there log [OPTIONS]
Listen for log records, send logging output to stdout. This command blocks
the execution thread until stopped.
Options:
--help Show this message and exit.
Note
Since the command blocks and never ends, it is useful to run with the --background (-b) option
%there -b -l 10 log
Emit a log record on the remote side. The record is streamed into the background output above in an interactive notebook.
%%there -d 0.2
import logging
logging.warning("hello from remote logging")
Example output:
[WARNING] 2026-05-25 14:00:00 SSHServerHereThread_0 root: hello from remote logging
Custom subcommands#
New subcommands can be registered with the @there_code_shortcut decorator and click options:
from herethere.there.commands import there_code_shortcut
import click
@there_code_shortcut
@click.option("-n", "--number_to_print", type=int)
def mycommand(code: str, number_to_print):
return f"print({number_to_print})"
%there mycommand -n 123
123
Using it in code#
from herethere.everywhere import ConnectionConfig
from herethere.there import Client
config = ConnectionConfig.load(prefix="there")
client = Client()
await client.connect(config)
await client.runcode("print('Hello there :)')")
Hello there :)
await client.runcode("x = 10")
value = await client.get("x + 1")
await client.shell("sleep 1 ; ping -c 1 8.8.8.8")
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=117 time=95.3 ms
--- 8.8.8.8 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 95.316/95.316/95.316/0.000 ms
await client.disconnect()