Files
connpy/docs/connpy/tunnels.html
T
2026-05-28 18:22:00 -03:00

552 lines
25 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
<meta name="generator" content="pdoc3 0.11.5">
<title>connpy.tunnels API documentation</title>
<meta name="description" content="">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/typography.min.css" integrity="sha512-Y1DYSb995BAfxobCkKepB1BqJJTPrOp3zPL74AWFugHHmmdcvO+C48WLrUOlhGMc0QG7AE3f7gmvvcrmX2fDoA==" crossorigin>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css" crossorigin>
<style>:root{--highlight-color:#fe9}.flex{display:flex !important}body{line-height:1.5em}#content{padding:20px}#sidebar{padding:1.5em;overflow:hidden}#sidebar > *:last-child{margin-bottom:2cm}.http-server-breadcrumbs{font-size:130%;margin:0 0 15px 0}#footer{font-size:.75em;padding:5px 30px;border-top:1px solid #ddd;text-align:right}#footer p{margin:0 0 0 1em;display:inline-block}#footer p:last-child{margin-right:30px}h1,h2,h3,h4,h5{font-weight:300}h1{font-size:2.5em;line-height:1.1em}h2{font-size:1.75em;margin:2em 0 .50em 0}h3{font-size:1.4em;margin:1.6em 0 .7em 0}h4{margin:0;font-size:105%}h1:target,h2:target,h3:target,h4:target,h5:target,h6:target{background:var(--highlight-color);padding:.2em 0}a{color:#058;text-decoration:none;transition:color .2s ease-in-out}a:visited{color:#503}a:hover{color:#b62}.title code{font-weight:bold}h2[id^="header-"]{margin-top:2em}.ident{color:#900;font-weight:bold}pre code{font-size:.8em;line-height:1.4em;padding:1em;display:block}code{background:#f3f3f3;font-family:"DejaVu Sans Mono",monospace;padding:1px 4px;overflow-wrap:break-word}h1 code{background:transparent}pre{border-top:1px solid #ccc;border-bottom:1px solid #ccc;margin:1em 0}#http-server-module-list{display:flex;flex-flow:column}#http-server-module-list div{display:flex}#http-server-module-list dt{min-width:10%}#http-server-module-list p{margin-top:0}.toc ul,#index{list-style-type:none;margin:0;padding:0}#index code{background:transparent}#index h3{border-bottom:1px solid #ddd}#index ul{padding:0}#index h4{margin-top:.6em;font-weight:bold}@media (min-width:200ex){#index .two-column{column-count:2}}@media (min-width:300ex){#index .two-column{column-count:3}}dl{margin-bottom:2em}dl dl:last-child{margin-bottom:4em}dd{margin:0 0 1em 3em}#header-classes + dl > dd{margin-bottom:3em}dd dd{margin-left:2em}dd p{margin:10px 0}.name{background:#eee;font-size:.85em;padding:5px 10px;display:inline-block;min-width:40%}.name:hover{background:#e0e0e0}dt:target .name{background:var(--highlight-color)}.name > span:first-child{white-space:nowrap}.name.class > span:nth-child(2){margin-left:.4em}.inherited{color:#999;border-left:5px solid #eee;padding-left:1em}.inheritance em{font-style:normal;font-weight:bold}.desc h2{font-weight:400;font-size:1.25em}.desc h3{font-size:1em}.desc dt code{background:inherit}.source > summary,.git-link-div{color:#666;text-align:right;font-weight:400;font-size:.8em;text-transform:uppercase}.source summary > *{white-space:nowrap;cursor:pointer}.git-link{color:inherit;margin-left:1em}.source pre{max-height:500px;overflow:auto;margin:0}.source pre code{font-size:12px;overflow:visible;min-width:max-content}.hlist{list-style:none}.hlist li{display:inline}.hlist li:after{content:',\2002'}.hlist li:last-child:after{content:none}.hlist .hlist{display:inline;padding-left:1em}img{max-width:100%}td{padding:0 .5em}.admonition{padding:.1em 1em;margin:1em 0}.admonition-title{font-weight:bold}.admonition.note,.admonition.info,.admonition.important{background:#aef}.admonition.todo,.admonition.versionadded,.admonition.tip,.admonition.hint{background:#dfd}.admonition.warning,.admonition.versionchanged,.admonition.deprecated{background:#fd4}.admonition.error,.admonition.danger,.admonition.caution{background:lightpink}</style>
<style media="screen and (min-width: 700px)">@media screen and (min-width:700px){#sidebar{width:30%;height:100vh;overflow:auto;position:sticky;top:0}#content{width:70%;max-width:100ch;padding:3em 4em;border-left:1px solid #ddd}pre code{font-size:1em}.name{font-size:1em}main{display:flex;flex-direction:row-reverse;justify-content:flex-end}.toc ul ul,#index ul ul{padding-left:1em}.toc > ul > li{margin-top:.5em}}</style>
<style media="print">@media print{#sidebar h1{page-break-before:always}.source{display:none}}@media print{*{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a[href]:after{content:" (" attr(href) ")";font-size:90%}a[href][title]:after{content:none}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h1,h2,h3,h4,h5,h6{page-break-after:avoid}}</style>
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js" integrity="sha512-D9gUyxqja7hBtkWpPWGt9wfbfaMGVt9gnyCvYa+jojwwPHLCzUm5i8rpk7vD7wNee9bA35eYIjobYPaQuKS1MQ==" crossorigin></script>
<script>window.addEventListener('DOMContentLoaded', () => {
hljs.configure({languages: ['bash', 'css', 'diff', 'graphql', 'ini', 'javascript', 'json', 'plaintext', 'python', 'python-repl', 'rust', 'shell', 'sql', 'typescript', 'xml', 'yaml']});
hljs.highlightAll();
/* Collapse source docstrings */
setTimeout(() => {
[...document.querySelectorAll('.hljs.language-python > .hljs-string')]
.filter(el => el.innerHTML.length > 200 && ['"""', "'''"].includes(el.innerHTML.substring(0, 3)))
.forEach(el => {
let d = document.createElement('details');
d.classList.add('hljs-string');
d.innerHTML = '<summary>"""</summary>' + el.innerHTML.substring(3);
el.replaceWith(d);
});
}, 100);
})</script>
</head>
<body>
<main>
<article id="content">
<header>
<h1 class="title">Module <code>connpy.tunnels</code></h1>
</header>
<section id="section-intro">
</section>
<section>
</section>
<section>
</section>
<section>
</section>
<section>
<h2 class="section-title" id="header-classes">Classes</h2>
<dl>
<dt id="connpy.tunnels.LocalStream"><code class="flex name class">
<span>class <span class="ident">LocalStream</span></span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">class LocalStream:
&#34;&#34;&#34;
Asynchronous stream wrapper for local stdin/stdout.
Handles terminal raw mode, async I/O, and SIGWINCH signals.
&#34;&#34;&#34;
def __init__(self):
self.stdin_fd = sys.stdin.fileno()
self.stdout_fd = sys.stdout.fileno()
self.original_tty_settings = None
self.resize_callback = None
self._reader_queue = asyncio.Queue()
self._loop = None
def setup(self, resize_callback=None):
self._loop = asyncio.get_running_loop()
self.resize_callback = resize_callback
# Save original terminal settings
try:
self.original_tty_settings = termios.tcgetattr(self.stdin_fd)
tty.setraw(self.stdin_fd)
except termios.error:
# Not a TTY, maybe piped or redirected
pass
# Set stdin non-blocking
flags = fcntl.fcntl(self.stdin_fd, fcntl.F_GETFL)
fcntl.fcntl(self.stdin_fd, fcntl.F_SETFL, flags | os.O_NONBLOCK)
# Setup read callback
self._loop.add_reader(self.stdin_fd, self._read_ready)
# Register SIGWINCH
if resize_callback:
try:
self._loop.add_signal_handler(signal.SIGWINCH, self._handle_winch)
except (NotImplementedError, RuntimeError):
# signal handling not supported on some loops (e.g., Windows Proactor)
pass
def stop_reading(self):
&#34;&#34;&#34;Temporarily stop reading from stdin.&#34;&#34;&#34;
if self._loop and self.stdin_fd is not None:
try:
self._loop.remove_reader(self.stdin_fd)
except Exception:
pass
def start_reading(self):
&#34;&#34;&#34;Resume reading from stdin.&#34;&#34;&#34;
if self._loop and self.stdin_fd is not None:
try:
# Ensure we don&#39;t add it twice
self._loop.remove_reader(self.stdin_fd)
except Exception:
pass
self._loop.add_reader(self.stdin_fd, self._read_ready)
def teardown(self):
if self._loop:
try:
self._loop.remove_reader(self.stdin_fd)
except Exception:
pass
if self.resize_callback:
try:
self._loop.remove_signal_handler(signal.SIGWINCH)
except Exception:
pass
# Restore terminal settings
if self.original_tty_settings is not None:
try:
termios.tcsetattr(self.stdin_fd, termios.TCSADRAIN, self.original_tty_settings)
except termios.error:
pass
# Restore blocking mode for stdin
try:
flags = fcntl.fcntl(self.stdin_fd, fcntl.F_GETFL)
fcntl.fcntl(self.stdin_fd, fcntl.F_SETFL, flags &amp; ~os.O_NONBLOCK)
except Exception:
pass
def _read_ready(self):
try:
# Read whatever is available
data = os.read(self.stdin_fd, 4096)
if data:
self._reader_queue.put_nowait(data)
else:
self._reader_queue.put_nowait(b&#39;&#39;) # EOF
except BlockingIOError:
pass
except OSError:
self._reader_queue.put_nowait(b&#39;&#39;) # EOF on error
async def read(self) -&gt; bytes:
&#34;&#34;&#34;Asynchronously read bytes from stdin.&#34;&#34;&#34;
return await self._reader_queue.get()
async def write(self, data: bytes):
&#34;&#34;&#34;Asynchronously write bytes to stdout.&#34;&#34;&#34;
if not data:
return
try:
os.write(self.stdout_fd, data)
except OSError:
pass
def _handle_winch(self):
if self.resize_callback:
try:
# Use ioctl to get the current window size
s = struct.pack(&#34;HHHH&#34;, 0, 0, 0, 0)
a = fcntl.ioctl(self.stdout_fd, termios.TIOCGWINSZ, s)
rows, cols, _, _ = struct.unpack(&#34;HHHH&#34;, a)
# We schedule the callback safely inside the asyncio loop
# instead of running it raw in the signal handler
self._loop.call_soon(self.resize_callback, rows, cols)
except Exception:
pass</code></pre>
</details>
<div class="desc"><p>Asynchronous stream wrapper for local stdin/stdout.
Handles terminal raw mode, async I/O, and SIGWINCH signals.</p></div>
<h3>Methods</h3>
<dl>
<dt id="connpy.tunnels.LocalStream.read"><code class="name flex">
<span>async def <span class="ident">read</span></span>(<span>self) > bytes</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">async def read(self) -&gt; bytes:
&#34;&#34;&#34;Asynchronously read bytes from stdin.&#34;&#34;&#34;
return await self._reader_queue.get()</code></pre>
</details>
<div class="desc"><p>Asynchronously read bytes from stdin.</p></div>
</dd>
<dt id="connpy.tunnels.LocalStream.setup"><code class="name flex">
<span>def <span class="ident">setup</span></span>(<span>self, resize_callback=None)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def setup(self, resize_callback=None):
self._loop = asyncio.get_running_loop()
self.resize_callback = resize_callback
# Save original terminal settings
try:
self.original_tty_settings = termios.tcgetattr(self.stdin_fd)
tty.setraw(self.stdin_fd)
except termios.error:
# Not a TTY, maybe piped or redirected
pass
# Set stdin non-blocking
flags = fcntl.fcntl(self.stdin_fd, fcntl.F_GETFL)
fcntl.fcntl(self.stdin_fd, fcntl.F_SETFL, flags | os.O_NONBLOCK)
# Setup read callback
self._loop.add_reader(self.stdin_fd, self._read_ready)
# Register SIGWINCH
if resize_callback:
try:
self._loop.add_signal_handler(signal.SIGWINCH, self._handle_winch)
except (NotImplementedError, RuntimeError):
# signal handling not supported on some loops (e.g., Windows Proactor)
pass</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tunnels.LocalStream.start_reading"><code class="name flex">
<span>def <span class="ident">start_reading</span></span>(<span>self)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def start_reading(self):
&#34;&#34;&#34;Resume reading from stdin.&#34;&#34;&#34;
if self._loop and self.stdin_fd is not None:
try:
# Ensure we don&#39;t add it twice
self._loop.remove_reader(self.stdin_fd)
except Exception:
pass
self._loop.add_reader(self.stdin_fd, self._read_ready)</code></pre>
</details>
<div class="desc"><p>Resume reading from stdin.</p></div>
</dd>
<dt id="connpy.tunnels.LocalStream.stop_reading"><code class="name flex">
<span>def <span class="ident">stop_reading</span></span>(<span>self)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def stop_reading(self):
&#34;&#34;&#34;Temporarily stop reading from stdin.&#34;&#34;&#34;
if self._loop and self.stdin_fd is not None:
try:
self._loop.remove_reader(self.stdin_fd)
except Exception:
pass</code></pre>
</details>
<div class="desc"><p>Temporarily stop reading from stdin.</p></div>
</dd>
<dt id="connpy.tunnels.LocalStream.teardown"><code class="name flex">
<span>def <span class="ident">teardown</span></span>(<span>self)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def teardown(self):
if self._loop:
try:
self._loop.remove_reader(self.stdin_fd)
except Exception:
pass
if self.resize_callback:
try:
self._loop.remove_signal_handler(signal.SIGWINCH)
except Exception:
pass
# Restore terminal settings
if self.original_tty_settings is not None:
try:
termios.tcsetattr(self.stdin_fd, termios.TCSADRAIN, self.original_tty_settings)
except termios.error:
pass
# Restore blocking mode for stdin
try:
flags = fcntl.fcntl(self.stdin_fd, fcntl.F_GETFL)
fcntl.fcntl(self.stdin_fd, fcntl.F_SETFL, flags &amp; ~os.O_NONBLOCK)
except Exception:
pass</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tunnels.LocalStream.write"><code class="name flex">
<span>async def <span class="ident">write</span></span>(<span>self, data: bytes)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">async def write(self, data: bytes):
&#34;&#34;&#34;Asynchronously write bytes to stdout.&#34;&#34;&#34;
if not data:
return
try:
os.write(self.stdout_fd, data)
except OSError:
pass</code></pre>
</details>
<div class="desc"><p>Asynchronously write bytes to stdout.</p></div>
</dd>
</dl>
</dd>
<dt id="connpy.tunnels.RemoteStream"><code class="flex name class">
<span>class <span class="ident">RemoteStream</span></span>
<span>(</span><span>request_iterator, response_queue)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">class RemoteStream:
&#34;&#34;&#34;
Asynchronous stream wrapper for gRPC remote connections.
Bridges the blocking gRPC iterators with the async _async_interact_loop.
&#34;&#34;&#34;
def __init__(self, request_iterator, response_queue):
self.request_iterator = request_iterator
self.response_queue = response_queue
self.running = True
self._reader_queue = asyncio.Queue()
self.copilot_queue = asyncio.Queue()
self.resize_callback = None
self._loop = None
self.t = None
def setup(self, resize_callback=None):
self._loop = asyncio.get_running_loop()
self.resize_callback = resize_callback
def read_requests():
try:
for req in self.request_iterator:
if not self.running:
break
if req.cols &gt; 0 and req.rows &gt; 0:
if self.resize_callback:
self._loop.call_soon_threadsafe(self.resize_callback, req.rows, req.cols)
# Copilot dispatching
copilot_msg = {}
if getattr(req, &#34;copilot_question&#34;, &#34;&#34;):
copilot_msg.update({
&#34;question&#34;: req.copilot_question,
&#34;context_buffer&#34;: getattr(req, &#34;copilot_context_buffer&#34;, &#34;&#34;),
&#34;node_info_json&#34;: getattr(req, &#34;copilot_node_info_json&#34;, &#34;&#34;)
})
if getattr(req, &#34;copilot_action&#34;, &#34;&#34;):
copilot_msg[&#34;action&#34;] = req.copilot_action
if copilot_msg:
self._loop.call_soon_threadsafe(self.copilot_queue.put_nowait, copilot_msg)
if req.stdin_data:
self._loop.call_soon_threadsafe(self._reader_queue.put_nowait, req.stdin_data)
except Exception:
pass
finally:
if self._loop and not self._loop.is_closed():
try:
self._loop.call_soon_threadsafe(self._reader_queue.put_nowait, b&#39;&#39;)
except RuntimeError:
pass
self.t = threading.Thread(target=read_requests, daemon=True)
self.t.start()
def teardown(self):
self.running = False
self.response_queue.put(None) # Signal EOF
async def read(self) -&gt; bytes:
&#34;&#34;&#34;Asynchronously read bytes from the gRPC iterator queue.&#34;&#34;&#34;
return await self._reader_queue.get()
async def write(self, data: bytes):
&#34;&#34;&#34;Asynchronously write bytes to the gRPC response queue.&#34;&#34;&#34;
if data:
self.response_queue.put(data)</code></pre>
</details>
<div class="desc"><p>Asynchronous stream wrapper for gRPC remote connections.
Bridges the blocking gRPC iterators with the async _async_interact_loop.</p></div>
<h3>Methods</h3>
<dl>
<dt id="connpy.tunnels.RemoteStream.read"><code class="name flex">
<span>async def <span class="ident">read</span></span>(<span>self) > bytes</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">async def read(self) -&gt; bytes:
&#34;&#34;&#34;Asynchronously read bytes from the gRPC iterator queue.&#34;&#34;&#34;
return await self._reader_queue.get()</code></pre>
</details>
<div class="desc"><p>Asynchronously read bytes from the gRPC iterator queue.</p></div>
</dd>
<dt id="connpy.tunnels.RemoteStream.setup"><code class="name flex">
<span>def <span class="ident">setup</span></span>(<span>self, resize_callback=None)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def setup(self, resize_callback=None):
self._loop = asyncio.get_running_loop()
self.resize_callback = resize_callback
def read_requests():
try:
for req in self.request_iterator:
if not self.running:
break
if req.cols &gt; 0 and req.rows &gt; 0:
if self.resize_callback:
self._loop.call_soon_threadsafe(self.resize_callback, req.rows, req.cols)
# Copilot dispatching
copilot_msg = {}
if getattr(req, &#34;copilot_question&#34;, &#34;&#34;):
copilot_msg.update({
&#34;question&#34;: req.copilot_question,
&#34;context_buffer&#34;: getattr(req, &#34;copilot_context_buffer&#34;, &#34;&#34;),
&#34;node_info_json&#34;: getattr(req, &#34;copilot_node_info_json&#34;, &#34;&#34;)
})
if getattr(req, &#34;copilot_action&#34;, &#34;&#34;):
copilot_msg[&#34;action&#34;] = req.copilot_action
if copilot_msg:
self._loop.call_soon_threadsafe(self.copilot_queue.put_nowait, copilot_msg)
if req.stdin_data:
self._loop.call_soon_threadsafe(self._reader_queue.put_nowait, req.stdin_data)
except Exception:
pass
finally:
if self._loop and not self._loop.is_closed():
try:
self._loop.call_soon_threadsafe(self._reader_queue.put_nowait, b&#39;&#39;)
except RuntimeError:
pass
self.t = threading.Thread(target=read_requests, daemon=True)
self.t.start()</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tunnels.RemoteStream.teardown"><code class="name flex">
<span>def <span class="ident">teardown</span></span>(<span>self)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def teardown(self):
self.running = False
self.response_queue.put(None) # Signal EOF</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tunnels.RemoteStream.write"><code class="name flex">
<span>async def <span class="ident">write</span></span>(<span>self, data: bytes)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">async def write(self, data: bytes):
&#34;&#34;&#34;Asynchronously write bytes to the gRPC response queue.&#34;&#34;&#34;
if data:
self.response_queue.put(data)</code></pre>
</details>
<div class="desc"><p>Asynchronously write bytes to the gRPC response queue.</p></div>
</dd>
</dl>
</dd>
</dl>
</section>
</article>
<nav id="sidebar">
<div class="toc">
<ul></ul>
</div>
<ul id="index">
<li><h3>Super-module</h3>
<ul>
<li><code><a title="connpy" href="index.html">connpy</a></code></li>
</ul>
</li>
<li><h3><a href="#header-classes">Classes</a></h3>
<ul>
<li>
<h4><code><a title="connpy.tunnels.LocalStream" href="#connpy.tunnels.LocalStream">LocalStream</a></code></h4>
<ul class="two-column">
<li><code><a title="connpy.tunnels.LocalStream.read" href="#connpy.tunnels.LocalStream.read">read</a></code></li>
<li><code><a title="connpy.tunnels.LocalStream.setup" href="#connpy.tunnels.LocalStream.setup">setup</a></code></li>
<li><code><a title="connpy.tunnels.LocalStream.start_reading" href="#connpy.tunnels.LocalStream.start_reading">start_reading</a></code></li>
<li><code><a title="connpy.tunnels.LocalStream.stop_reading" href="#connpy.tunnels.LocalStream.stop_reading">stop_reading</a></code></li>
<li><code><a title="connpy.tunnels.LocalStream.teardown" href="#connpy.tunnels.LocalStream.teardown">teardown</a></code></li>
<li><code><a title="connpy.tunnels.LocalStream.write" href="#connpy.tunnels.LocalStream.write">write</a></code></li>
</ul>
</li>
<li>
<h4><code><a title="connpy.tunnels.RemoteStream" href="#connpy.tunnels.RemoteStream">RemoteStream</a></code></h4>
<ul class="">
<li><code><a title="connpy.tunnels.RemoteStream.read" href="#connpy.tunnels.RemoteStream.read">read</a></code></li>
<li><code><a title="connpy.tunnels.RemoteStream.setup" href="#connpy.tunnels.RemoteStream.setup">setup</a></code></li>
<li><code><a title="connpy.tunnels.RemoteStream.teardown" href="#connpy.tunnels.RemoteStream.teardown">teardown</a></code></li>
<li><code><a title="connpy.tunnels.RemoteStream.write" href="#connpy.tunnels.RemoteStream.write">write</a></code></li>
</ul>
</li>
</ul>
</li>
</ul>
</nav>
</main>
<footer id="footer">
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
</footer>
</body>
</html>