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
+13
View File
@@ -2262,6 +2262,8 @@ class configfile:
defaultfile = configdir + '/config.yaml'
self.cachefile = configdir + '/.config.cache.json'
self.fzf_cachefile = configdir + '/.fzf_nodes_cache.txt'
self.folders_cachefile = configdir + '/.folders_cache.txt'
self.profiles_cachefile = configdir + '/.profiles_cache.txt'
defaultkey = configdir + '/.osk'
if conf == None:
self.file = defaultfile
@@ -2307,6 +2309,10 @@ class configfile:
f.close()
self.publickey = self.privatekey.publickey()
# Self-heal text caches if they are missing
if not os.path.exists(self.fzf_cachefile) or not os.path.exists(self.folders_cachefile) or not os.path.exists(self.profiles_cachefile):
self._generate_nodes_cache()
def _loadconfig(self, conf):
#Loads config file using dual cache
@@ -2364,8 +2370,15 @@ class configfile:
def _generate_nodes_cache(self):
try:
nodes = self._getallnodes()
folders = self._getallfolders()
profiles = list(self.profiles.keys())
with open(self.fzf_cachefile, "w") as f:
f.write("\n".join(nodes))
with open(self.folders_cachefile, "w") as f:
f.write("\n".join(folders))
with open(self.profiles_cachefile, "w") as f:
f.write("\n".join(profiles))
except Exception:
pass
+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>