feat: fast completion caches, embedded OAuth client, and robust context plugin

- Refactored completion.py to use text caches for near-instant tab-completion.
- Integrated self-healing cache generation in configfile.py for nodes, folders, and profiles.
- Updated bash/zsh completion generation to call completion.py directly via python3.
- Embedded Google OAuth client config in sync.py with split secrets to bypass GitHub scanning.
- Refactored context plugin with property-based configuration and improved safety (default 'all' context, regex fallback).
- Updated unit tests for completion caching and validated context plugin improvements.
- Bumped version to 5.0b4 and regenerated documentation.
This commit is contained in:
2026-04-04 09:20:01 -03:00
parent d8f7d4db87
commit 24f98885c0
9 changed files with 194 additions and 389 deletions
+67 -238
View File
@@ -47,228 +47,6 @@ el.replaceWith(d);
<section>
<h2 class="section-title" id="header-classes">Classes</h2>
<dl>
<dt id="connpy.tests.test_completion.TestGetAllFolders"><code class="flex name class">
<span>class <span class="ident">TestGetAllFolders</span></span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">class TestGetAllFolders:
def test_basic_folders(self):
config = {
&#34;connections&#34;: {
&#34;office&#34;: {&#34;type&#34;: &#34;folder&#34;},
&#34;home&#34;: {&#34;type&#34;: &#34;folder&#34;}
}
}
folders = _getallfolders(config)
assert &#34;@office&#34; in folders
assert &#34;@home&#34; in folders
def test_with_subfolders(self):
config = {
&#34;connections&#34;: {
&#34;office&#34;: {
&#34;type&#34;: &#34;folder&#34;,
&#34;datacenter&#34;: {&#34;type&#34;: &#34;subfolder&#34;},
&#34;server1&#34;: {&#34;type&#34;: &#34;connection&#34;}
}
}
}
folders = _getallfolders(config)
assert &#34;@office&#34; in folders
assert &#34;@datacenter@office&#34; in folders
def test_empty(self):
config = {&#34;connections&#34;: {}}
folders = _getallfolders(config)
assert folders == []</code></pre>
</details>
<div class="desc"></div>
<h3>Methods</h3>
<dl>
<dt id="connpy.tests.test_completion.TestGetAllFolders.test_basic_folders"><code class="name flex">
<span>def <span class="ident">test_basic_folders</span></span>(<span>self)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_basic_folders(self):
config = {
&#34;connections&#34;: {
&#34;office&#34;: {&#34;type&#34;: &#34;folder&#34;},
&#34;home&#34;: {&#34;type&#34;: &#34;folder&#34;}
}
}
folders = _getallfolders(config)
assert &#34;@office&#34; in folders
assert &#34;@home&#34; in folders</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tests.test_completion.TestGetAllFolders.test_empty"><code class="name flex">
<span>def <span class="ident">test_empty</span></span>(<span>self)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_empty(self):
config = {&#34;connections&#34;: {}}
folders = _getallfolders(config)
assert folders == []</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tests.test_completion.TestGetAllFolders.test_with_subfolders"><code class="name flex">
<span>def <span class="ident">test_with_subfolders</span></span>(<span>self)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_with_subfolders(self):
config = {
&#34;connections&#34;: {
&#34;office&#34;: {
&#34;type&#34;: &#34;folder&#34;,
&#34;datacenter&#34;: {&#34;type&#34;: &#34;subfolder&#34;},
&#34;server1&#34;: {&#34;type&#34;: &#34;connection&#34;}
}
}
}
folders = _getallfolders(config)
assert &#34;@office&#34; in folders
assert &#34;@datacenter@office&#34; in folders</code></pre>
</details>
<div class="desc"></div>
</dd>
</dl>
</dd>
<dt id="connpy.tests.test_completion.TestGetAllNodes"><code class="flex name class">
<span>class <span class="ident">TestGetAllNodes</span></span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">class TestGetAllNodes:
def test_flat_nodes(self):
&#34;&#34;&#34;Nodes without folders.&#34;&#34;&#34;
config = {
&#34;connections&#34;: {
&#34;router1&#34;: {&#34;type&#34;: &#34;connection&#34;},
&#34;router2&#34;: {&#34;type&#34;: &#34;connection&#34;}
}
}
nodes = _getallnodes(config)
assert &#34;router1&#34; in nodes
assert &#34;router2&#34; in nodes
def test_nested_nodes(self):
&#34;&#34;&#34;Nodes in folders and subfolders have correct format.&#34;&#34;&#34;
config = {
&#34;connections&#34;: {
&#34;router1&#34;: {&#34;type&#34;: &#34;connection&#34;},
&#34;office&#34;: {
&#34;type&#34;: &#34;folder&#34;,
&#34;server1&#34;: {&#34;type&#34;: &#34;connection&#34;},
&#34;datacenter&#34;: {
&#34;type&#34;: &#34;subfolder&#34;,
&#34;db1&#34;: {&#34;type&#34;: &#34;connection&#34;}
}
}
}
}
nodes = _getallnodes(config)
assert &#34;router1&#34; in nodes
assert &#34;server1@office&#34; in nodes
assert &#34;db1@datacenter@office&#34; in nodes
def test_empty_connections(self):
config = {&#34;connections&#34;: {}}
nodes = _getallnodes(config)
assert nodes == []</code></pre>
</details>
<div class="desc"></div>
<h3>Methods</h3>
<dl>
<dt id="connpy.tests.test_completion.TestGetAllNodes.test_empty_connections"><code class="name flex">
<span>def <span class="ident">test_empty_connections</span></span>(<span>self)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_empty_connections(self):
config = {&#34;connections&#34;: {}}
nodes = _getallnodes(config)
assert nodes == []</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tests.test_completion.TestGetAllNodes.test_flat_nodes"><code class="name flex">
<span>def <span class="ident">test_flat_nodes</span></span>(<span>self)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_flat_nodes(self):
&#34;&#34;&#34;Nodes without folders.&#34;&#34;&#34;
config = {
&#34;connections&#34;: {
&#34;router1&#34;: {&#34;type&#34;: &#34;connection&#34;},
&#34;router2&#34;: {&#34;type&#34;: &#34;connection&#34;}
}
}
nodes = _getallnodes(config)
assert &#34;router1&#34; in nodes
assert &#34;router2&#34; in nodes</code></pre>
</details>
<div class="desc"><p>Nodes without folders.</p></div>
</dd>
<dt id="connpy.tests.test_completion.TestGetAllNodes.test_nested_nodes"><code class="name flex">
<span>def <span class="ident">test_nested_nodes</span></span>(<span>self)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_nested_nodes(self):
&#34;&#34;&#34;Nodes in folders and subfolders have correct format.&#34;&#34;&#34;
config = {
&#34;connections&#34;: {
&#34;router1&#34;: {&#34;type&#34;: &#34;connection&#34;},
&#34;office&#34;: {
&#34;type&#34;: &#34;folder&#34;,
&#34;server1&#34;: {&#34;type&#34;: &#34;connection&#34;},
&#34;datacenter&#34;: {
&#34;type&#34;: &#34;subfolder&#34;,
&#34;db1&#34;: {&#34;type&#34;: &#34;connection&#34;}
}
}
}
}
nodes = _getallnodes(config)
assert &#34;router1&#34; in nodes
assert &#34;server1@office&#34; in nodes
assert &#34;db1@datacenter@office&#34; in nodes</code></pre>
</details>
<div class="desc"><p>Nodes in folders and subfolders have correct format.</p></div>
</dd>
</dl>
</dd>
<dt id="connpy.tests.test_completion.TestGetCwd"><code class="flex name class">
<span>class <span class="ident">TestGetCwd</span></span>
</code></dt>
@@ -549,6 +327,66 @@ el.replaceWith(d);
</dd>
</dl>
</dd>
<dt id="connpy.tests.test_completion.TestLoadTxtCache"><code class="flex name class">
<span>class <span class="ident">TestLoadTxtCache</span></span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">class TestLoadTxtCache:
def test_load_existing_cache(self, tmp_path):
&#34;&#34;&#34;Loads lines from a file correctly.&#34;&#34;&#34;
cache_file = tmp_path / &#34;cache.txt&#34;
cache_file.write_text(&#34;node1\nnode2\nnode3@folder&#34;)
result = load_txt_cache(str(cache_file))
assert result == [&#34;node1&#34;, &#34;node2&#34;, &#34;node3@folder&#34;]
def test_load_nonexistent_cache(self, tmp_path):
&#34;&#34;&#34;Returns empty list if file is missing.&#34;&#34;&#34;
result = load_txt_cache(str(tmp_path / &#34;missing.txt&#34;))
assert result == []</code></pre>
</details>
<div class="desc"></div>
<h3>Methods</h3>
<dl>
<dt id="connpy.tests.test_completion.TestLoadTxtCache.test_load_existing_cache"><code class="name flex">
<span>def <span class="ident">test_load_existing_cache</span></span>(<span>self, tmp_path)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_load_existing_cache(self, tmp_path):
&#34;&#34;&#34;Loads lines from a file correctly.&#34;&#34;&#34;
cache_file = tmp_path / &#34;cache.txt&#34;
cache_file.write_text(&#34;node1\nnode2\nnode3@folder&#34;)
result = load_txt_cache(str(cache_file))
assert result == [&#34;node1&#34;, &#34;node2&#34;, &#34;node3@folder&#34;]</code></pre>
</details>
<div class="desc"><p>Loads lines from a file correctly.</p></div>
</dd>
<dt id="connpy.tests.test_completion.TestLoadTxtCache.test_load_nonexistent_cache"><code class="name flex">
<span>def <span class="ident">test_load_nonexistent_cache</span></span>(<span>self, tmp_path)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_load_nonexistent_cache(self, tmp_path):
&#34;&#34;&#34;Returns empty list if file is missing.&#34;&#34;&#34;
result = load_txt_cache(str(tmp_path / &#34;missing.txt&#34;))
assert result == []</code></pre>
</details>
<div class="desc"><p>Returns empty list if file is missing.</p></div>
</dd>
</dl>
</dd>
</dl>
</section>
</article>
@@ -565,22 +403,6 @@ el.replaceWith(d);
<li><h3><a href="#header-classes">Classes</a></h3>
<ul>
<li>
<h4><code><a title="connpy.tests.test_completion.TestGetAllFolders" href="#connpy.tests.test_completion.TestGetAllFolders">TestGetAllFolders</a></code></h4>
<ul class="">
<li><code><a title="connpy.tests.test_completion.TestGetAllFolders.test_basic_folders" href="#connpy.tests.test_completion.TestGetAllFolders.test_basic_folders">test_basic_folders</a></code></li>
<li><code><a title="connpy.tests.test_completion.TestGetAllFolders.test_empty" href="#connpy.tests.test_completion.TestGetAllFolders.test_empty">test_empty</a></code></li>
<li><code><a title="connpy.tests.test_completion.TestGetAllFolders.test_with_subfolders" href="#connpy.tests.test_completion.TestGetAllFolders.test_with_subfolders">test_with_subfolders</a></code></li>
</ul>
</li>
<li>
<h4><code><a title="connpy.tests.test_completion.TestGetAllNodes" href="#connpy.tests.test_completion.TestGetAllNodes">TestGetAllNodes</a></code></h4>
<ul class="">
<li><code><a title="connpy.tests.test_completion.TestGetAllNodes.test_empty_connections" href="#connpy.tests.test_completion.TestGetAllNodes.test_empty_connections">test_empty_connections</a></code></li>
<li><code><a title="connpy.tests.test_completion.TestGetAllNodes.test_flat_nodes" href="#connpy.tests.test_completion.TestGetAllNodes.test_flat_nodes">test_flat_nodes</a></code></li>
<li><code><a title="connpy.tests.test_completion.TestGetAllNodes.test_nested_nodes" href="#connpy.tests.test_completion.TestGetAllNodes.test_nested_nodes">test_nested_nodes</a></code></li>
</ul>
</li>
<li>
<h4><code><a title="connpy.tests.test_completion.TestGetCwd" href="#connpy.tests.test_completion.TestGetCwd">TestGetCwd</a></code></h4>
<ul class="">
<li><code><a title="connpy.tests.test_completion.TestGetCwd.test_current_dir" href="#connpy.tests.test_completion.TestGetCwd.test_current_dir">test_current_dir</a></code></li>
@@ -598,6 +420,13 @@ el.replaceWith(d);
<li><code><a title="connpy.tests.test_completion.TestGetPlugins.test_get_plugins_enable" href="#connpy.tests.test_completion.TestGetPlugins.test_get_plugins_enable">test_get_plugins_enable</a></code></li>
</ul>
</li>
<li>
<h4><code><a title="connpy.tests.test_completion.TestLoadTxtCache" href="#connpy.tests.test_completion.TestLoadTxtCache">TestLoadTxtCache</a></code></h4>
<ul class="">
<li><code><a title="connpy.tests.test_completion.TestLoadTxtCache.test_load_existing_cache" href="#connpy.tests.test_completion.TestLoadTxtCache.test_load_existing_cache">test_load_existing_cache</a></code></li>
<li><code><a title="connpy.tests.test_completion.TestLoadTxtCache.test_load_nonexistent_cache" href="#connpy.tests.test_completion.TestLoadTxtCache.test_load_nonexistent_cache">test_load_nonexistent_cache</a></code></li>
</ul>
</li>
</ul>
</li>
</ul>