Module bussilab.jremote

Module implementing tools for remote jupyter connections.

This module contains some utilities that are mostly designed to be used as command line tools. The interface of the functions defined in this module is subject to changes. One should instead use the subcommands jrun and jremote in the cli_documentation submodule.

Functions

def find_free_port()
Expand source code
def find_free_port():
    """Returns the number of a free port."""
    with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
        s.bind(('', 0))
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        return s.getsockname()[1]

Returns the number of a free port.

def remote(server: str,
python_exec: str = 'python',
dry_run: bool = False,
list_only: bool = False,
server_url: str = '',
port: int = 0,
index: int = 0,
open_cmd: str = '')
Expand source code
def remote(server: str,
           python_exec: str = "python",
           dry_run: bool = False,
           list_only: bool = False,
           server_url: str ="",
           port: int = 0,
           index: int = 0,
           open_cmd: str = ""):

    cmd =  "( " + python_exec + " -m jupyter notebook list 2> /dev/null ) ;"
    cmd += "echo x ;"
    cmd += "( " + python_exec + " -m jupyterlab list 2>&1 ) ;" # needed for jupyter-lab list 3.6

    print("server:", server)
    print("cmd:", cmd)

    if dry_run and list_only:
        return

    if server_url == "":
        ll = []
        ll_localhost = []
        args = ['ssh', server, cmd]
        ll_type = []
        found_lab=False

        for l in subprocess.run(args,
                                stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE,
                                universal_newlines=True,
                                check=True).stdout.split('\n'):
            l=re.sub(r"^\[JupyterServerListApp\] ","",l) # needed for newer jupyter-lab list 3.6
            if re.match("^http", l):
                ll.append(re.sub("localhost", server, l))
                ll_localhost.append(l)
                if found_lab:
                    ll_type.append("(L)")
                else:
                    ll_type.append("(N)")

            if l == "x":
                found_lab = True

        for i in range(len(ll)):
            print(str(i+1)+") "+ll[i]+" "+ll_type[i])

        if list_only:
            return

        if len(ll_localhost) == 1:
            index = 1

        if index == 0:
            while True:
                sys.stdout.write("Choose a notebook or interrupt (^c):")
                sys.stdout.flush()
                iii = sys.stdin.readline()
                try:
                    index = int(iii)
                except ValueError:
                    print("Cannot parse " + iii.strip() + ", try again.")
                if index>=1 and index<=len(ll_localhost):
                    print(index)
                    break
                print("Index " + str(index) + " out of range, try again.")

        url = ll_localhost[index-1].split()[0]
    else:
        url = server_url

    print("Chosen url:", url)

    if not re.match(r"^http://localhost:[0-9]*/\?token=[0-9a-f]*$", url):
        raise Exception("URL "+url+" looks incorrectly formatted")

    server_port = re.sub("^http://localhost:", "", url)
    server_port = re.sub("/.*$", "", server_port)

    if port == 0:
        port = find_free_port()

    ssh_cmd = ["ssh", "-NL", str(port) + ":localhost:" + str(server_port), server]

    xopen_cmd = [] # list rather than string

    if open_cmd == "":
        plat = platform.system()
        if plat == "Darwin":
            xopen_cmd.append("open")
        elif plat == "Linux":
            xopen_cmd.append("xdg-open")
        else:
            raise Exception("Unknown platform " + plat)
    else:
        xopen_cmd = [open_cmd]

    xopen_cmd.append(re.sub(":" + str(server_port), ":" + str(port), url))
    print("open_cmd:", ' '.join(xopen_cmd))
    print("ssh:", ' '.join(ssh_cmd))
    if dry_run:
        print(xopen_cmd)
    else:
        ssh = subprocess.Popen(ssh_cmd)
        try:
            print("Don't kill this terminal or you will be disconnected!")
            time.sleep(1)
            subprocess.call(xopen_cmd)
            print("Use CTRL+c to kill the connection")
        except KeyboardInterrupt:
            print("Interrupted")
        finally:
            try:
                ssh.communicate()
            except KeyboardInterrupt:
                print("Interrupted")
def run_server(dry_run: bool = False,
port: int = 0,
screen_cmd: str = 'screen',
screen_log: str = '',
no_screen: bool = False,
keep_ld_library_path: bool = True,
python_exec: str = '',
sockname: str = '(path):(port):jupyter',
lab: bool = False,
detach: bool = False)
Expand source code
def run_server(dry_run: bool = False,
               port: int = 0,
               screen_cmd: str = "screen",
               screen_log: str = "",
               no_screen: bool = False,
               keep_ld_library_path: bool = True,
               python_exec: str = "",
               sockname: str = "(path):(port):jupyter",
               lab: bool = False,
               detach: bool = False):
    """Runs a jupyter server inside a screen command.

       This function is only designed to be used as a command line
       tool.
    """
    if python_exec == "":
        python_exec = sys.executable
    print("python_exec:", python_exec)
    if port == 0:
        port = find_free_port()
    print("port:", port)
    cmd = []
    if not no_screen:
        cmd.extend(shlex.split(screen_cmd)) # allows screen_cmd to contain space separated options
        if detach:
            cmd.append("-d")
        if screen_log != "":
            cmd.append("-L")
            if screen_log != "screenlog.0":
              cmd.append("-Logfile")
              cmd.append(screen_log)
        cmd.append("-m")
        cmd.append("-h")
        cmd.append("100000") # scrollback
        cmd.append("-S")
        cmd.append(_adjust_sockname(sockname,port))
        if keep_ld_library_path and 'LD_LIBRARY_PATH' in os.environ:
            cmd.append("/usr/bin/env")
            cmd.append("LD_LIBRARY_PATH=" + os.environ["LD_LIBRARY_PATH"])
    cmd.extend(shlex.split(python_exec)) # allows python_exec to contain space separated options
    cmd.append("-m")
    if lab:
        cmd.append("jupyterlab")
    else:
        cmd.append("jupyter")
        cmd.append("notebook")
    cmd.append("--no-browser")
    cmd.append("--port=" + str(port))
    print("cmd:", cmd)
    if dry_run:
        return
    subprocess.call(cmd)

Runs a jupyter server inside a screen command.

This function is only designed to be used as a command line tool.