docs: bump version to 5.0b2 and regenerate documentation

This commit is contained in:
2026-04-03 17:14:16 -03:00
parent cf95befb43
commit 5d8c372f23
12 changed files with 7976 additions and 37 deletions
+1 -1
View File
@@ -1,2 +1,2 @@
__version__ = "5.0b1"
__version__ = "5.0b2"
+51 -36
View File
@@ -3,7 +3,7 @@
<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">
<meta name="generator" content="pdoc3 0.11.6">
<title>connpy API documentation</title>
<meta name="description" content="Connection manager …">
<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>
@@ -551,6 +551,13 @@ class Preload:
</code></pre>
</section>
<section>
<h2 class="section-title" id="header-submodules">Sub-modules</h2>
<dl>
<dt><code class="name"><a title="connpy.tests" href="tests/index.html">connpy.tests</a></code></dt>
<dd>
<div class="desc"></div>
</dd>
</dl>
</section>
<section>
</section>
@@ -623,8 +630,8 @@ class Preload:
if not (isinstance(node.test, ast.Compare) and
isinstance(node.test.left, ast.Name) and
node.test.left.id == &#39;__name__&#39; and
isinstance(node.test.comparators[0], ast.Str) and
node.test.comparators[0].s == &#39;__main__&#39;):
((hasattr(ast, &#39;Str&#39;) and isinstance(node.test.comparators[0], getattr(ast, &#39;Str&#39;)) and node.test.comparators[0].s == &#39;__main__&#39;) or
(hasattr(ast, &#39;Constant&#39;) and isinstance(node.test.comparators[0], getattr(ast, &#39;Constant&#39;)) and node.test.comparators[0].value == &#39;__main__&#39;))):
return &#34;Only __name__ == __main__ If is allowed&#34;
elif not isinstance(node, (ast.FunctionDef, ast.ClassDef, ast.Import, ast.ImportFrom, ast.Pass)):
@@ -758,8 +765,8 @@ class Preload:
if not (isinstance(node.test, ast.Compare) and
isinstance(node.test.left, ast.Name) and
node.test.left.id == &#39;__name__&#39; and
isinstance(node.test.comparators[0], ast.Str) and
node.test.comparators[0].s == &#39;__main__&#39;):
((hasattr(ast, &#39;Str&#39;) and isinstance(node.test.comparators[0], getattr(ast, &#39;Str&#39;)) and node.test.comparators[0].s == &#39;__main__&#39;) or
(hasattr(ast, &#39;Constant&#39;) and isinstance(node.test.comparators[0], getattr(ast, &#39;Constant&#39;)) and node.test.comparators[0].value == &#39;__main__&#39;))):
return &#34;Only __name__ == __main__ If is allowed&#34;
elif not isinstance(node, (ast.FunctionDef, ast.ClassDef, ast.Import, ast.ImportFrom, ast.Pass)):
@@ -1217,7 +1224,7 @@ class ai:
if isinstance(commands, str):
try:
commands = json.loads(commands)
except:
except ValueError:
commands = [c.strip() for c in commands.split(&#39;\n&#39;) if c.strip()]
# Expand multi-line commands within a list (in case the AI packs them)
@@ -1616,9 +1623,10 @@ class ai:
response = completion(model=model, messages=safe_messages, tools=[], api_key=key)
resp_msg = response.choices[0].message
messages.append(resp_msg.model_dump(exclude_none=True))
except:
pass
except Exception as e:
if status:
status.update(f&#34;[bold red]Error fetching summary: {e}[/bold red]&#34;)
printer.warning(f&#34;Failed to fetch final summary from LLM: {e}&#34;)
except KeyboardInterrupt:
if status: status.update(&#34;[bold red]Interrupted! Closing pending tasks...&#34;)
last_msg = messages[-1]
@@ -1631,7 +1639,7 @@ class ai:
response = completion(model=model, messages=safe_messages, tools=tools, api_key=key)
resp_msg = response.choices[0].message
messages.append(resp_msg.model_dump(exclude_none=True))
except: pass
except Exception: pass
finally:
try:
log_dir = self.config.defaultdir
@@ -1641,7 +1649,7 @@ class ai:
if os.path.exists(log_path):
try:
with open(log_path, &#34;r&#34;) as f: hist = json.load(f)
except: hist = []
except (IOError, json.JSONDecodeError): hist = []
hist.append({&#34;timestamp&#34;: datetime.datetime.now().isoformat(), &#34;roles&#34;: {&#34;strategic_engine&#34;: self.architect_model, &#34;execution_engine&#34;: self.engineer_model}, &#34;session&#34;: messages})
with open(log_path, &#34;w&#34;) as f: json.dump(hist[-10:], f, indent=4)
except Exception as e:
@@ -1664,7 +1672,7 @@ class ai:
<dl>
<dt id="connpy.ai.SAFE_COMMANDS"><code class="name">var <span class="ident">SAFE_COMMANDS</span></code></dt>
<dd>
<div class="desc"></div>
<div class="desc"><p>The type of the None singleton.</p></div>
</dd>
</dl>
<h3>Instance variables</h3>
@@ -1925,9 +1933,10 @@ def ask(self, user_input, dryrun=False, chat_history=None, status=None, debug=Fa
response = completion(model=model, messages=safe_messages, tools=[], api_key=key)
resp_msg = response.choices[0].message
messages.append(resp_msg.model_dump(exclude_none=True))
except:
pass
except Exception as e:
if status:
status.update(f&#34;[bold red]Error fetching summary: {e}[/bold red]&#34;)
printer.warning(f&#34;Failed to fetch final summary from LLM: {e}&#34;)
except KeyboardInterrupt:
if status: status.update(&#34;[bold red]Interrupted! Closing pending tasks...&#34;)
last_msg = messages[-1]
@@ -1940,7 +1949,7 @@ def ask(self, user_input, dryrun=False, chat_history=None, status=None, debug=Fa
response = completion(model=model, messages=safe_messages, tools=tools, api_key=key)
resp_msg = response.choices[0].message
messages.append(resp_msg.model_dump(exclude_none=True))
except: pass
except Exception: pass
finally:
try:
log_dir = self.config.defaultdir
@@ -1950,7 +1959,7 @@ def ask(self, user_input, dryrun=False, chat_history=None, status=None, debug=Fa
if os.path.exists(log_path):
try:
with open(log_path, &#34;r&#34;) as f: hist = json.load(f)
except: hist = []
except (IOError, json.JSONDecodeError): hist = []
hist.append({&#34;timestamp&#34;: datetime.datetime.now().isoformat(), &#34;roles&#34;: {&#34;strategic_engine&#34;: self.architect_model, &#34;execution_engine&#34;: self.engineer_model}, &#34;session&#34;: messages})
with open(log_path, &#34;w&#34;) as f: json.dump(hist[-10:], f, indent=4)
except Exception as e:
@@ -2123,7 +2132,7 @@ def confirm(self, user_input): return True</code></pre>
if isinstance(commands, str):
try:
commands = json.loads(commands)
except:
except ValueError:
commands = [c.strip() for c in commands.split(&#39;\n&#39;) if c.strip()]
# Expand multi-line commands within a list (in case the AI packs them)
@@ -2246,7 +2255,7 @@ class configfile:
try:
with open(pathfile, &#34;r&#34;) as f:
configdir = f.read().strip()
except:
except (FileNotFoundError, IOError):
with open(pathfile, &#34;w&#34;) as f:
f.write(str(defaultdir))
configdir = defaultdir
@@ -2306,7 +2315,8 @@ class configfile:
with open(conf, &#34;w&#34;) as f:
json.dump(newconfig, f, indent = 4)
f.close()
except:
except (IOError, OSError) as e:
printer.error(f&#34;Failed to save config: {e}&#34;)
return 1
return 0
@@ -2391,12 +2401,12 @@ class configfile:
if profile:
try:
newfolder[node_name][key] = self.profiles[profile.group(1)][key]
except:
except KeyError:
newfolder[node_name][key] = &#34;&#34;
elif value == &#39;&#39; and key == &#34;protocol&#34;:
try:
newfolder[node_name][key] = self.profiles[&#34;default&#34;][key]
except:
except KeyError:
newfolder[node_name][key] = &#34;ssh&#34;
newfolder = {&#34;{}{}&#34;.format(k,unique):v for k,v in newfolder.items()}
@@ -2417,12 +2427,12 @@ class configfile:
if profile:
try:
newnode[key] = self.profiles[profile.group(1)][key]
except:
except KeyError:
newnode[key] = &#34;&#34;
elif value == &#39;&#39; and key == &#34;protocol&#34;:
try:
newnode[key] = self.profiles[&#34;default&#34;][key]
except:
except KeyError:
newnode[key] = &#34;ssh&#34;
return newnode
@@ -2577,12 +2587,12 @@ class configfile:
if profile:
try:
nodes[node][key] = self.profiles[profile.group(1)][key]
except:
except KeyError:
nodes[node][key] = &#34;&#34;
elif value == &#39;&#39; and key == &#34;protocol&#34;:
try:
nodes[node][key] = self.profiles[&#34;default&#34;][key]
except:
except KeyError:
nodes[node][key] = &#34;ssh&#34;
return nodes
@@ -2780,12 +2790,12 @@ def getitem(self, unique, keys = None, extract = False):
if profile:
try:
newfolder[node_name][key] = self.profiles[profile.group(1)][key]
except:
except KeyError:
newfolder[node_name][key] = &#34;&#34;
elif value == &#39;&#39; and key == &#34;protocol&#34;:
try:
newfolder[node_name][key] = self.profiles[&#34;default&#34;][key]
except:
except KeyError:
newfolder[node_name][key] = &#34;ssh&#34;
newfolder = {&#34;{}{}&#34;.format(k,unique):v for k,v in newfolder.items()}
@@ -2806,12 +2816,12 @@ def getitem(self, unique, keys = None, extract = False):
if profile:
try:
newnode[key] = self.profiles[profile.group(1)][key]
except:
except KeyError:
newnode[key] = &#34;&#34;
elif value == &#39;&#39; and key == &#34;protocol&#34;:
try:
newnode[key] = self.profiles[&#34;default&#34;][key]
except:
except KeyError:
newnode[key] = &#34;ssh&#34;
return newnode</code></pre>
</details>
@@ -2979,12 +2989,12 @@ class node:
if profile and config != &#39;&#39;:
try:
setattr(self,key,config.profiles[profile.group(1)][key])
except:
except KeyError:
setattr(self,key,&#34;&#34;)
elif attr[key] == &#39;&#39; and key == &#34;protocol&#34;:
try:
setattr(self,key,config.profiles[&#34;default&#34;][key])
except:
except (KeyError, AttributeError):
setattr(self,key,&#34;ssh&#34;)
else:
setattr(self,key,attr[key])
@@ -3003,12 +3013,12 @@ class node:
if profile:
try:
self.jumphost[key] = config.profiles[profile.group(1)][key]
except:
except KeyError:
self.jumphost[key] = &#34;&#34;
elif self.jumphost[key] == &#39;&#39; and key == &#34;protocol&#34;:
try:
self.jumphost[key] = config.profiles[&#34;default&#34;][key]
except:
except KeyError:
self.jumphost[key] = &#34;ssh&#34;
if isinstance(self.jumphost[&#34;password&#34;],list):
jumphost_password = []
@@ -3053,7 +3063,7 @@ class node:
try:
decrypted = decryptor.decrypt(ast.literal_eval(passwd)).decode(&#34;utf-8&#34;)
dpass.append(decrypted)
except:
except Exception:
raise ValueError(&#34;Missing or corrupted key&#34;)
return dpass
@@ -4579,6 +4589,11 @@ def test(self, commands, expected, vars = None,*, prompt = None, parallel = 10,
</ul>
</div>
<ul id="index">
<li><h3><a href="#header-submodules">Sub-modules</a></h3>
<ul>
<li><code><a title="connpy.tests" href="tests/index.html">connpy.tests</a></code></li>
</ul>
</li>
<li><h3><a href="#header-classes">Classes</a></h3>
<ul>
<li>
@@ -4631,7 +4646,7 @@ def test(self, commands, expected, vars = None,*, prompt = None, parallel = 10,
</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>
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
</footer>
</body>
</html>
+264
View File
@@ -0,0 +1,264 @@
<!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.6">
<title>connpy.tests.conftest API documentation</title>
<meta name="description" content="Shared fixtures for connpy tests …">
<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.tests.conftest</code></h1>
</header>
<section id="section-intro">
<p>Shared fixtures for connpy tests.</p>
<p>All tests use tmp_path to create isolated config/keys.
No test touches ~/.config/conn/</p>
</section>
<section>
</section>
<section>
</section>
<section>
<h2 class="section-title" id="header-functions">Functions</h2>
<dl>
<dt id="connpy.tests.conftest.ai_config"><code class="name flex">
<span>def <span class="ident">ai_config</span></span>(<span>tmp_config_dir)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.fixture
def ai_config(tmp_config_dir):
&#34;&#34;&#34;Create a configfile with AI keys configured for AI tests.&#34;&#34;&#34;
config_file = tmp_config_dir / &#34;config.json&#34;
data = {
&#34;config&#34;: {
&#34;case&#34;: False, &#34;idletime&#34;: 30, &#34;fzf&#34;: False,
&#34;ai&#34;: {
&#34;engineer_model&#34;: &#34;test/test-model&#34;,
&#34;engineer_api_key&#34;: &#34;test-engineer-key&#34;,
&#34;architect_model&#34;: &#34;test/test-architect&#34;,
&#34;architect_api_key&#34;: &#34;test-architect-key&#34;
}
},
&#34;connections&#34;: SAMPLE_CONNECTIONS,
&#34;profiles&#34;: SAMPLE_PROFILES
}
config_file.write_text(json.dumps(data, indent=4))
from connpy.configfile import configfile
return configfile(conf=str(config_file), key=str(tmp_config_dir / &#34;.osk&#34;))</code></pre>
</details>
<div class="desc"><p>Create a configfile with AI keys configured for AI tests.</p></div>
</dd>
<dt id="connpy.tests.conftest.config"><code class="name flex">
<span>def <span class="ident">config</span></span>(<span>tmp_config_dir)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.fixture
def config(tmp_config_dir):
&#34;&#34;&#34;Create a configfile instance pointing to tmp directory.&#34;&#34;&#34;
from connpy.configfile import configfile
conf_path = str(tmp_config_dir / &#34;config.json&#34;)
key_path = str(tmp_config_dir / &#34;.osk&#34;)
return configfile(conf=conf_path, key=key_path)</code></pre>
</details>
<div class="desc"><p>Create a configfile instance pointing to tmp directory.</p></div>
</dd>
<dt id="connpy.tests.conftest.mock_litellm"><code class="name flex">
<span>def <span class="ident">mock_litellm</span></span>(<span>)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.fixture
def mock_litellm():
&#34;&#34;&#34;Mock litellm.completion for AI tests.&#34;&#34;&#34;
with patch(&#34;connpy.ai.completion&#34;) as mock_comp:
# Create a default response
msg = MagicMock()
msg.content = &#34;Test response from AI&#34;
msg.tool_calls = None
msg.role = &#34;assistant&#34;
msg.model_dump.return_value = {
&#34;role&#34;: &#34;assistant&#34;,
&#34;content&#34;: &#34;Test response from AI&#34;
}
choice = MagicMock()
choice.message = msg
response = MagicMock()
response.choices = [choice]
response.usage = MagicMock()
response.usage.prompt_tokens = 100
response.usage.completion_tokens = 50
response.usage.total_tokens = 150
mock_comp.return_value = response
yield {
&#34;completion&#34;: mock_comp,
&#34;response&#34;: response,
&#34;message&#34;: msg,
&#34;choice&#34;: choice
}</code></pre>
</details>
<div class="desc"><p>Mock litellm.completion for AI tests.</p></div>
</dd>
<dt id="connpy.tests.conftest.mock_pexpect"><code class="name flex">
<span>def <span class="ident">mock_pexpect</span></span>(<span>)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.fixture
def mock_pexpect():
&#34;&#34;&#34;Mock pexpect.spawn for connection tests.&#34;&#34;&#34;
with patch(&#34;connpy.core.pexpect&#34;) as mock_pexp:
child = MagicMock()
child.before = b&#34;&#34;
child.after = b&#34;router#&#34;
child.readline.return_value = b&#34;&#34;
child.child_fd = 3
mock_pexp.spawn.return_value = child
mock_pexp.EOF = object()
mock_pexp.TIMEOUT = object()
# Also mock fdpexpect
with patch(&#34;connpy.core.fdpexpect&#34;, create=True) as mock_fd:
mock_fd.fdspawn.return_value = MagicMock()
yield {
&#34;pexpect&#34;: mock_pexp,
&#34;child&#34;: child,
&#34;fdpexpect&#34;: mock_fd
}</code></pre>
</details>
<div class="desc"><p>Mock pexpect.spawn for connection tests.</p></div>
</dd>
<dt id="connpy.tests.conftest.populated_config"><code class="name flex">
<span>def <span class="ident">populated_config</span></span>(<span>tmp_config_dir)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.fixture
def populated_config(tmp_config_dir):
&#34;&#34;&#34;Create a configfile with sample nodes/profiles pre-loaded.&#34;&#34;&#34;
config_file = tmp_config_dir / &#34;config.json&#34;
data = {
&#34;config&#34;: {&#34;case&#34;: False, &#34;idletime&#34;: 30, &#34;fzf&#34;: False},
&#34;connections&#34;: SAMPLE_CONNECTIONS,
&#34;profiles&#34;: SAMPLE_PROFILES
}
config_file.write_text(json.dumps(data, indent=4))
from connpy.configfile import configfile
return configfile(conf=str(config_file), key=str(tmp_config_dir / &#34;.osk&#34;))</code></pre>
</details>
<div class="desc"><p>Create a configfile with sample nodes/profiles pre-loaded.</p></div>
</dd>
<dt id="connpy.tests.conftest.tmp_config_dir"><code class="name flex">
<span>def <span class="ident">tmp_config_dir</span></span>(<span>tmp_path)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.fixture
def tmp_config_dir(tmp_path):
&#34;&#34;&#34;Create an isolated config directory with config.json and RSA key.&#34;&#34;&#34;
config_dir = tmp_path / &#34;.config&#34; / &#34;conn&#34;
config_dir.mkdir(parents=True)
plugins_dir = config_dir / &#34;plugins&#34;
plugins_dir.mkdir()
# Write config.json
config_file = config_dir / &#34;config.json&#34;
config_file.write_text(json.dumps(DEFAULT_CONFIG, indent=4))
os.chmod(str(config_file), 0o600)
# Write .folder (points to itself)
folder_file = config_dir / &#34;.folder&#34;
folder_file.write_text(str(config_dir))
# Generate RSA key
key = RSA.generate(2048)
key_file = config_dir / &#34;.osk&#34;
key_file.write_bytes(key.export_key(&#34;PEM&#34;))
os.chmod(str(key_file), 0o600)
return config_dir</code></pre>
</details>
<div class="desc"><p>Create an isolated config directory with config.json and RSA key.</p></div>
</dd>
</dl>
</section>
<section>
</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.tests" href="index.html">connpy.tests</a></code></li>
</ul>
</li>
<li><h3><a href="#header-functions">Functions</a></h3>
<ul class="two-column">
<li><code><a title="connpy.tests.conftest.ai_config" href="#connpy.tests.conftest.ai_config">ai_config</a></code></li>
<li><code><a title="connpy.tests.conftest.config" href="#connpy.tests.conftest.config">config</a></code></li>
<li><code><a title="connpy.tests.conftest.mock_litellm" href="#connpy.tests.conftest.mock_litellm">mock_litellm</a></code></li>
<li><code><a title="connpy.tests.conftest.mock_pexpect" href="#connpy.tests.conftest.mock_pexpect">mock_pexpect</a></code></li>
<li><code><a title="connpy.tests.conftest.populated_config" href="#connpy.tests.conftest.populated_config">populated_config</a></code></li>
<li><code><a title="connpy.tests.conftest.tmp_config_dir" href="#connpy.tests.conftest.tmp_config_dir">tmp_config_dir</a></code></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.6</a>.</p>
</footer>
</body>
</html>
+118
View File
@@ -0,0 +1,118 @@
<!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.6">
<title>connpy.tests 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.tests</code></h1>
</header>
<section id="section-intro">
</section>
<section>
<h2 class="section-title" id="header-submodules">Sub-modules</h2>
<dl>
<dt><code class="name"><a title="connpy.tests.conftest" href="conftest.html">connpy.tests.conftest</a></code></dt>
<dd>
<div class="desc"><p>Shared fixtures for connpy tests …</p></div>
</dd>
<dt><code class="name"><a title="connpy.tests.test_ai" href="test_ai.html">connpy.tests.test_ai</a></code></dt>
<dd>
<div class="desc"><p>Tests for connpy.ai module.</p></div>
</dd>
<dt><code class="name"><a title="connpy.tests.test_api" href="test_api.html">connpy.tests.test_api</a></code></dt>
<dd>
<div class="desc"><p>Tests for connpy.api module — Flask routes.</p></div>
</dd>
<dt><code class="name"><a title="connpy.tests.test_completion" href="test_completion.html">connpy.tests.test_completion</a></code></dt>
<dd>
<div class="desc"><p>Tests for connpy.completion module.</p></div>
</dd>
<dt><code class="name"><a title="connpy.tests.test_configfile" href="test_configfile.html">connpy.tests.test_configfile</a></code></dt>
<dd>
<div class="desc"><p>Tests for connpy.configfile module.</p></div>
</dd>
<dt><code class="name"><a title="connpy.tests.test_core" href="test_core.html">connpy.tests.test_core</a></code></dt>
<dd>
<div class="desc"><p>Tests for connpy.core module — node and nodes classes.</p></div>
</dd>
<dt><code class="name"><a title="connpy.tests.test_hooks" href="test_hooks.html">connpy.tests.test_hooks</a></code></dt>
<dd>
<div class="desc"><p>Tests for connpy.hooks module — MethodHook and ClassHook.</p></div>
</dd>
<dt><code class="name"><a title="connpy.tests.test_plugins" href="test_plugins.html">connpy.tests.test_plugins</a></code></dt>
<dd>
<div class="desc"><p>Tests for connpy.plugins module.</p></div>
</dd>
<dt><code class="name"><a title="connpy.tests.test_printer" href="test_printer.html">connpy.tests.test_printer</a></code></dt>
<dd>
<div class="desc"><p>Tests for connpy.printer module.</p></div>
</dd>
</dl>
</section>
<section>
</section>
<section>
</section>
<section>
</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-submodules">Sub-modules</a></h3>
<ul>
<li><code><a title="connpy.tests.conftest" href="conftest.html">connpy.tests.conftest</a></code></li>
<li><code><a title="connpy.tests.test_ai" href="test_ai.html">connpy.tests.test_ai</a></code></li>
<li><code><a title="connpy.tests.test_api" href="test_api.html">connpy.tests.test_api</a></code></li>
<li><code><a title="connpy.tests.test_completion" href="test_completion.html">connpy.tests.test_completion</a></code></li>
<li><code><a title="connpy.tests.test_configfile" href="test_configfile.html">connpy.tests.test_configfile</a></code></li>
<li><code><a title="connpy.tests.test_core" href="test_core.html">connpy.tests.test_core</a></code></li>
<li><code><a title="connpy.tests.test_hooks" href="test_hooks.html">connpy.tests.test_hooks</a></code></li>
<li><code><a title="connpy.tests.test_plugins" href="test_plugins.html">connpy.tests.test_plugins</a></code></li>
<li><code><a title="connpy.tests.test_printer" href="test_printer.html">connpy.tests.test_printer</a></code></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.6</a>.</p>
</footer>
</body>
</html>
File diff suppressed because it is too large Load Diff
+882
View File
@@ -0,0 +1,882 @@
<!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.6">
<title>connpy.tests.test_api API documentation</title>
<meta name="description" content="Tests for connpy.api module — Flask routes.">
<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.tests.test_api</code></h1>
</header>
<section id="section-intro">
<p>Tests for connpy.api module — Flask routes.</p>
</section>
<section>
</section>
<section>
</section>
<section>
<h2 class="section-title" id="header-functions">Functions</h2>
<dl>
<dt id="connpy.tests.test_api.api_client"><code class="name flex">
<span>def <span class="ident">api_client</span></span>(<span>populated_config)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.fixture
def api_client(populated_config):
&#34;&#34;&#34;Create a Flask test client with a populated config.&#34;&#34;&#34;
from connpy.api import app
app.custom_config = populated_config
app.config[&#34;TESTING&#34;] = True
with app.test_client() as client:
yield client</code></pre>
</details>
<div class="desc"><p>Create a Flask test client with a populated config.</p></div>
</dd>
</dl>
</section>
<section>
<h2 class="section-title" id="header-classes">Classes</h2>
<dl>
<dt id="connpy.tests.test_api.TestAskAI"><code class="flex name class">
<span>class <span class="ident">TestAskAI</span></span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">class TestAskAI:
@patch(&#34;connpy.api.myai&#34;)
def test_ask_ai(self, mock_ai_cls, api_client):
mock_instance = MagicMock()
mock_instance.ask.return_value = {&#34;response&#34;: &#34;AI says hello&#34;}
mock_ai_cls.return_value = mock_instance
response = api_client.post(&#34;/ask_ai&#34;, json={
&#34;input&#34;: &#34;list my routers&#34;
})
data = response.get_json()
assert data is not None
@patch(&#34;connpy.api.myai&#34;)
def test_ask_ai_with_dryrun(self, mock_ai_cls, api_client):
mock_instance = MagicMock()
mock_instance.ask.return_value = {&#34;response&#34;: &#34;dry run&#34;}
mock_ai_cls.return_value = mock_instance
response = api_client.post(&#34;/ask_ai&#34;, json={
&#34;input&#34;: &#34;test&#34;,
&#34;dryrun&#34;: True
})
assert response.status_code == 200
@patch(&#34;connpy.api.myai&#34;)
def test_ask_ai_with_history(self, mock_ai_cls, api_client):
mock_instance = MagicMock()
mock_instance.ask.return_value = {&#34;response&#34;: &#34;with history&#34;}
mock_ai_cls.return_value = mock_instance
response = api_client.post(&#34;/ask_ai&#34;, json={
&#34;input&#34;: &#34;follow up&#34;,
&#34;chat_history&#34;: [
{&#34;role&#34;: &#34;user&#34;, &#34;content&#34;: &#34;previous&#34;},
{&#34;role&#34;: &#34;assistant&#34;, &#34;content&#34;: &#34;answer&#34;}
]
})
assert response.status_code == 200</code></pre>
</details>
<div class="desc"></div>
<h3>Methods</h3>
<dl>
<dt id="connpy.tests.test_api.TestAskAI.test_ask_ai"><code class="name flex">
<span>def <span class="ident">test_ask_ai</span></span>(<span>self, mock_ai_cls, api_client)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@patch(&#34;connpy.api.myai&#34;)
def test_ask_ai(self, mock_ai_cls, api_client):
mock_instance = MagicMock()
mock_instance.ask.return_value = {&#34;response&#34;: &#34;AI says hello&#34;}
mock_ai_cls.return_value = mock_instance
response = api_client.post(&#34;/ask_ai&#34;, json={
&#34;input&#34;: &#34;list my routers&#34;
})
data = response.get_json()
assert data is not None</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tests.test_api.TestAskAI.test_ask_ai_with_dryrun"><code class="name flex">
<span>def <span class="ident">test_ask_ai_with_dryrun</span></span>(<span>self, mock_ai_cls, api_client)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@patch(&#34;connpy.api.myai&#34;)
def test_ask_ai_with_dryrun(self, mock_ai_cls, api_client):
mock_instance = MagicMock()
mock_instance.ask.return_value = {&#34;response&#34;: &#34;dry run&#34;}
mock_ai_cls.return_value = mock_instance
response = api_client.post(&#34;/ask_ai&#34;, json={
&#34;input&#34;: &#34;test&#34;,
&#34;dryrun&#34;: True
})
assert response.status_code == 200</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tests.test_api.TestAskAI.test_ask_ai_with_history"><code class="name flex">
<span>def <span class="ident">test_ask_ai_with_history</span></span>(<span>self, mock_ai_cls, api_client)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@patch(&#34;connpy.api.myai&#34;)
def test_ask_ai_with_history(self, mock_ai_cls, api_client):
mock_instance = MagicMock()
mock_instance.ask.return_value = {&#34;response&#34;: &#34;with history&#34;}
mock_ai_cls.return_value = mock_instance
response = api_client.post(&#34;/ask_ai&#34;, json={
&#34;input&#34;: &#34;follow up&#34;,
&#34;chat_history&#34;: [
{&#34;role&#34;: &#34;user&#34;, &#34;content&#34;: &#34;previous&#34;},
{&#34;role&#34;: &#34;assistant&#34;, &#34;content&#34;: &#34;answer&#34;}
]
})
assert response.status_code == 200</code></pre>
</details>
<div class="desc"></div>
</dd>
</dl>
</dd>
<dt id="connpy.tests.test_api.TestConfirm"><code class="flex name class">
<span>class <span class="ident">TestConfirm</span></span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">class TestConfirm:
@patch(&#34;connpy.api.myai&#34;)
def test_confirm(self, mock_ai_cls, api_client):
mock_instance = MagicMock()
mock_instance.confirm.return_value = True
mock_ai_cls.return_value = mock_instance
response = api_client.post(&#34;/confirm&#34;, json={
&#34;input&#34;: &#34;yes&#34;
})
assert response.status_code == 200</code></pre>
</details>
<div class="desc"></div>
<h3>Methods</h3>
<dl>
<dt id="connpy.tests.test_api.TestConfirm.test_confirm"><code class="name flex">
<span>def <span class="ident">test_confirm</span></span>(<span>self, mock_ai_cls, api_client)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@patch(&#34;connpy.api.myai&#34;)
def test_confirm(self, mock_ai_cls, api_client):
mock_instance = MagicMock()
mock_instance.confirm.return_value = True
mock_ai_cls.return_value = mock_instance
response = api_client.post(&#34;/confirm&#34;, json={
&#34;input&#34;: &#34;yes&#34;
})
assert response.status_code == 200</code></pre>
</details>
<div class="desc"></div>
</dd>
</dl>
</dd>
<dt id="connpy.tests.test_api.TestGetNodes"><code class="flex name class">
<span>class <span class="ident">TestGetNodes</span></span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">class TestGetNodes:
def test_get_nodes_no_filter(self, api_client):
response = api_client.post(&#34;/get_nodes&#34;, json={})
data = response.get_json()
assert response.status_code == 200
assert isinstance(data, dict)
assert &#34;router1&#34; in data
def test_get_nodes_with_filter(self, api_client):
response = api_client.post(&#34;/get_nodes&#34;, json={&#34;filter&#34;: &#34;router.*&#34;})
data = response.get_json()
assert &#34;router1&#34; in data
assert &#34;host&#34; in data[&#34;router1&#34;]
def test_get_nodes_has_attributes(self, api_client):
response = api_client.post(&#34;/get_nodes&#34;, json={&#34;filter&#34;: &#34;router1&#34;})
data = response.get_json()
if &#34;router1&#34; in data:
assert &#34;host&#34; in data[&#34;router1&#34;]
assert &#34;protocol&#34; in data[&#34;router1&#34;]</code></pre>
</details>
<div class="desc"></div>
<h3>Methods</h3>
<dl>
<dt id="connpy.tests.test_api.TestGetNodes.test_get_nodes_has_attributes"><code class="name flex">
<span>def <span class="ident">test_get_nodes_has_attributes</span></span>(<span>self, api_client)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_get_nodes_has_attributes(self, api_client):
response = api_client.post(&#34;/get_nodes&#34;, json={&#34;filter&#34;: &#34;router1&#34;})
data = response.get_json()
if &#34;router1&#34; in data:
assert &#34;host&#34; in data[&#34;router1&#34;]
assert &#34;protocol&#34; in data[&#34;router1&#34;]</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tests.test_api.TestGetNodes.test_get_nodes_no_filter"><code class="name flex">
<span>def <span class="ident">test_get_nodes_no_filter</span></span>(<span>self, api_client)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_get_nodes_no_filter(self, api_client):
response = api_client.post(&#34;/get_nodes&#34;, json={})
data = response.get_json()
assert response.status_code == 200
assert isinstance(data, dict)
assert &#34;router1&#34; in data</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tests.test_api.TestGetNodes.test_get_nodes_with_filter"><code class="name flex">
<span>def <span class="ident">test_get_nodes_with_filter</span></span>(<span>self, api_client)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_get_nodes_with_filter(self, api_client):
response = api_client.post(&#34;/get_nodes&#34;, json={&#34;filter&#34;: &#34;router.*&#34;})
data = response.get_json()
assert &#34;router1&#34; in data
assert &#34;host&#34; in data[&#34;router1&#34;]</code></pre>
</details>
<div class="desc"></div>
</dd>
</dl>
</dd>
<dt id="connpy.tests.test_api.TestListNodes"><code class="flex name class">
<span>class <span class="ident">TestListNodes</span></span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">class TestListNodes:
def test_list_nodes_no_filter(self, api_client):
response = api_client.post(&#34;/list_nodes&#34;, json={})
data = response.get_json()
assert response.status_code == 200
assert isinstance(data, list)
assert &#34;router1&#34; in data
def test_list_nodes_with_filter(self, api_client):
response = api_client.post(&#34;/list_nodes&#34;, json={&#34;filter&#34;: &#34;router.*&#34;})
data = response.get_json()
assert &#34;router1&#34; in data
assert all(&#34;router&#34; in n or &#34;Router&#34; in n for n in data)
def test_list_nodes_case_insensitive(self, api_client):
&#34;&#34;&#34;Filter is lowercased when case=false.&#34;&#34;&#34;
response = api_client.post(&#34;/list_nodes&#34;, json={&#34;filter&#34;: &#34;ROUTER.*&#34;})
data = response.get_json()
# Should still match since the filter gets lowercased
assert isinstance(data, list)
def test_list_nodes_no_body(self, api_client):
&#34;&#34;&#34;No body returns all nodes.&#34;&#34;&#34;
response = api_client.post(&#34;/list_nodes&#34;,
data=&#34;&#34;,
content_type=&#34;application/json&#34;)
data = response.get_json()
assert isinstance(data, list)</code></pre>
</details>
<div class="desc"></div>
<h3>Methods</h3>
<dl>
<dt id="connpy.tests.test_api.TestListNodes.test_list_nodes_case_insensitive"><code class="name flex">
<span>def <span class="ident">test_list_nodes_case_insensitive</span></span>(<span>self, api_client)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_list_nodes_case_insensitive(self, api_client):
&#34;&#34;&#34;Filter is lowercased when case=false.&#34;&#34;&#34;
response = api_client.post(&#34;/list_nodes&#34;, json={&#34;filter&#34;: &#34;ROUTER.*&#34;})
data = response.get_json()
# Should still match since the filter gets lowercased
assert isinstance(data, list)</code></pre>
</details>
<div class="desc"><p>Filter is lowercased when case=false.</p></div>
</dd>
<dt id="connpy.tests.test_api.TestListNodes.test_list_nodes_no_body"><code class="name flex">
<span>def <span class="ident">test_list_nodes_no_body</span></span>(<span>self, api_client)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_list_nodes_no_body(self, api_client):
&#34;&#34;&#34;No body returns all nodes.&#34;&#34;&#34;
response = api_client.post(&#34;/list_nodes&#34;,
data=&#34;&#34;,
content_type=&#34;application/json&#34;)
data = response.get_json()
assert isinstance(data, list)</code></pre>
</details>
<div class="desc"><p>No body returns all nodes.</p></div>
</dd>
<dt id="connpy.tests.test_api.TestListNodes.test_list_nodes_no_filter"><code class="name flex">
<span>def <span class="ident">test_list_nodes_no_filter</span></span>(<span>self, api_client)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_list_nodes_no_filter(self, api_client):
response = api_client.post(&#34;/list_nodes&#34;, json={})
data = response.get_json()
assert response.status_code == 200
assert isinstance(data, list)
assert &#34;router1&#34; in data</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tests.test_api.TestListNodes.test_list_nodes_with_filter"><code class="name flex">
<span>def <span class="ident">test_list_nodes_with_filter</span></span>(<span>self, api_client)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_list_nodes_with_filter(self, api_client):
response = api_client.post(&#34;/list_nodes&#34;, json={&#34;filter&#34;: &#34;router.*&#34;})
data = response.get_json()
assert &#34;router1&#34; in data
assert all(&#34;router&#34; in n or &#34;Router&#34; in n for n in data)</code></pre>
</details>
<div class="desc"></div>
</dd>
</dl>
</dd>
<dt id="connpy.tests.test_api.TestRootEndpoint"><code class="flex name class">
<span>class <span class="ident">TestRootEndpoint</span></span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">class TestRootEndpoint:
def test_root_returns_welcome(self, api_client):
response = api_client.get(&#34;/&#34;)
data = response.get_json()
assert response.status_code == 200
assert &#34;Welcome&#34; in data[&#34;message&#34;]
assert &#34;version&#34; in data</code></pre>
</details>
<div class="desc"></div>
<h3>Methods</h3>
<dl>
<dt id="connpy.tests.test_api.TestRootEndpoint.test_root_returns_welcome"><code class="name flex">
<span>def <span class="ident">test_root_returns_welcome</span></span>(<span>self, api_client)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_root_returns_welcome(self, api_client):
response = api_client.get(&#34;/&#34;)
data = response.get_json()
assert response.status_code == 200
assert &#34;Welcome&#34; in data[&#34;message&#34;]
assert &#34;version&#34; in data</code></pre>
</details>
<div class="desc"></div>
</dd>
</dl>
</dd>
<dt id="connpy.tests.test_api.TestRunCommands"><code class="flex name class">
<span>class <span class="ident">TestRunCommands</span></span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">class TestRunCommands:
def test_missing_action(self, api_client):
response = api_client.post(&#34;/run_commands&#34;, json={
&#34;nodes&#34;: &#34;router1&#34;,
&#34;commands&#34;: [&#34;show version&#34;]
})
data = response.get_json()
assert &#34;DataError&#34; in data
assert &#34;action&#34; in data[&#34;DataError&#34;]
def test_missing_nodes(self, api_client):
response = api_client.post(&#34;/run_commands&#34;, json={
&#34;action&#34;: &#34;run&#34;,
&#34;commands&#34;: [&#34;show version&#34;]
})
data = response.get_json()
assert &#34;DataError&#34; in data
assert &#34;nodes&#34; in data[&#34;DataError&#34;]
def test_missing_commands(self, api_client):
response = api_client.post(&#34;/run_commands&#34;, json={
&#34;action&#34;: &#34;run&#34;,
&#34;nodes&#34;: &#34;router1&#34;
})
data = response.get_json()
assert &#34;DataError&#34; in data
assert &#34;commands&#34; in data[&#34;DataError&#34;]
def test_wrong_action(self, api_client):
response = api_client.post(&#34;/run_commands&#34;, json={
&#34;action&#34;: &#34;invalid&#34;,
&#34;nodes&#34;: &#34;router1&#34;,
&#34;commands&#34;: [&#34;show version&#34;]
})
data = response.get_json()
assert &#34;DataError&#34; in data
assert &#34;Wrong action&#34; in data[&#34;DataError&#34;]
@patch(&#34;connpy.api.nodes&#34;)
def test_run_action(self, mock_nodes_cls, api_client):
&#34;&#34;&#34;action=run executes and returns output.&#34;&#34;&#34;
mock_instance = MagicMock()
mock_instance.run.return_value = {&#34;router1&#34;: &#34;Router v1.0&#34;}
mock_nodes_cls.return_value = mock_instance
response = api_client.post(&#34;/run_commands&#34;, json={
&#34;action&#34;: &#34;run&#34;,
&#34;nodes&#34;: &#34;router1&#34;,
&#34;commands&#34;: [&#34;show version&#34;]
})
data = response.get_json()
assert &#34;router1&#34; in data
@patch(&#34;connpy.api.nodes&#34;)
def test_test_action(self, mock_nodes_cls, api_client):
&#34;&#34;&#34;action=test returns result + output.&#34;&#34;&#34;
mock_instance = MagicMock()
mock_instance.test.return_value = {&#34;router1&#34;: {&#34;expected&#34;: True}}
mock_instance.output = {&#34;router1&#34;: &#34;output text&#34;}
mock_nodes_cls.return_value = mock_instance
response = api_client.post(&#34;/run_commands&#34;, json={
&#34;action&#34;: &#34;test&#34;,
&#34;nodes&#34;: &#34;router1&#34;,
&#34;commands&#34;: [&#34;show version&#34;],
&#34;expected&#34;: &#34;Router&#34;
})
data = response.get_json()
assert &#34;result&#34; in data
assert &#34;output&#34; in data
@patch(&#34;connpy.api.nodes&#34;)
def test_run_with_options(self, mock_nodes_cls, api_client):
&#34;&#34;&#34;Options get passed through.&#34;&#34;&#34;
mock_instance = MagicMock()
mock_instance.run.return_value = {&#34;router1&#34;: &#34;ok&#34;}
mock_nodes_cls.return_value = mock_instance
response = api_client.post(&#34;/run_commands&#34;, json={
&#34;action&#34;: &#34;run&#34;,
&#34;nodes&#34;: &#34;router1&#34;,
&#34;commands&#34;: [&#34;show version&#34;],
&#34;options&#34;: {&#34;timeout&#34;: 30, &#34;parallel&#34;: 5}
})
assert response.status_code == 200
@patch(&#34;connpy.api.nodes&#34;)
def test_run_folder_nodes(self, mock_nodes_cls, api_client):
&#34;&#34;&#34;Nodes with @ prefix are resolved as folders.&#34;&#34;&#34;
mock_instance = MagicMock()
mock_instance.run.return_value = {&#34;server1@office&#34;: &#34;ok&#34;}
mock_nodes_cls.return_value = mock_instance
response = api_client.post(&#34;/run_commands&#34;, json={
&#34;action&#34;: &#34;run&#34;,
&#34;nodes&#34;: &#34;@office&#34;,
&#34;commands&#34;: [&#34;ls -la&#34;]
})
assert response.status_code == 200
@patch(&#34;connpy.api.nodes&#34;)
def test_run_list_nodes(self, mock_nodes_cls, api_client):
&#34;&#34;&#34;List of nodes is resolved correctly.&#34;&#34;&#34;
mock_instance = MagicMock()
mock_instance.run.return_value = {&#34;router1&#34;: &#34;ok&#34;, &#34;server1@office&#34;: &#34;ok&#34;}
mock_nodes_cls.return_value = mock_instance
response = api_client.post(&#34;/run_commands&#34;, json={
&#34;action&#34;: &#34;run&#34;,
&#34;nodes&#34;: [&#34;router1&#34;, &#34;server1@office&#34;],
&#34;commands&#34;: [&#34;show version&#34;]
})
assert response.status_code == 200</code></pre>
</details>
<div class="desc"></div>
<h3>Methods</h3>
<dl>
<dt id="connpy.tests.test_api.TestRunCommands.test_missing_action"><code class="name flex">
<span>def <span class="ident">test_missing_action</span></span>(<span>self, api_client)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_missing_action(self, api_client):
response = api_client.post(&#34;/run_commands&#34;, json={
&#34;nodes&#34;: &#34;router1&#34;,
&#34;commands&#34;: [&#34;show version&#34;]
})
data = response.get_json()
assert &#34;DataError&#34; in data
assert &#34;action&#34; in data[&#34;DataError&#34;]</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tests.test_api.TestRunCommands.test_missing_commands"><code class="name flex">
<span>def <span class="ident">test_missing_commands</span></span>(<span>self, api_client)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_missing_commands(self, api_client):
response = api_client.post(&#34;/run_commands&#34;, json={
&#34;action&#34;: &#34;run&#34;,
&#34;nodes&#34;: &#34;router1&#34;
})
data = response.get_json()
assert &#34;DataError&#34; in data
assert &#34;commands&#34; in data[&#34;DataError&#34;]</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tests.test_api.TestRunCommands.test_missing_nodes"><code class="name flex">
<span>def <span class="ident">test_missing_nodes</span></span>(<span>self, api_client)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_missing_nodes(self, api_client):
response = api_client.post(&#34;/run_commands&#34;, json={
&#34;action&#34;: &#34;run&#34;,
&#34;commands&#34;: [&#34;show version&#34;]
})
data = response.get_json()
assert &#34;DataError&#34; in data
assert &#34;nodes&#34; in data[&#34;DataError&#34;]</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tests.test_api.TestRunCommands.test_run_action"><code class="name flex">
<span>def <span class="ident">test_run_action</span></span>(<span>self, mock_nodes_cls, api_client)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@patch(&#34;connpy.api.nodes&#34;)
def test_run_action(self, mock_nodes_cls, api_client):
&#34;&#34;&#34;action=run executes and returns output.&#34;&#34;&#34;
mock_instance = MagicMock()
mock_instance.run.return_value = {&#34;router1&#34;: &#34;Router v1.0&#34;}
mock_nodes_cls.return_value = mock_instance
response = api_client.post(&#34;/run_commands&#34;, json={
&#34;action&#34;: &#34;run&#34;,
&#34;nodes&#34;: &#34;router1&#34;,
&#34;commands&#34;: [&#34;show version&#34;]
})
data = response.get_json()
assert &#34;router1&#34; in data</code></pre>
</details>
<div class="desc"><p>action=run executes and returns output.</p></div>
</dd>
<dt id="connpy.tests.test_api.TestRunCommands.test_run_folder_nodes"><code class="name flex">
<span>def <span class="ident">test_run_folder_nodes</span></span>(<span>self, mock_nodes_cls, api_client)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@patch(&#34;connpy.api.nodes&#34;)
def test_run_folder_nodes(self, mock_nodes_cls, api_client):
&#34;&#34;&#34;Nodes with @ prefix are resolved as folders.&#34;&#34;&#34;
mock_instance = MagicMock()
mock_instance.run.return_value = {&#34;server1@office&#34;: &#34;ok&#34;}
mock_nodes_cls.return_value = mock_instance
response = api_client.post(&#34;/run_commands&#34;, json={
&#34;action&#34;: &#34;run&#34;,
&#34;nodes&#34;: &#34;@office&#34;,
&#34;commands&#34;: [&#34;ls -la&#34;]
})
assert response.status_code == 200</code></pre>
</details>
<div class="desc"><p>Nodes with @ prefix are resolved as folders.</p></div>
</dd>
<dt id="connpy.tests.test_api.TestRunCommands.test_run_list_nodes"><code class="name flex">
<span>def <span class="ident">test_run_list_nodes</span></span>(<span>self, mock_nodes_cls, api_client)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@patch(&#34;connpy.api.nodes&#34;)
def test_run_list_nodes(self, mock_nodes_cls, api_client):
&#34;&#34;&#34;List of nodes is resolved correctly.&#34;&#34;&#34;
mock_instance = MagicMock()
mock_instance.run.return_value = {&#34;router1&#34;: &#34;ok&#34;, &#34;server1@office&#34;: &#34;ok&#34;}
mock_nodes_cls.return_value = mock_instance
response = api_client.post(&#34;/run_commands&#34;, json={
&#34;action&#34;: &#34;run&#34;,
&#34;nodes&#34;: [&#34;router1&#34;, &#34;server1@office&#34;],
&#34;commands&#34;: [&#34;show version&#34;]
})
assert response.status_code == 200</code></pre>
</details>
<div class="desc"><p>List of nodes is resolved correctly.</p></div>
</dd>
<dt id="connpy.tests.test_api.TestRunCommands.test_run_with_options"><code class="name flex">
<span>def <span class="ident">test_run_with_options</span></span>(<span>self, mock_nodes_cls, api_client)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@patch(&#34;connpy.api.nodes&#34;)
def test_run_with_options(self, mock_nodes_cls, api_client):
&#34;&#34;&#34;Options get passed through.&#34;&#34;&#34;
mock_instance = MagicMock()
mock_instance.run.return_value = {&#34;router1&#34;: &#34;ok&#34;}
mock_nodes_cls.return_value = mock_instance
response = api_client.post(&#34;/run_commands&#34;, json={
&#34;action&#34;: &#34;run&#34;,
&#34;nodes&#34;: &#34;router1&#34;,
&#34;commands&#34;: [&#34;show version&#34;],
&#34;options&#34;: {&#34;timeout&#34;: 30, &#34;parallel&#34;: 5}
})
assert response.status_code == 200</code></pre>
</details>
<div class="desc"><p>Options get passed through.</p></div>
</dd>
<dt id="connpy.tests.test_api.TestRunCommands.test_test_action"><code class="name flex">
<span>def <span class="ident">test_test_action</span></span>(<span>self, mock_nodes_cls, api_client)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@patch(&#34;connpy.api.nodes&#34;)
def test_test_action(self, mock_nodes_cls, api_client):
&#34;&#34;&#34;action=test returns result + output.&#34;&#34;&#34;
mock_instance = MagicMock()
mock_instance.test.return_value = {&#34;router1&#34;: {&#34;expected&#34;: True}}
mock_instance.output = {&#34;router1&#34;: &#34;output text&#34;}
mock_nodes_cls.return_value = mock_instance
response = api_client.post(&#34;/run_commands&#34;, json={
&#34;action&#34;: &#34;test&#34;,
&#34;nodes&#34;: &#34;router1&#34;,
&#34;commands&#34;: [&#34;show version&#34;],
&#34;expected&#34;: &#34;Router&#34;
})
data = response.get_json()
assert &#34;result&#34; in data
assert &#34;output&#34; in data</code></pre>
</details>
<div class="desc"><p>action=test returns result + output.</p></div>
</dd>
<dt id="connpy.tests.test_api.TestRunCommands.test_wrong_action"><code class="name flex">
<span>def <span class="ident">test_wrong_action</span></span>(<span>self, api_client)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_wrong_action(self, api_client):
response = api_client.post(&#34;/run_commands&#34;, json={
&#34;action&#34;: &#34;invalid&#34;,
&#34;nodes&#34;: &#34;router1&#34;,
&#34;commands&#34;: [&#34;show version&#34;]
})
data = response.get_json()
assert &#34;DataError&#34; in data
assert &#34;Wrong action&#34; in data[&#34;DataError&#34;]</code></pre>
</details>
<div class="desc"></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.tests" href="index.html">connpy.tests</a></code></li>
</ul>
</li>
<li><h3><a href="#header-functions">Functions</a></h3>
<ul class="">
<li><code><a title="connpy.tests.test_api.api_client" href="#connpy.tests.test_api.api_client">api_client</a></code></li>
</ul>
</li>
<li><h3><a href="#header-classes">Classes</a></h3>
<ul>
<li>
<h4><code><a title="connpy.tests.test_api.TestAskAI" href="#connpy.tests.test_api.TestAskAI">TestAskAI</a></code></h4>
<ul class="">
<li><code><a title="connpy.tests.test_api.TestAskAI.test_ask_ai" href="#connpy.tests.test_api.TestAskAI.test_ask_ai">test_ask_ai</a></code></li>
<li><code><a title="connpy.tests.test_api.TestAskAI.test_ask_ai_with_dryrun" href="#connpy.tests.test_api.TestAskAI.test_ask_ai_with_dryrun">test_ask_ai_with_dryrun</a></code></li>
<li><code><a title="connpy.tests.test_api.TestAskAI.test_ask_ai_with_history" href="#connpy.tests.test_api.TestAskAI.test_ask_ai_with_history">test_ask_ai_with_history</a></code></li>
</ul>
</li>
<li>
<h4><code><a title="connpy.tests.test_api.TestConfirm" href="#connpy.tests.test_api.TestConfirm">TestConfirm</a></code></h4>
<ul class="">
<li><code><a title="connpy.tests.test_api.TestConfirm.test_confirm" href="#connpy.tests.test_api.TestConfirm.test_confirm">test_confirm</a></code></li>
</ul>
</li>
<li>
<h4><code><a title="connpy.tests.test_api.TestGetNodes" href="#connpy.tests.test_api.TestGetNodes">TestGetNodes</a></code></h4>
<ul class="">
<li><code><a title="connpy.tests.test_api.TestGetNodes.test_get_nodes_has_attributes" href="#connpy.tests.test_api.TestGetNodes.test_get_nodes_has_attributes">test_get_nodes_has_attributes</a></code></li>
<li><code><a title="connpy.tests.test_api.TestGetNodes.test_get_nodes_no_filter" href="#connpy.tests.test_api.TestGetNodes.test_get_nodes_no_filter">test_get_nodes_no_filter</a></code></li>
<li><code><a title="connpy.tests.test_api.TestGetNodes.test_get_nodes_with_filter" href="#connpy.tests.test_api.TestGetNodes.test_get_nodes_with_filter">test_get_nodes_with_filter</a></code></li>
</ul>
</li>
<li>
<h4><code><a title="connpy.tests.test_api.TestListNodes" href="#connpy.tests.test_api.TestListNodes">TestListNodes</a></code></h4>
<ul class="">
<li><code><a title="connpy.tests.test_api.TestListNodes.test_list_nodes_case_insensitive" href="#connpy.tests.test_api.TestListNodes.test_list_nodes_case_insensitive">test_list_nodes_case_insensitive</a></code></li>
<li><code><a title="connpy.tests.test_api.TestListNodes.test_list_nodes_no_body" href="#connpy.tests.test_api.TestListNodes.test_list_nodes_no_body">test_list_nodes_no_body</a></code></li>
<li><code><a title="connpy.tests.test_api.TestListNodes.test_list_nodes_no_filter" href="#connpy.tests.test_api.TestListNodes.test_list_nodes_no_filter">test_list_nodes_no_filter</a></code></li>
<li><code><a title="connpy.tests.test_api.TestListNodes.test_list_nodes_with_filter" href="#connpy.tests.test_api.TestListNodes.test_list_nodes_with_filter">test_list_nodes_with_filter</a></code></li>
</ul>
</li>
<li>
<h4><code><a title="connpy.tests.test_api.TestRootEndpoint" href="#connpy.tests.test_api.TestRootEndpoint">TestRootEndpoint</a></code></h4>
<ul class="">
<li><code><a title="connpy.tests.test_api.TestRootEndpoint.test_root_returns_welcome" href="#connpy.tests.test_api.TestRootEndpoint.test_root_returns_welcome">test_root_returns_welcome</a></code></li>
</ul>
</li>
<li>
<h4><code><a title="connpy.tests.test_api.TestRunCommands" href="#connpy.tests.test_api.TestRunCommands">TestRunCommands</a></code></h4>
<ul class="">
<li><code><a title="connpy.tests.test_api.TestRunCommands.test_missing_action" href="#connpy.tests.test_api.TestRunCommands.test_missing_action">test_missing_action</a></code></li>
<li><code><a title="connpy.tests.test_api.TestRunCommands.test_missing_commands" href="#connpy.tests.test_api.TestRunCommands.test_missing_commands">test_missing_commands</a></code></li>
<li><code><a title="connpy.tests.test_api.TestRunCommands.test_missing_nodes" href="#connpy.tests.test_api.TestRunCommands.test_missing_nodes">test_missing_nodes</a></code></li>
<li><code><a title="connpy.tests.test_api.TestRunCommands.test_run_action" href="#connpy.tests.test_api.TestRunCommands.test_run_action">test_run_action</a></code></li>
<li><code><a title="connpy.tests.test_api.TestRunCommands.test_run_folder_nodes" href="#connpy.tests.test_api.TestRunCommands.test_run_folder_nodes">test_run_folder_nodes</a></code></li>
<li><code><a title="connpy.tests.test_api.TestRunCommands.test_run_list_nodes" href="#connpy.tests.test_api.TestRunCommands.test_run_list_nodes">test_run_list_nodes</a></code></li>
<li><code><a title="connpy.tests.test_api.TestRunCommands.test_run_with_options" href="#connpy.tests.test_api.TestRunCommands.test_run_with_options">test_run_with_options</a></code></li>
<li><code><a title="connpy.tests.test_api.TestRunCommands.test_test_action" href="#connpy.tests.test_api.TestRunCommands.test_test_action">test_test_action</a></code></li>
<li><code><a title="connpy.tests.test_api.TestRunCommands.test_wrong_action" href="#connpy.tests.test_api.TestRunCommands.test_wrong_action">test_wrong_action</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.6</a>.</p>
</footer>
</body>
</html>
+610
View File
@@ -0,0 +1,610 @@
<!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.6">
<title>connpy.tests.test_completion API documentation</title>
<meta name="description" content="Tests for connpy.completion module.">
<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.tests.test_completion</code></h1>
</header>
<section id="section-intro">
<p>Tests for connpy.completion module.</p>
</section>
<section>
</section>
<section>
</section>
<section>
</section>
<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>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">class TestGetCwd:
def test_current_dir(self, tmp_path, monkeypatch):
&#34;&#34;&#34;Lists files in current directory.&#34;&#34;&#34;
monkeypatch.chdir(tmp_path)
(tmp_path / &#34;file1.txt&#34;).touch()
(tmp_path / &#34;file2.py&#34;).touch()
subdir = tmp_path / &#34;subdir&#34;
subdir.mkdir()
result = _getcwd([&#34;run&#34;, &#34;run&#34;], &#34;run&#34;)
# Should list files
assert any(&#34;file1.txt&#34; in r for r in result)
assert any(&#34;subdir/&#34; in r for r in result)
def test_specific_path(self, tmp_path, monkeypatch):
&#34;&#34;&#34;Lists files matching a partial path.&#34;&#34;&#34;
monkeypatch.chdir(tmp_path)
(tmp_path / &#34;script.yaml&#34;).touch()
(tmp_path / &#34;script2.yaml&#34;).touch()
result = _getcwd([&#34;run&#34;, &#34;script&#34;], &#34;run&#34;)
assert any(&#34;script&#34; in r for r in result)
def test_folder_only(self, tmp_path, monkeypatch):
&#34;&#34;&#34;folderonly=True returns only directories.&#34;&#34;&#34;
monkeypatch.chdir(tmp_path)
(tmp_path / &#34;file.txt&#34;).touch()
subdir = tmp_path / &#34;mydir&#34;
subdir.mkdir()
result = _getcwd([&#34;export&#34;, &#34;export&#34;], &#34;export&#34;, folderonly=True)
files_in_result = [r for r in result if &#34;file.txt&#34; in r]
assert len(files_in_result) == 0
dirs_in_result = [r for r in result if &#34;mydir&#34; in r]
assert len(dirs_in_result) &gt; 0</code></pre>
</details>
<div class="desc"></div>
<h3>Methods</h3>
<dl>
<dt id="connpy.tests.test_completion.TestGetCwd.test_current_dir"><code class="name flex">
<span>def <span class="ident">test_current_dir</span></span>(<span>self, tmp_path, monkeypatch)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_current_dir(self, tmp_path, monkeypatch):
&#34;&#34;&#34;Lists files in current directory.&#34;&#34;&#34;
monkeypatch.chdir(tmp_path)
(tmp_path / &#34;file1.txt&#34;).touch()
(tmp_path / &#34;file2.py&#34;).touch()
subdir = tmp_path / &#34;subdir&#34;
subdir.mkdir()
result = _getcwd([&#34;run&#34;, &#34;run&#34;], &#34;run&#34;)
# Should list files
assert any(&#34;file1.txt&#34; in r for r in result)
assert any(&#34;subdir/&#34; in r for r in result)</code></pre>
</details>
<div class="desc"><p>Lists files in current directory.</p></div>
</dd>
<dt id="connpy.tests.test_completion.TestGetCwd.test_folder_only"><code class="name flex">
<span>def <span class="ident">test_folder_only</span></span>(<span>self, tmp_path, monkeypatch)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_folder_only(self, tmp_path, monkeypatch):
&#34;&#34;&#34;folderonly=True returns only directories.&#34;&#34;&#34;
monkeypatch.chdir(tmp_path)
(tmp_path / &#34;file.txt&#34;).touch()
subdir = tmp_path / &#34;mydir&#34;
subdir.mkdir()
result = _getcwd([&#34;export&#34;, &#34;export&#34;], &#34;export&#34;, folderonly=True)
files_in_result = [r for r in result if &#34;file.txt&#34; in r]
assert len(files_in_result) == 0
dirs_in_result = [r for r in result if &#34;mydir&#34; in r]
assert len(dirs_in_result) &gt; 0</code></pre>
</details>
<div class="desc"><p>folderonly=True returns only directories.</p></div>
</dd>
<dt id="connpy.tests.test_completion.TestGetCwd.test_specific_path"><code class="name flex">
<span>def <span class="ident">test_specific_path</span></span>(<span>self, tmp_path, monkeypatch)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_specific_path(self, tmp_path, monkeypatch):
&#34;&#34;&#34;Lists files matching a partial path.&#34;&#34;&#34;
monkeypatch.chdir(tmp_path)
(tmp_path / &#34;script.yaml&#34;).touch()
(tmp_path / &#34;script2.yaml&#34;).touch()
result = _getcwd([&#34;run&#34;, &#34;script&#34;], &#34;run&#34;)
assert any(&#34;script&#34; in r for r in result)</code></pre>
</details>
<div class="desc"><p>Lists files matching a partial path.</p></div>
</dd>
</dl>
</dd>
<dt id="connpy.tests.test_completion.TestGetPlugins"><code class="flex name class">
<span>class <span class="ident">TestGetPlugins</span></span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">class TestGetPlugins:
def test_get_plugins_disable(self, tmp_path):
&#34;&#34;&#34;--disable returns enabled plugins.&#34;&#34;&#34;
plugin_dir = tmp_path / &#34;plugins&#34;
plugin_dir.mkdir()
(plugin_dir / &#34;active.py&#34;).touch()
(plugin_dir / &#34;disabled.py.bkp&#34;).touch()
result = _get_plugins(&#34;--disable&#34;, str(tmp_path))
assert &#34;active&#34; in result
assert &#34;disabled&#34; not in result
def test_get_plugins_enable(self, tmp_path):
&#34;&#34;&#34;--enable returns disabled plugins.&#34;&#34;&#34;
plugin_dir = tmp_path / &#34;plugins&#34;
plugin_dir.mkdir()
(plugin_dir / &#34;active.py&#34;).touch()
(plugin_dir / &#34;disabled.py.bkp&#34;).touch()
result = _get_plugins(&#34;--enable&#34;, str(tmp_path))
assert &#34;disabled&#34; in result
assert &#34;active&#34; not in result
def test_get_plugins_del(self, tmp_path):
&#34;&#34;&#34;--del returns all plugins.&#34;&#34;&#34;
plugin_dir = tmp_path / &#34;plugins&#34;
plugin_dir.mkdir()
(plugin_dir / &#34;active.py&#34;).touch()
(plugin_dir / &#34;disabled.py.bkp&#34;).touch()
result = _get_plugins(&#34;--del&#34;, str(tmp_path))
assert &#34;active&#34; in result
assert &#34;disabled&#34; in result
def test_get_plugins_all(self, tmp_path):
&#34;&#34;&#34;&#39;all&#39; returns dict with paths.&#34;&#34;&#34;
plugin_dir = tmp_path / &#34;plugins&#34;
plugin_dir.mkdir()
(plugin_dir / &#34;myplugin.py&#34;).touch()
result = _get_plugins(&#34;all&#34;, str(tmp_path))
assert isinstance(result, dict)
assert &#34;myplugin&#34; in result
def test_get_plugins_empty_dir(self, tmp_path):
&#34;&#34;&#34;Empty plugins directory returns empty list.&#34;&#34;&#34;
plugin_dir = tmp_path / &#34;plugins&#34;
plugin_dir.mkdir()
result = _get_plugins(&#34;--disable&#34;, str(tmp_path))
assert result == []</code></pre>
</details>
<div class="desc"></div>
<h3>Methods</h3>
<dl>
<dt id="connpy.tests.test_completion.TestGetPlugins.test_get_plugins_all"><code class="name flex">
<span>def <span class="ident">test_get_plugins_all</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_get_plugins_all(self, tmp_path):
&#34;&#34;&#34;&#39;all&#39; returns dict with paths.&#34;&#34;&#34;
plugin_dir = tmp_path / &#34;plugins&#34;
plugin_dir.mkdir()
(plugin_dir / &#34;myplugin.py&#34;).touch()
result = _get_plugins(&#34;all&#34;, str(tmp_path))
assert isinstance(result, dict)
assert &#34;myplugin&#34; in result</code></pre>
</details>
<div class="desc"><p>'all' returns dict with paths.</p></div>
</dd>
<dt id="connpy.tests.test_completion.TestGetPlugins.test_get_plugins_del"><code class="name flex">
<span>def <span class="ident">test_get_plugins_del</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_get_plugins_del(self, tmp_path):
&#34;&#34;&#34;--del returns all plugins.&#34;&#34;&#34;
plugin_dir = tmp_path / &#34;plugins&#34;
plugin_dir.mkdir()
(plugin_dir / &#34;active.py&#34;).touch()
(plugin_dir / &#34;disabled.py.bkp&#34;).touch()
result = _get_plugins(&#34;--del&#34;, str(tmp_path))
assert &#34;active&#34; in result
assert &#34;disabled&#34; in result</code></pre>
</details>
<div class="desc"><p>&ndash;del returns all plugins.</p></div>
</dd>
<dt id="connpy.tests.test_completion.TestGetPlugins.test_get_plugins_disable"><code class="name flex">
<span>def <span class="ident">test_get_plugins_disable</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_get_plugins_disable(self, tmp_path):
&#34;&#34;&#34;--disable returns enabled plugins.&#34;&#34;&#34;
plugin_dir = tmp_path / &#34;plugins&#34;
plugin_dir.mkdir()
(plugin_dir / &#34;active.py&#34;).touch()
(plugin_dir / &#34;disabled.py.bkp&#34;).touch()
result = _get_plugins(&#34;--disable&#34;, str(tmp_path))
assert &#34;active&#34; in result
assert &#34;disabled&#34; not in result</code></pre>
</details>
<div class="desc"><p>&ndash;disable returns enabled plugins.</p></div>
</dd>
<dt id="connpy.tests.test_completion.TestGetPlugins.test_get_plugins_empty_dir"><code class="name flex">
<span>def <span class="ident">test_get_plugins_empty_dir</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_get_plugins_empty_dir(self, tmp_path):
&#34;&#34;&#34;Empty plugins directory returns empty list.&#34;&#34;&#34;
plugin_dir = tmp_path / &#34;plugins&#34;
plugin_dir.mkdir()
result = _get_plugins(&#34;--disable&#34;, str(tmp_path))
assert result == []</code></pre>
</details>
<div class="desc"><p>Empty plugins directory returns empty list.</p></div>
</dd>
<dt id="connpy.tests.test_completion.TestGetPlugins.test_get_plugins_enable"><code class="name flex">
<span>def <span class="ident">test_get_plugins_enable</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_get_plugins_enable(self, tmp_path):
&#34;&#34;&#34;--enable returns disabled plugins.&#34;&#34;&#34;
plugin_dir = tmp_path / &#34;plugins&#34;
plugin_dir.mkdir()
(plugin_dir / &#34;active.py&#34;).touch()
(plugin_dir / &#34;disabled.py.bkp&#34;).touch()
result = _get_plugins(&#34;--enable&#34;, str(tmp_path))
assert &#34;disabled&#34; in result
assert &#34;active&#34; not in result</code></pre>
</details>
<div class="desc"><p>&ndash;enable returns disabled plugins.</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.tests" href="index.html">connpy.tests</a></code></li>
</ul>
</li>
<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>
<li><code><a title="connpy.tests.test_completion.TestGetCwd.test_folder_only" href="#connpy.tests.test_completion.TestGetCwd.test_folder_only">test_folder_only</a></code></li>
<li><code><a title="connpy.tests.test_completion.TestGetCwd.test_specific_path" href="#connpy.tests.test_completion.TestGetCwd.test_specific_path">test_specific_path</a></code></li>
</ul>
</li>
<li>
<h4><code><a title="connpy.tests.test_completion.TestGetPlugins" href="#connpy.tests.test_completion.TestGetPlugins">TestGetPlugins</a></code></h4>
<ul class="">
<li><code><a title="connpy.tests.test_completion.TestGetPlugins.test_get_plugins_all" href="#connpy.tests.test_completion.TestGetPlugins.test_get_plugins_all">test_get_plugins_all</a></code></li>
<li><code><a title="connpy.tests.test_completion.TestGetPlugins.test_get_plugins_del" href="#connpy.tests.test_completion.TestGetPlugins.test_get_plugins_del">test_get_plugins_del</a></code></li>
<li><code><a title="connpy.tests.test_completion.TestGetPlugins.test_get_plugins_disable" href="#connpy.tests.test_completion.TestGetPlugins.test_get_plugins_disable">test_get_plugins_disable</a></code></li>
<li><code><a title="connpy.tests.test_completion.TestGetPlugins.test_get_plugins_empty_dir" href="#connpy.tests.test_completion.TestGetPlugins.test_get_plugins_empty_dir">test_get_plugins_empty_dir</a></code></li>
<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>
</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.6</a>.</p>
</footer>
</body>
</html>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+679
View File
@@ -0,0 +1,679 @@
<!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.6">
<title>connpy.tests.test_hooks API documentation</title>
<meta name="description" content="Tests for connpy.hooks module — MethodHook and ClassHook.">
<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.tests.test_hooks</code></h1>
</header>
<section id="section-intro">
<p>Tests for connpy.hooks module — MethodHook and ClassHook.</p>
</section>
<section>
</section>
<section>
</section>
<section>
</section>
<section>
<h2 class="section-title" id="header-classes">Classes</h2>
<dl>
<dt id="connpy.tests.test_hooks.TestClassHook"><code class="flex name class">
<span>class <span class="ident">TestClassHook</span></span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">class TestClassHook:
def test_creates_instance(self):
&#34;&#34;&#34;ClassHook still creates instances normally.&#34;&#34;&#34;
@ClassHook
class MyClass:
def __init__(self, value):
self.value = value
obj = MyClass(42)
assert obj.value == 42
def test_modify_future_instances(self):
&#34;&#34;&#34;modify() affects all future instances.&#34;&#34;&#34;
@ClassHook
class MyClass:
def __init__(self):
self.x = 1
def set_x_to_99(instance):
instance.x = 99
MyClass.modify(set_x_to_99)
obj = MyClass()
assert obj.x == 99
def test_modify_does_not_affect_past(self):
&#34;&#34;&#34;modify() does not affect already-created instances.&#34;&#34;&#34;
@ClassHook
class MyClass:
def __init__(self):
self.x = 1
old_obj = MyClass()
def set_x_to_99(instance):
instance.x = 99
MyClass.modify(set_x_to_99)
assert old_obj.x == 1 # Not affected
assert MyClass().x == 99 # New instance IS affected
def test_instance_modify(self):
&#34;&#34;&#34;instance.modify() only affects that specific instance.&#34;&#34;&#34;
@ClassHook
class MyClass:
def __init__(self):
self.x = 1
obj1 = MyClass()
obj2 = MyClass()
obj1.modify(lambda inst: setattr(inst, &#39;x&#39;, 999))
assert obj1.x == 999
assert obj2.x == 1
def test_multiple_deferred_hooks(self):
&#34;&#34;&#34;Multiple modify() calls apply in order.&#34;&#34;&#34;
@ClassHook
class MyClass:
def __init__(self):
self.log = []
MyClass.modify(lambda inst: inst.log.append(&#34;first&#34;))
MyClass.modify(lambda inst: inst.log.append(&#34;second&#34;))
obj = MyClass()
assert obj.log == [&#34;first&#34;, &#34;second&#34;]
def test_getattr_delegation(self):
&#34;&#34;&#34;ClassHook delegates attribute access to the wrapped class.&#34;&#34;&#34;
@ClassHook
class MyClass:
class_var = &#34;hello&#34;
def __init__(self):
pass
assert MyClass.class_var == &#34;hello&#34;</code></pre>
</details>
<div class="desc"></div>
<h3>Methods</h3>
<dl>
<dt id="connpy.tests.test_hooks.TestClassHook.test_creates_instance"><code class="name flex">
<span>def <span class="ident">test_creates_instance</span></span>(<span>self)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_creates_instance(self):
&#34;&#34;&#34;ClassHook still creates instances normally.&#34;&#34;&#34;
@ClassHook
class MyClass:
def __init__(self, value):
self.value = value
obj = MyClass(42)
assert obj.value == 42</code></pre>
</details>
<div class="desc"><p>ClassHook still creates instances normally.</p></div>
</dd>
<dt id="connpy.tests.test_hooks.TestClassHook.test_getattr_delegation"><code class="name flex">
<span>def <span class="ident">test_getattr_delegation</span></span>(<span>self)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_getattr_delegation(self):
&#34;&#34;&#34;ClassHook delegates attribute access to the wrapped class.&#34;&#34;&#34;
@ClassHook
class MyClass:
class_var = &#34;hello&#34;
def __init__(self):
pass
assert MyClass.class_var == &#34;hello&#34;</code></pre>
</details>
<div class="desc"><p>ClassHook delegates attribute access to the wrapped class.</p></div>
</dd>
<dt id="connpy.tests.test_hooks.TestClassHook.test_instance_modify"><code class="name flex">
<span>def <span class="ident">test_instance_modify</span></span>(<span>self)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_instance_modify(self):
&#34;&#34;&#34;instance.modify() only affects that specific instance.&#34;&#34;&#34;
@ClassHook
class MyClass:
def __init__(self):
self.x = 1
obj1 = MyClass()
obj2 = MyClass()
obj1.modify(lambda inst: setattr(inst, &#39;x&#39;, 999))
assert obj1.x == 999
assert obj2.x == 1</code></pre>
</details>
<div class="desc"><p>instance.modify() only affects that specific instance.</p></div>
</dd>
<dt id="connpy.tests.test_hooks.TestClassHook.test_modify_does_not_affect_past"><code class="name flex">
<span>def <span class="ident">test_modify_does_not_affect_past</span></span>(<span>self)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_modify_does_not_affect_past(self):
&#34;&#34;&#34;modify() does not affect already-created instances.&#34;&#34;&#34;
@ClassHook
class MyClass:
def __init__(self):
self.x = 1
old_obj = MyClass()
def set_x_to_99(instance):
instance.x = 99
MyClass.modify(set_x_to_99)
assert old_obj.x == 1 # Not affected
assert MyClass().x == 99 # New instance IS affected</code></pre>
</details>
<div class="desc"><p>modify() does not affect already-created instances.</p></div>
</dd>
<dt id="connpy.tests.test_hooks.TestClassHook.test_modify_future_instances"><code class="name flex">
<span>def <span class="ident">test_modify_future_instances</span></span>(<span>self)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_modify_future_instances(self):
&#34;&#34;&#34;modify() affects all future instances.&#34;&#34;&#34;
@ClassHook
class MyClass:
def __init__(self):
self.x = 1
def set_x_to_99(instance):
instance.x = 99
MyClass.modify(set_x_to_99)
obj = MyClass()
assert obj.x == 99</code></pre>
</details>
<div class="desc"><p>modify() affects all future instances.</p></div>
</dd>
<dt id="connpy.tests.test_hooks.TestClassHook.test_multiple_deferred_hooks"><code class="name flex">
<span>def <span class="ident">test_multiple_deferred_hooks</span></span>(<span>self)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_multiple_deferred_hooks(self):
&#34;&#34;&#34;Multiple modify() calls apply in order.&#34;&#34;&#34;
@ClassHook
class MyClass:
def __init__(self):
self.log = []
MyClass.modify(lambda inst: inst.log.append(&#34;first&#34;))
MyClass.modify(lambda inst: inst.log.append(&#34;second&#34;))
obj = MyClass()
assert obj.log == [&#34;first&#34;, &#34;second&#34;]</code></pre>
</details>
<div class="desc"><p>Multiple modify() calls apply in order.</p></div>
</dd>
</dl>
</dd>
<dt id="connpy.tests.test_hooks.TestMethodHook"><code class="flex name class">
<span>class <span class="ident">TestMethodHook</span></span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">class TestMethodHook:
def test_basic_call(self):
&#34;&#34;&#34;Decorated function executes normally.&#34;&#34;&#34;
@MethodHook
def add(a, b):
return a + b
assert add(2, 3) == 5
def test_pre_hook_modifies_args(self):
&#34;&#34;&#34;Pre-hook can modify arguments before execution.&#34;&#34;&#34;
@MethodHook
def greet(name):
return f&#34;Hello {name}&#34;
def uppercase_hook(name):
return (name.upper(),), {}
greet.register_pre_hook(uppercase_hook)
assert greet(&#34;world&#34;) == &#34;Hello WORLD&#34;
def test_post_hook_modifies_result(self):
&#34;&#34;&#34;Post-hook can modify the return value.&#34;&#34;&#34;
@MethodHook
def compute(x):
return x * 2
def double_result(*args, **kwargs):
return kwargs[&#34;result&#34;] * 2
compute.register_post_hook(double_result)
assert compute(5) == 20 # 5*2=10, then 10*2=20
def test_multiple_pre_hooks_order(self):
&#34;&#34;&#34;Pre-hooks execute in registration order.&#34;&#34;&#34;
calls = []
@MethodHook
def func(x):
return x
def hook1(x):
calls.append(&#34;hook1&#34;)
return (x,), {}
def hook2(x):
calls.append(&#34;hook2&#34;)
return (x,), {}
func.register_pre_hook(hook1)
func.register_pre_hook(hook2)
func(1)
assert calls == [&#34;hook1&#34;, &#34;hook2&#34;]
def test_multiple_post_hooks_order(self):
&#34;&#34;&#34;Post-hooks execute in registration order.&#34;&#34;&#34;
calls = []
@MethodHook
def func(x):
return x
def hook1(*args, **kwargs):
calls.append(&#34;hook1&#34;)
return kwargs[&#34;result&#34;]
def hook2(*args, **kwargs):
calls.append(&#34;hook2&#34;)
return kwargs[&#34;result&#34;]
func.register_post_hook(hook1)
func.register_post_hook(hook2)
func(1)
assert calls == [&#34;hook1&#34;, &#34;hook2&#34;]
def test_pre_hook_exception_continues(self, capsys):
&#34;&#34;&#34;If a pre-hook raises, the function still executes.&#34;&#34;&#34;
@MethodHook
def func(x):
return x + 1
def bad_hook(x):
raise RuntimeError(&#34;broken hook&#34;)
func.register_pre_hook(bad_hook)
# Should not raise — the hook error is printed but execution continues
result = func(5)
assert result == 6
def test_post_hook_exception_continues(self, capsys):
&#34;&#34;&#34;If a post-hook raises, the result is still returned.&#34;&#34;&#34;
@MethodHook
def func(x):
return x + 1
def bad_hook(*args, **kwargs):
raise RuntimeError(&#34;broken post hook&#34;)
func.register_post_hook(bad_hook)
result = func(5)
assert result == 6
def test_method_hook_as_instance_method(self):
&#34;&#34;&#34;MethodHook works as a descriptor on a class.&#34;&#34;&#34;
class MyClass:
@MethodHook
def double(self, x):
return x * 2
obj = MyClass()
assert obj.double(5) == 10
def test_method_hook_instance_hook_registration(self):
&#34;&#34;&#34;Can register hooks via instance method access.&#34;&#34;&#34;
class MyClass:
@MethodHook
def process(self, x):
return x
def add_ten(*args, **kwargs):
return kwargs[&#34;result&#34;] + 10
obj = MyClass()
obj.process.register_post_hook(add_ten)
assert obj.process(5) == 15</code></pre>
</details>
<div class="desc"></div>
<h3>Methods</h3>
<dl>
<dt id="connpy.tests.test_hooks.TestMethodHook.test_basic_call"><code class="name flex">
<span>def <span class="ident">test_basic_call</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_call(self):
&#34;&#34;&#34;Decorated function executes normally.&#34;&#34;&#34;
@MethodHook
def add(a, b):
return a + b
assert add(2, 3) == 5</code></pre>
</details>
<div class="desc"><p>Decorated function executes normally.</p></div>
</dd>
<dt id="connpy.tests.test_hooks.TestMethodHook.test_method_hook_as_instance_method"><code class="name flex">
<span>def <span class="ident">test_method_hook_as_instance_method</span></span>(<span>self)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_method_hook_as_instance_method(self):
&#34;&#34;&#34;MethodHook works as a descriptor on a class.&#34;&#34;&#34;
class MyClass:
@MethodHook
def double(self, x):
return x * 2
obj = MyClass()
assert obj.double(5) == 10</code></pre>
</details>
<div class="desc"><p>MethodHook works as a descriptor on a class.</p></div>
</dd>
<dt id="connpy.tests.test_hooks.TestMethodHook.test_method_hook_instance_hook_registration"><code class="name flex">
<span>def <span class="ident">test_method_hook_instance_hook_registration</span></span>(<span>self)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_method_hook_instance_hook_registration(self):
&#34;&#34;&#34;Can register hooks via instance method access.&#34;&#34;&#34;
class MyClass:
@MethodHook
def process(self, x):
return x
def add_ten(*args, **kwargs):
return kwargs[&#34;result&#34;] + 10
obj = MyClass()
obj.process.register_post_hook(add_ten)
assert obj.process(5) == 15</code></pre>
</details>
<div class="desc"><p>Can register hooks via instance method access.</p></div>
</dd>
<dt id="connpy.tests.test_hooks.TestMethodHook.test_multiple_post_hooks_order"><code class="name flex">
<span>def <span class="ident">test_multiple_post_hooks_order</span></span>(<span>self)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_multiple_post_hooks_order(self):
&#34;&#34;&#34;Post-hooks execute in registration order.&#34;&#34;&#34;
calls = []
@MethodHook
def func(x):
return x
def hook1(*args, **kwargs):
calls.append(&#34;hook1&#34;)
return kwargs[&#34;result&#34;]
def hook2(*args, **kwargs):
calls.append(&#34;hook2&#34;)
return kwargs[&#34;result&#34;]
func.register_post_hook(hook1)
func.register_post_hook(hook2)
func(1)
assert calls == [&#34;hook1&#34;, &#34;hook2&#34;]</code></pre>
</details>
<div class="desc"><p>Post-hooks execute in registration order.</p></div>
</dd>
<dt id="connpy.tests.test_hooks.TestMethodHook.test_multiple_pre_hooks_order"><code class="name flex">
<span>def <span class="ident">test_multiple_pre_hooks_order</span></span>(<span>self)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_multiple_pre_hooks_order(self):
&#34;&#34;&#34;Pre-hooks execute in registration order.&#34;&#34;&#34;
calls = []
@MethodHook
def func(x):
return x
def hook1(x):
calls.append(&#34;hook1&#34;)
return (x,), {}
def hook2(x):
calls.append(&#34;hook2&#34;)
return (x,), {}
func.register_pre_hook(hook1)
func.register_pre_hook(hook2)
func(1)
assert calls == [&#34;hook1&#34;, &#34;hook2&#34;]</code></pre>
</details>
<div class="desc"><p>Pre-hooks execute in registration order.</p></div>
</dd>
<dt id="connpy.tests.test_hooks.TestMethodHook.test_post_hook_exception_continues"><code class="name flex">
<span>def <span class="ident">test_post_hook_exception_continues</span></span>(<span>self, capsys)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_post_hook_exception_continues(self, capsys):
&#34;&#34;&#34;If a post-hook raises, the result is still returned.&#34;&#34;&#34;
@MethodHook
def func(x):
return x + 1
def bad_hook(*args, **kwargs):
raise RuntimeError(&#34;broken post hook&#34;)
func.register_post_hook(bad_hook)
result = func(5)
assert result == 6</code></pre>
</details>
<div class="desc"><p>If a post-hook raises, the result is still returned.</p></div>
</dd>
<dt id="connpy.tests.test_hooks.TestMethodHook.test_post_hook_modifies_result"><code class="name flex">
<span>def <span class="ident">test_post_hook_modifies_result</span></span>(<span>self)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_post_hook_modifies_result(self):
&#34;&#34;&#34;Post-hook can modify the return value.&#34;&#34;&#34;
@MethodHook
def compute(x):
return x * 2
def double_result(*args, **kwargs):
return kwargs[&#34;result&#34;] * 2
compute.register_post_hook(double_result)
assert compute(5) == 20 # 5*2=10, then 10*2=20</code></pre>
</details>
<div class="desc"><p>Post-hook can modify the return value.</p></div>
</dd>
<dt id="connpy.tests.test_hooks.TestMethodHook.test_pre_hook_exception_continues"><code class="name flex">
<span>def <span class="ident">test_pre_hook_exception_continues</span></span>(<span>self, capsys)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_pre_hook_exception_continues(self, capsys):
&#34;&#34;&#34;If a pre-hook raises, the function still executes.&#34;&#34;&#34;
@MethodHook
def func(x):
return x + 1
def bad_hook(x):
raise RuntimeError(&#34;broken hook&#34;)
func.register_pre_hook(bad_hook)
# Should not raise — the hook error is printed but execution continues
result = func(5)
assert result == 6</code></pre>
</details>
<div class="desc"><p>If a pre-hook raises, the function still executes.</p></div>
</dd>
<dt id="connpy.tests.test_hooks.TestMethodHook.test_pre_hook_modifies_args"><code class="name flex">
<span>def <span class="ident">test_pre_hook_modifies_args</span></span>(<span>self)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_pre_hook_modifies_args(self):
&#34;&#34;&#34;Pre-hook can modify arguments before execution.&#34;&#34;&#34;
@MethodHook
def greet(name):
return f&#34;Hello {name}&#34;
def uppercase_hook(name):
return (name.upper(),), {}
greet.register_pre_hook(uppercase_hook)
assert greet(&#34;world&#34;) == &#34;Hello WORLD&#34;</code></pre>
</details>
<div class="desc"><p>Pre-hook can modify arguments before execution.</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.tests" href="index.html">connpy.tests</a></code></li>
</ul>
</li>
<li><h3><a href="#header-classes">Classes</a></h3>
<ul>
<li>
<h4><code><a title="connpy.tests.test_hooks.TestClassHook" href="#connpy.tests.test_hooks.TestClassHook">TestClassHook</a></code></h4>
<ul class="">
<li><code><a title="connpy.tests.test_hooks.TestClassHook.test_creates_instance" href="#connpy.tests.test_hooks.TestClassHook.test_creates_instance">test_creates_instance</a></code></li>
<li><code><a title="connpy.tests.test_hooks.TestClassHook.test_getattr_delegation" href="#connpy.tests.test_hooks.TestClassHook.test_getattr_delegation">test_getattr_delegation</a></code></li>
<li><code><a title="connpy.tests.test_hooks.TestClassHook.test_instance_modify" href="#connpy.tests.test_hooks.TestClassHook.test_instance_modify">test_instance_modify</a></code></li>
<li><code><a title="connpy.tests.test_hooks.TestClassHook.test_modify_does_not_affect_past" href="#connpy.tests.test_hooks.TestClassHook.test_modify_does_not_affect_past">test_modify_does_not_affect_past</a></code></li>
<li><code><a title="connpy.tests.test_hooks.TestClassHook.test_modify_future_instances" href="#connpy.tests.test_hooks.TestClassHook.test_modify_future_instances">test_modify_future_instances</a></code></li>
<li><code><a title="connpy.tests.test_hooks.TestClassHook.test_multiple_deferred_hooks" href="#connpy.tests.test_hooks.TestClassHook.test_multiple_deferred_hooks">test_multiple_deferred_hooks</a></code></li>
</ul>
</li>
<li>
<h4><code><a title="connpy.tests.test_hooks.TestMethodHook" href="#connpy.tests.test_hooks.TestMethodHook">TestMethodHook</a></code></h4>
<ul class="">
<li><code><a title="connpy.tests.test_hooks.TestMethodHook.test_basic_call" href="#connpy.tests.test_hooks.TestMethodHook.test_basic_call">test_basic_call</a></code></li>
<li><code><a title="connpy.tests.test_hooks.TestMethodHook.test_method_hook_as_instance_method" href="#connpy.tests.test_hooks.TestMethodHook.test_method_hook_as_instance_method">test_method_hook_as_instance_method</a></code></li>
<li><code><a title="connpy.tests.test_hooks.TestMethodHook.test_method_hook_instance_hook_registration" href="#connpy.tests.test_hooks.TestMethodHook.test_method_hook_instance_hook_registration">test_method_hook_instance_hook_registration</a></code></li>
<li><code><a title="connpy.tests.test_hooks.TestMethodHook.test_multiple_post_hooks_order" href="#connpy.tests.test_hooks.TestMethodHook.test_multiple_post_hooks_order">test_multiple_post_hooks_order</a></code></li>
<li><code><a title="connpy.tests.test_hooks.TestMethodHook.test_multiple_pre_hooks_order" href="#connpy.tests.test_hooks.TestMethodHook.test_multiple_pre_hooks_order">test_multiple_pre_hooks_order</a></code></li>
<li><code><a title="connpy.tests.test_hooks.TestMethodHook.test_post_hook_exception_continues" href="#connpy.tests.test_hooks.TestMethodHook.test_post_hook_exception_continues">test_post_hook_exception_continues</a></code></li>
<li><code><a title="connpy.tests.test_hooks.TestMethodHook.test_post_hook_modifies_result" href="#connpy.tests.test_hooks.TestMethodHook.test_post_hook_modifies_result">test_post_hook_modifies_result</a></code></li>
<li><code><a title="connpy.tests.test_hooks.TestMethodHook.test_pre_hook_exception_continues" href="#connpy.tests.test_hooks.TestMethodHook.test_pre_hook_exception_continues">test_pre_hook_exception_continues</a></code></li>
<li><code><a title="connpy.tests.test_hooks.TestMethodHook.test_pre_hook_modifies_args" href="#connpy.tests.test_hooks.TestMethodHook.test_pre_hook_modifies_args">test_pre_hook_modifies_args</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.6</a>.</p>
</footer>
</body>
</html>
+923
View File
@@ -0,0 +1,923 @@
<!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.6">
<title>connpy.tests.test_plugins API documentation</title>
<meta name="description" content="Tests for connpy.plugins module.">
<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.tests.test_plugins</code></h1>
</header>
<section id="section-intro">
<p>Tests for connpy.plugins module.</p>
</section>
<section>
</section>
<section>
</section>
<section>
</section>
<section>
<h2 class="section-title" id="header-classes">Classes</h2>
<dl>
<dt id="connpy.tests.test_plugins.TestPluginLoading"><code class="flex name class">
<span>class <span class="ident">TestPluginLoading</span></span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">class TestPluginLoading:
def test_import_from_path(self, tmp_path):
p = tmp_path / &#34;mymod.py&#34;
_write_plugin(p, &#34;&#34;&#34;\
MY_VAR = 42
&#34;&#34;&#34;)
plugins = Plugins()
module = plugins._import_from_path(str(p))
assert module.MY_VAR == 42
def test_import_plugins_to_argparse(self, tmp_path):
&#34;&#34;&#34;Valid plugins get loaded into argparse.&#34;&#34;&#34;
import argparse
plugin_dir = tmp_path / &#34;plugins&#34;
plugin_dir.mkdir()
_write_plugin(plugin_dir / &#34;myplugin.py&#34;, &#34;&#34;&#34;\
import argparse
class Parser:
def __init__(self):
self.parser = argparse.ArgumentParser(description=&#34;My plugin&#34;)
class Entrypoint:
def __init__(self, args, parser, connapp):
pass
&#34;&#34;&#34;)
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
plugins = Plugins()
plugins._import_plugins_to_argparse(str(plugin_dir), subparsers)
assert &#34;myplugin&#34; in plugins.plugins
assert &#34;myplugin&#34; in plugins.plugin_parsers
def test_plugin_name_collision(self, tmp_path):
&#34;&#34;&#34;Plugin with same name as existing subcommand is skipped.&#34;&#34;&#34;
import argparse
plugin_dir = tmp_path / &#34;plugins&#34;
plugin_dir.mkdir()
_write_plugin(plugin_dir / &#34;existcmd.py&#34;, &#34;&#34;&#34;\
import argparse
class Parser:
def __init__(self):
self.parser = argparse.ArgumentParser()
class Entrypoint:
def __init__(self, args, parser, connapp):
pass
&#34;&#34;&#34;)
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
subparsers.add_parser(&#34;existcmd&#34;) # Already taken
plugins = Plugins()
plugins._import_plugins_to_argparse(str(plugin_dir), subparsers)
assert &#34;existcmd&#34; not in plugins.plugins
def test_preload_registration(self, tmp_path):
&#34;&#34;&#34;Preload class gets registered in preloads dict.&#34;&#34;&#34;
import argparse
plugin_dir = tmp_path / &#34;plugins&#34;
plugin_dir.mkdir()
_write_plugin(plugin_dir / &#34;preloader.py&#34;, &#34;&#34;&#34;\
class Preload:
def __init__(self, connapp):
pass
&#34;&#34;&#34;)
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
plugins = Plugins()
plugins._import_plugins_to_argparse(str(plugin_dir), subparsers)
assert &#34;preloader&#34; in plugins.preloads
def test_invalid_plugin_skipped(self, tmp_path, capsys):
&#34;&#34;&#34;Invalid plugin is skipped with error message.&#34;&#34;&#34;
import argparse
plugin_dir = tmp_path / &#34;plugins&#34;
plugin_dir.mkdir()
_write_plugin(plugin_dir / &#34;badplugin.py&#34;, &#34;&#34;&#34;\
MY_GLOBAL = &#34;bad&#34;
&#34;&#34;&#34;)
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
plugins = Plugins()
plugins._import_plugins_to_argparse(str(plugin_dir), subparsers)
assert &#34;badplugin&#34; not in plugins.plugins
captured = capsys.readouterr()
assert &#34;Failed to load plugin&#34; in captured.err or &#34;Failed to load plugin&#34; in captured.out
def test_empty_directory(self, tmp_path):
&#34;&#34;&#34;Empty directory doesn&#39;t cause errors.&#34;&#34;&#34;
import argparse
plugin_dir = tmp_path / &#34;plugins&#34;
plugin_dir.mkdir()
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
plugins = Plugins()
plugins._import_plugins_to_argparse(str(plugin_dir), subparsers)
assert len(plugins.plugins) == 0</code></pre>
</details>
<div class="desc"></div>
<h3>Methods</h3>
<dl>
<dt id="connpy.tests.test_plugins.TestPluginLoading.test_empty_directory"><code class="name flex">
<span>def <span class="ident">test_empty_directory</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_empty_directory(self, tmp_path):
&#34;&#34;&#34;Empty directory doesn&#39;t cause errors.&#34;&#34;&#34;
import argparse
plugin_dir = tmp_path / &#34;plugins&#34;
plugin_dir.mkdir()
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
plugins = Plugins()
plugins._import_plugins_to_argparse(str(plugin_dir), subparsers)
assert len(plugins.plugins) == 0</code></pre>
</details>
<div class="desc"><p>Empty directory doesn't cause errors.</p></div>
</dd>
<dt id="connpy.tests.test_plugins.TestPluginLoading.test_import_from_path"><code class="name flex">
<span>def <span class="ident">test_import_from_path</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_import_from_path(self, tmp_path):
p = tmp_path / &#34;mymod.py&#34;
_write_plugin(p, &#34;&#34;&#34;\
MY_VAR = 42
&#34;&#34;&#34;)
plugins = Plugins()
module = plugins._import_from_path(str(p))
assert module.MY_VAR == 42</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tests.test_plugins.TestPluginLoading.test_import_plugins_to_argparse"><code class="name flex">
<span>def <span class="ident">test_import_plugins_to_argparse</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_import_plugins_to_argparse(self, tmp_path):
&#34;&#34;&#34;Valid plugins get loaded into argparse.&#34;&#34;&#34;
import argparse
plugin_dir = tmp_path / &#34;plugins&#34;
plugin_dir.mkdir()
_write_plugin(plugin_dir / &#34;myplugin.py&#34;, &#34;&#34;&#34;\
import argparse
class Parser:
def __init__(self):
self.parser = argparse.ArgumentParser(description=&#34;My plugin&#34;)
class Entrypoint:
def __init__(self, args, parser, connapp):
pass
&#34;&#34;&#34;)
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
plugins = Plugins()
plugins._import_plugins_to_argparse(str(plugin_dir), subparsers)
assert &#34;myplugin&#34; in plugins.plugins
assert &#34;myplugin&#34; in plugins.plugin_parsers</code></pre>
</details>
<div class="desc"><p>Valid plugins get loaded into argparse.</p></div>
</dd>
<dt id="connpy.tests.test_plugins.TestPluginLoading.test_invalid_plugin_skipped"><code class="name flex">
<span>def <span class="ident">test_invalid_plugin_skipped</span></span>(<span>self, tmp_path, capsys)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_invalid_plugin_skipped(self, tmp_path, capsys):
&#34;&#34;&#34;Invalid plugin is skipped with error message.&#34;&#34;&#34;
import argparse
plugin_dir = tmp_path / &#34;plugins&#34;
plugin_dir.mkdir()
_write_plugin(plugin_dir / &#34;badplugin.py&#34;, &#34;&#34;&#34;\
MY_GLOBAL = &#34;bad&#34;
&#34;&#34;&#34;)
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
plugins = Plugins()
plugins._import_plugins_to_argparse(str(plugin_dir), subparsers)
assert &#34;badplugin&#34; not in plugins.plugins
captured = capsys.readouterr()
assert &#34;Failed to load plugin&#34; in captured.err or &#34;Failed to load plugin&#34; in captured.out</code></pre>
</details>
<div class="desc"><p>Invalid plugin is skipped with error message.</p></div>
</dd>
<dt id="connpy.tests.test_plugins.TestPluginLoading.test_plugin_name_collision"><code class="name flex">
<span>def <span class="ident">test_plugin_name_collision</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_plugin_name_collision(self, tmp_path):
&#34;&#34;&#34;Plugin with same name as existing subcommand is skipped.&#34;&#34;&#34;
import argparse
plugin_dir = tmp_path / &#34;plugins&#34;
plugin_dir.mkdir()
_write_plugin(plugin_dir / &#34;existcmd.py&#34;, &#34;&#34;&#34;\
import argparse
class Parser:
def __init__(self):
self.parser = argparse.ArgumentParser()
class Entrypoint:
def __init__(self, args, parser, connapp):
pass
&#34;&#34;&#34;)
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
subparsers.add_parser(&#34;existcmd&#34;) # Already taken
plugins = Plugins()
plugins._import_plugins_to_argparse(str(plugin_dir), subparsers)
assert &#34;existcmd&#34; not in plugins.plugins</code></pre>
</details>
<div class="desc"><p>Plugin with same name as existing subcommand is skipped.</p></div>
</dd>
<dt id="connpy.tests.test_plugins.TestPluginLoading.test_preload_registration"><code class="name flex">
<span>def <span class="ident">test_preload_registration</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_preload_registration(self, tmp_path):
&#34;&#34;&#34;Preload class gets registered in preloads dict.&#34;&#34;&#34;
import argparse
plugin_dir = tmp_path / &#34;plugins&#34;
plugin_dir.mkdir()
_write_plugin(plugin_dir / &#34;preloader.py&#34;, &#34;&#34;&#34;\
class Preload:
def __init__(self, connapp):
pass
&#34;&#34;&#34;)
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
plugins = Plugins()
plugins._import_plugins_to_argparse(str(plugin_dir), subparsers)
assert &#34;preloader&#34; in plugins.preloads</code></pre>
</details>
<div class="desc"><p>Preload class gets registered in preloads dict.</p></div>
</dd>
</dl>
</dd>
<dt id="connpy.tests.test_plugins.TestVerifyScript"><code class="flex name class">
<span>class <span class="ident">TestVerifyScript</span></span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">class TestVerifyScript:
def test_valid_parser_entrypoint(self, tmp_path):
p = tmp_path / &#34;good.py&#34;
_write_plugin(p, &#34;&#34;&#34;\
import argparse
class Parser:
def __init__(self):
self.parser = argparse.ArgumentParser()
class Entrypoint:
def __init__(self, args, parser, connapp):
pass
&#34;&#34;&#34;)
plugins = Plugins()
assert plugins.verify_script(str(p)) == False
def test_valid_preload_only(self, tmp_path):
p = tmp_path / &#34;preload.py&#34;
_write_plugin(p, &#34;&#34;&#34;\
class Preload:
def __init__(self, connapp):
pass
&#34;&#34;&#34;)
plugins = Plugins()
assert plugins.verify_script(str(p)) == False
def test_valid_all_three(self, tmp_path):
p = tmp_path / &#34;all.py&#34;
_write_plugin(p, &#34;&#34;&#34;\
import argparse
class Parser:
def __init__(self):
self.parser = argparse.ArgumentParser()
class Entrypoint:
def __init__(self, args, parser, connapp):
pass
class Preload:
def __init__(self, connapp):
pass
&#34;&#34;&#34;)
plugins = Plugins()
assert plugins.verify_script(str(p)) == False
def test_parser_without_entrypoint(self, tmp_path):
p = tmp_path / &#34;bad.py&#34;
_write_plugin(p, &#34;&#34;&#34;\
import argparse
class Parser:
def __init__(self):
self.parser = argparse.ArgumentParser()
&#34;&#34;&#34;)
plugins = Plugins()
result = plugins.verify_script(str(p))
assert result # Should be a truthy error string
assert &#34;Entrypoint&#34; in result
def test_entrypoint_without_parser(self, tmp_path):
p = tmp_path / &#34;bad.py&#34;
_write_plugin(p, &#34;&#34;&#34;\
class Entrypoint:
def __init__(self, args, parser, connapp):
pass
&#34;&#34;&#34;)
plugins = Plugins()
result = plugins.verify_script(str(p))
assert result
assert &#34;Parser&#34; in result
def test_no_valid_class(self, tmp_path):
p = tmp_path / &#34;empty.py&#34;
_write_plugin(p, &#34;&#34;&#34;\
def some_function():
pass
&#34;&#34;&#34;)
plugins = Plugins()
result = plugins.verify_script(str(p))
assert result
assert &#34;No valid class&#34; in result
def test_parser_missing_self_parser(self, tmp_path):
p = tmp_path / &#34;bad.py&#34;
_write_plugin(p, &#34;&#34;&#34;\
class Parser:
def __init__(self):
self.something = &#34;not parser&#34;
class Entrypoint:
def __init__(self, args, parser, connapp):
pass
&#34;&#34;&#34;)
plugins = Plugins()
result = plugins.verify_script(str(p))
assert result
assert &#34;self.parser&#34; in result
def test_entrypoint_wrong_args(self, tmp_path):
p = tmp_path / &#34;bad.py&#34;
_write_plugin(p, &#34;&#34;&#34;\
import argparse
class Parser:
def __init__(self):
self.parser = argparse.ArgumentParser()
class Entrypoint:
def __init__(self, args):
pass
&#34;&#34;&#34;)
plugins = Plugins()
result = plugins.verify_script(str(p))
assert result
assert &#34;Entrypoint&#34; in result
def test_preload_wrong_args(self, tmp_path):
p = tmp_path / &#34;bad.py&#34;
_write_plugin(p, &#34;&#34;&#34;\
class Preload:
def __init__(self, connapp, extra):
pass
&#34;&#34;&#34;)
plugins = Plugins()
result = plugins.verify_script(str(p))
assert result
assert &#34;Preload&#34; in result
def test_disallowed_top_level(self, tmp_path):
p = tmp_path / &#34;bad.py&#34;
_write_plugin(p, &#34;&#34;&#34;\
MY_GLOBAL = &#34;not allowed&#34;
class Preload:
def __init__(self, connapp):
pass
&#34;&#34;&#34;)
plugins = Plugins()
result = plugins.verify_script(str(p))
assert result
assert &#34;not allowed&#34; in result.lower() or &#34;Plugin can only have&#34; in result
def test_syntax_error(self, tmp_path):
p = tmp_path / &#34;bad.py&#34;
_write_plugin(p, &#34;&#34;&#34;\
def broken(
&#34;&#34;&#34;)
plugins = Plugins()
result = plugins.verify_script(str(p))
assert result
assert &#34;Syntax error&#34; in result
def test_if_name_main_allowed(self, tmp_path):
p = tmp_path / &#34;good.py&#34;
_write_plugin(p, &#34;&#34;&#34;\
class Preload:
def __init__(self, connapp):
pass
if __name__ == &#34;__main__&#34;:
print(&#34;standalone&#34;)
&#34;&#34;&#34;)
plugins = Plugins()
assert plugins.verify_script(str(p)) == False
def test_other_if_not_allowed(self, tmp_path):
p = tmp_path / &#34;bad.py&#34;
_write_plugin(p, &#34;&#34;&#34;\
import sys
if sys.platform == &#34;linux&#34;:
pass
class Preload:
def __init__(self, connapp):
pass
&#34;&#34;&#34;)
plugins = Plugins()
result = plugins.verify_script(str(p))
assert result
assert &#34;__name__&#34; in result</code></pre>
</details>
<div class="desc"></div>
<h3>Methods</h3>
<dl>
<dt id="connpy.tests.test_plugins.TestVerifyScript.test_disallowed_top_level"><code class="name flex">
<span>def <span class="ident">test_disallowed_top_level</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_disallowed_top_level(self, tmp_path):
p = tmp_path / &#34;bad.py&#34;
_write_plugin(p, &#34;&#34;&#34;\
MY_GLOBAL = &#34;not allowed&#34;
class Preload:
def __init__(self, connapp):
pass
&#34;&#34;&#34;)
plugins = Plugins()
result = plugins.verify_script(str(p))
assert result
assert &#34;not allowed&#34; in result.lower() or &#34;Plugin can only have&#34; in result</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tests.test_plugins.TestVerifyScript.test_entrypoint_without_parser"><code class="name flex">
<span>def <span class="ident">test_entrypoint_without_parser</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_entrypoint_without_parser(self, tmp_path):
p = tmp_path / &#34;bad.py&#34;
_write_plugin(p, &#34;&#34;&#34;\
class Entrypoint:
def __init__(self, args, parser, connapp):
pass
&#34;&#34;&#34;)
plugins = Plugins()
result = plugins.verify_script(str(p))
assert result
assert &#34;Parser&#34; in result</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tests.test_plugins.TestVerifyScript.test_entrypoint_wrong_args"><code class="name flex">
<span>def <span class="ident">test_entrypoint_wrong_args</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_entrypoint_wrong_args(self, tmp_path):
p = tmp_path / &#34;bad.py&#34;
_write_plugin(p, &#34;&#34;&#34;\
import argparse
class Parser:
def __init__(self):
self.parser = argparse.ArgumentParser()
class Entrypoint:
def __init__(self, args):
pass
&#34;&#34;&#34;)
plugins = Plugins()
result = plugins.verify_script(str(p))
assert result
assert &#34;Entrypoint&#34; in result</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tests.test_plugins.TestVerifyScript.test_if_name_main_allowed"><code class="name flex">
<span>def <span class="ident">test_if_name_main_allowed</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_if_name_main_allowed(self, tmp_path):
p = tmp_path / &#34;good.py&#34;
_write_plugin(p, &#34;&#34;&#34;\
class Preload:
def __init__(self, connapp):
pass
if __name__ == &#34;__main__&#34;:
print(&#34;standalone&#34;)
&#34;&#34;&#34;)
plugins = Plugins()
assert plugins.verify_script(str(p)) == False</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tests.test_plugins.TestVerifyScript.test_no_valid_class"><code class="name flex">
<span>def <span class="ident">test_no_valid_class</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_no_valid_class(self, tmp_path):
p = tmp_path / &#34;empty.py&#34;
_write_plugin(p, &#34;&#34;&#34;\
def some_function():
pass
&#34;&#34;&#34;)
plugins = Plugins()
result = plugins.verify_script(str(p))
assert result
assert &#34;No valid class&#34; in result</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tests.test_plugins.TestVerifyScript.test_other_if_not_allowed"><code class="name flex">
<span>def <span class="ident">test_other_if_not_allowed</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_other_if_not_allowed(self, tmp_path):
p = tmp_path / &#34;bad.py&#34;
_write_plugin(p, &#34;&#34;&#34;\
import sys
if sys.platform == &#34;linux&#34;:
pass
class Preload:
def __init__(self, connapp):
pass
&#34;&#34;&#34;)
plugins = Plugins()
result = plugins.verify_script(str(p))
assert result
assert &#34;__name__&#34; in result</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tests.test_plugins.TestVerifyScript.test_parser_missing_self_parser"><code class="name flex">
<span>def <span class="ident">test_parser_missing_self_parser</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_parser_missing_self_parser(self, tmp_path):
p = tmp_path / &#34;bad.py&#34;
_write_plugin(p, &#34;&#34;&#34;\
class Parser:
def __init__(self):
self.something = &#34;not parser&#34;
class Entrypoint:
def __init__(self, args, parser, connapp):
pass
&#34;&#34;&#34;)
plugins = Plugins()
result = plugins.verify_script(str(p))
assert result
assert &#34;self.parser&#34; in result</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tests.test_plugins.TestVerifyScript.test_parser_without_entrypoint"><code class="name flex">
<span>def <span class="ident">test_parser_without_entrypoint</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_parser_without_entrypoint(self, tmp_path):
p = tmp_path / &#34;bad.py&#34;
_write_plugin(p, &#34;&#34;&#34;\
import argparse
class Parser:
def __init__(self):
self.parser = argparse.ArgumentParser()
&#34;&#34;&#34;)
plugins = Plugins()
result = plugins.verify_script(str(p))
assert result # Should be a truthy error string
assert &#34;Entrypoint&#34; in result</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tests.test_plugins.TestVerifyScript.test_preload_wrong_args"><code class="name flex">
<span>def <span class="ident">test_preload_wrong_args</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_preload_wrong_args(self, tmp_path):
p = tmp_path / &#34;bad.py&#34;
_write_plugin(p, &#34;&#34;&#34;\
class Preload:
def __init__(self, connapp, extra):
pass
&#34;&#34;&#34;)
plugins = Plugins()
result = plugins.verify_script(str(p))
assert result
assert &#34;Preload&#34; in result</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tests.test_plugins.TestVerifyScript.test_syntax_error"><code class="name flex">
<span>def <span class="ident">test_syntax_error</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_syntax_error(self, tmp_path):
p = tmp_path / &#34;bad.py&#34;
_write_plugin(p, &#34;&#34;&#34;\
def broken(
&#34;&#34;&#34;)
plugins = Plugins()
result = plugins.verify_script(str(p))
assert result
assert &#34;Syntax error&#34; in result</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tests.test_plugins.TestVerifyScript.test_valid_all_three"><code class="name flex">
<span>def <span class="ident">test_valid_all_three</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_valid_all_three(self, tmp_path):
p = tmp_path / &#34;all.py&#34;
_write_plugin(p, &#34;&#34;&#34;\
import argparse
class Parser:
def __init__(self):
self.parser = argparse.ArgumentParser()
class Entrypoint:
def __init__(self, args, parser, connapp):
pass
class Preload:
def __init__(self, connapp):
pass
&#34;&#34;&#34;)
plugins = Plugins()
assert plugins.verify_script(str(p)) == False</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tests.test_plugins.TestVerifyScript.test_valid_parser_entrypoint"><code class="name flex">
<span>def <span class="ident">test_valid_parser_entrypoint</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_valid_parser_entrypoint(self, tmp_path):
p = tmp_path / &#34;good.py&#34;
_write_plugin(p, &#34;&#34;&#34;\
import argparse
class Parser:
def __init__(self):
self.parser = argparse.ArgumentParser()
class Entrypoint:
def __init__(self, args, parser, connapp):
pass
&#34;&#34;&#34;)
plugins = Plugins()
assert plugins.verify_script(str(p)) == False</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tests.test_plugins.TestVerifyScript.test_valid_preload_only"><code class="name flex">
<span>def <span class="ident">test_valid_preload_only</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_valid_preload_only(self, tmp_path):
p = tmp_path / &#34;preload.py&#34;
_write_plugin(p, &#34;&#34;&#34;\
class Preload:
def __init__(self, connapp):
pass
&#34;&#34;&#34;)
plugins = Plugins()
assert plugins.verify_script(str(p)) == False</code></pre>
</details>
<div class="desc"></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.tests" href="index.html">connpy.tests</a></code></li>
</ul>
</li>
<li><h3><a href="#header-classes">Classes</a></h3>
<ul>
<li>
<h4><code><a title="connpy.tests.test_plugins.TestPluginLoading" href="#connpy.tests.test_plugins.TestPluginLoading">TestPluginLoading</a></code></h4>
<ul class="">
<li><code><a title="connpy.tests.test_plugins.TestPluginLoading.test_empty_directory" href="#connpy.tests.test_plugins.TestPluginLoading.test_empty_directory">test_empty_directory</a></code></li>
<li><code><a title="connpy.tests.test_plugins.TestPluginLoading.test_import_from_path" href="#connpy.tests.test_plugins.TestPluginLoading.test_import_from_path">test_import_from_path</a></code></li>
<li><code><a title="connpy.tests.test_plugins.TestPluginLoading.test_import_plugins_to_argparse" href="#connpy.tests.test_plugins.TestPluginLoading.test_import_plugins_to_argparse">test_import_plugins_to_argparse</a></code></li>
<li><code><a title="connpy.tests.test_plugins.TestPluginLoading.test_invalid_plugin_skipped" href="#connpy.tests.test_plugins.TestPluginLoading.test_invalid_plugin_skipped">test_invalid_plugin_skipped</a></code></li>
<li><code><a title="connpy.tests.test_plugins.TestPluginLoading.test_plugin_name_collision" href="#connpy.tests.test_plugins.TestPluginLoading.test_plugin_name_collision">test_plugin_name_collision</a></code></li>
<li><code><a title="connpy.tests.test_plugins.TestPluginLoading.test_preload_registration" href="#connpy.tests.test_plugins.TestPluginLoading.test_preload_registration">test_preload_registration</a></code></li>
</ul>
</li>
<li>
<h4><code><a title="connpy.tests.test_plugins.TestVerifyScript" href="#connpy.tests.test_plugins.TestVerifyScript">TestVerifyScript</a></code></h4>
<ul class="">
<li><code><a title="connpy.tests.test_plugins.TestVerifyScript.test_disallowed_top_level" href="#connpy.tests.test_plugins.TestVerifyScript.test_disallowed_top_level">test_disallowed_top_level</a></code></li>
<li><code><a title="connpy.tests.test_plugins.TestVerifyScript.test_entrypoint_without_parser" href="#connpy.tests.test_plugins.TestVerifyScript.test_entrypoint_without_parser">test_entrypoint_without_parser</a></code></li>
<li><code><a title="connpy.tests.test_plugins.TestVerifyScript.test_entrypoint_wrong_args" href="#connpy.tests.test_plugins.TestVerifyScript.test_entrypoint_wrong_args">test_entrypoint_wrong_args</a></code></li>
<li><code><a title="connpy.tests.test_plugins.TestVerifyScript.test_if_name_main_allowed" href="#connpy.tests.test_plugins.TestVerifyScript.test_if_name_main_allowed">test_if_name_main_allowed</a></code></li>
<li><code><a title="connpy.tests.test_plugins.TestVerifyScript.test_no_valid_class" href="#connpy.tests.test_plugins.TestVerifyScript.test_no_valid_class">test_no_valid_class</a></code></li>
<li><code><a title="connpy.tests.test_plugins.TestVerifyScript.test_other_if_not_allowed" href="#connpy.tests.test_plugins.TestVerifyScript.test_other_if_not_allowed">test_other_if_not_allowed</a></code></li>
<li><code><a title="connpy.tests.test_plugins.TestVerifyScript.test_parser_missing_self_parser" href="#connpy.tests.test_plugins.TestVerifyScript.test_parser_missing_self_parser">test_parser_missing_self_parser</a></code></li>
<li><code><a title="connpy.tests.test_plugins.TestVerifyScript.test_parser_without_entrypoint" href="#connpy.tests.test_plugins.TestVerifyScript.test_parser_without_entrypoint">test_parser_without_entrypoint</a></code></li>
<li><code><a title="connpy.tests.test_plugins.TestVerifyScript.test_preload_wrong_args" href="#connpy.tests.test_plugins.TestVerifyScript.test_preload_wrong_args">test_preload_wrong_args</a></code></li>
<li><code><a title="connpy.tests.test_plugins.TestVerifyScript.test_syntax_error" href="#connpy.tests.test_plugins.TestVerifyScript.test_syntax_error">test_syntax_error</a></code></li>
<li><code><a title="connpy.tests.test_plugins.TestVerifyScript.test_valid_all_three" href="#connpy.tests.test_plugins.TestVerifyScript.test_valid_all_three">test_valid_all_three</a></code></li>
<li><code><a title="connpy.tests.test_plugins.TestVerifyScript.test_valid_parser_entrypoint" href="#connpy.tests.test_plugins.TestVerifyScript.test_valid_parser_entrypoint">test_valid_parser_entrypoint</a></code></li>
<li><code><a title="connpy.tests.test_plugins.TestVerifyScript.test_valid_preload_only" href="#connpy.tests.test_plugins.TestVerifyScript.test_valid_preload_only">test_valid_preload_only</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.6</a>.</p>
</footer>
</body>
</html>
+269
View File
@@ -0,0 +1,269 @@
<!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.6">
<title>connpy.tests.test_printer API documentation</title>
<meta name="description" content="Tests for connpy.printer module.">
<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.tests.test_printer</code></h1>
</header>
<section id="section-intro">
<p>Tests for connpy.printer module.</p>
</section>
<section>
</section>
<section>
</section>
<section>
</section>
<section>
<h2 class="section-title" id="header-classes">Classes</h2>
<dl>
<dt id="connpy.tests.test_printer.TestPrinter"><code class="flex name class">
<span>class <span class="ident">TestPrinter</span></span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">class TestPrinter:
def test_info_output(self, capsys):
printer.info(&#34;hello world&#34;)
captured = capsys.readouterr()
assert &#34;[i] hello world&#34; in captured.out
def test_success_output(self, capsys):
printer.success(&#34;done&#34;)
captured = capsys.readouterr()
assert &#34;[✓] done&#34; in captured.out
def test_warning_output(self, capsys):
printer.warning(&#34;careful&#34;)
captured = capsys.readouterr()
assert &#34;[!] careful&#34; in captured.out
def test_error_output(self, capsys):
printer.error(&#34;failed&#34;)
captured = capsys.readouterr()
assert &#34;[✗] failed&#34; in captured.err
def test_debug_output(self, capsys):
printer.debug(&#34;debug info&#34;)
captured = capsys.readouterr()
assert &#34;[d] debug info&#34; in captured.out
def test_start_output(self, capsys):
printer.start(&#34;starting&#34;)
captured = capsys.readouterr()
assert &#34;[+] starting&#34; in captured.out
def test_custom_output(self, capsys):
printer.custom(&#34;TAG&#34;, &#34;custom message&#34;)
captured = capsys.readouterr()
assert &#34;[TAG] custom message&#34; in captured.out
def test_multiline_indentation(self, capsys):
printer.info(&#34;line1\nline2\nline3&#34;)
captured = capsys.readouterr()
lines = captured.out.strip().split(&#34;\n&#34;)
assert lines[0] == &#34;[i] line1&#34;
# Second line should be indented by len(&#34;[i] &#34;) = 4 chars
assert lines[1].startswith(&#34; line2&#34;)
assert lines[2].startswith(&#34; line3&#34;)</code></pre>
</details>
<div class="desc"></div>
<h3>Methods</h3>
<dl>
<dt id="connpy.tests.test_printer.TestPrinter.test_custom_output"><code class="name flex">
<span>def <span class="ident">test_custom_output</span></span>(<span>self, capsys)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_custom_output(self, capsys):
printer.custom(&#34;TAG&#34;, &#34;custom message&#34;)
captured = capsys.readouterr()
assert &#34;[TAG] custom message&#34; in captured.out</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tests.test_printer.TestPrinter.test_debug_output"><code class="name flex">
<span>def <span class="ident">test_debug_output</span></span>(<span>self, capsys)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_debug_output(self, capsys):
printer.debug(&#34;debug info&#34;)
captured = capsys.readouterr()
assert &#34;[d] debug info&#34; in captured.out</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tests.test_printer.TestPrinter.test_error_output"><code class="name flex">
<span>def <span class="ident">test_error_output</span></span>(<span>self, capsys)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_error_output(self, capsys):
printer.error(&#34;failed&#34;)
captured = capsys.readouterr()
assert &#34;[✗] failed&#34; in captured.err</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tests.test_printer.TestPrinter.test_info_output"><code class="name flex">
<span>def <span class="ident">test_info_output</span></span>(<span>self, capsys)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_info_output(self, capsys):
printer.info(&#34;hello world&#34;)
captured = capsys.readouterr()
assert &#34;[i] hello world&#34; in captured.out</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tests.test_printer.TestPrinter.test_multiline_indentation"><code class="name flex">
<span>def <span class="ident">test_multiline_indentation</span></span>(<span>self, capsys)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_multiline_indentation(self, capsys):
printer.info(&#34;line1\nline2\nline3&#34;)
captured = capsys.readouterr()
lines = captured.out.strip().split(&#34;\n&#34;)
assert lines[0] == &#34;[i] line1&#34;
# Second line should be indented by len(&#34;[i] &#34;) = 4 chars
assert lines[1].startswith(&#34; line2&#34;)
assert lines[2].startswith(&#34; line3&#34;)</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tests.test_printer.TestPrinter.test_start_output"><code class="name flex">
<span>def <span class="ident">test_start_output</span></span>(<span>self, capsys)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_start_output(self, capsys):
printer.start(&#34;starting&#34;)
captured = capsys.readouterr()
assert &#34;[+] starting&#34; in captured.out</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tests.test_printer.TestPrinter.test_success_output"><code class="name flex">
<span>def <span class="ident">test_success_output</span></span>(<span>self, capsys)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_success_output(self, capsys):
printer.success(&#34;done&#34;)
captured = capsys.readouterr()
assert &#34;[✓] done&#34; in captured.out</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="connpy.tests.test_printer.TestPrinter.test_warning_output"><code class="name flex">
<span>def <span class="ident">test_warning_output</span></span>(<span>self, capsys)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def test_warning_output(self, capsys):
printer.warning(&#34;careful&#34;)
captured = capsys.readouterr()
assert &#34;[!] careful&#34; in captured.out</code></pre>
</details>
<div class="desc"></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.tests" href="index.html">connpy.tests</a></code></li>
</ul>
</li>
<li><h3><a href="#header-classes">Classes</a></h3>
<ul>
<li>
<h4><code><a title="connpy.tests.test_printer.TestPrinter" href="#connpy.tests.test_printer.TestPrinter">TestPrinter</a></code></h4>
<ul class="">
<li><code><a title="connpy.tests.test_printer.TestPrinter.test_custom_output" href="#connpy.tests.test_printer.TestPrinter.test_custom_output">test_custom_output</a></code></li>
<li><code><a title="connpy.tests.test_printer.TestPrinter.test_debug_output" href="#connpy.tests.test_printer.TestPrinter.test_debug_output">test_debug_output</a></code></li>
<li><code><a title="connpy.tests.test_printer.TestPrinter.test_error_output" href="#connpy.tests.test_printer.TestPrinter.test_error_output">test_error_output</a></code></li>
<li><code><a title="connpy.tests.test_printer.TestPrinter.test_info_output" href="#connpy.tests.test_printer.TestPrinter.test_info_output">test_info_output</a></code></li>
<li><code><a title="connpy.tests.test_printer.TestPrinter.test_multiline_indentation" href="#connpy.tests.test_printer.TestPrinter.test_multiline_indentation">test_multiline_indentation</a></code></li>
<li><code><a title="connpy.tests.test_printer.TestPrinter.test_start_output" href="#connpy.tests.test_printer.TestPrinter.test_start_output">test_start_output</a></code></li>
<li><code><a title="connpy.tests.test_printer.TestPrinter.test_success_output" href="#connpy.tests.test_printer.TestPrinter.test_success_output">test_success_output</a></code></li>
<li><code><a title="connpy.tests.test_printer.TestPrinter.test_warning_output" href="#connpy.tests.test_printer.TestPrinter.test_warning_output">test_warning_output</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.6</a>.</p>
</footer>
</body>
</html>