feat: major architectural refactor to 5.1b1 - Service Layer, gRPC & Agent evolution (fragmented secrets)
This commit is contained in:
@@ -0,0 +1,375 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.cli.ai_handler 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.cli.ai_handler</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
||||
<dl>
|
||||
<dt id="connpy.cli.ai_handler.AIHandler"><code class="flex name class">
|
||||
<span>class <span class="ident">AIHandler</span></span>
|
||||
<span>(</span><span>app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class AIHandler:
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
|
||||
def dispatch(self, args):
|
||||
if args.list_sessions:
|
||||
sessions = self.app.services.ai.list_sessions()
|
||||
if not sessions:
|
||||
printer.info("No saved AI sessions found.")
|
||||
return
|
||||
columns = ["ID", "Title", "Created At", "Model"]
|
||||
rows = [[s["id"], s["title"], s["created_at"], s["model"]] for s in sessions]
|
||||
printer.table("AI Persisted Sessions", columns, rows)
|
||||
return
|
||||
|
||||
if args.delete_session:
|
||||
try:
|
||||
self.app.services.ai.delete_session(args.delete_session[0])
|
||||
printer.success(f"Session {args.delete_session[0]} deleted.")
|
||||
except Exception as e:
|
||||
printer.error(str(e))
|
||||
return
|
||||
|
||||
# Determinar session_id para retomar
|
||||
session_id = None
|
||||
if args.resume:
|
||||
sessions = self.app.services.ai.list_sessions()
|
||||
session_id = sessions[0]["id"] if sessions else None
|
||||
if not session_id:
|
||||
printer.warning("No previous session found to resume.")
|
||||
elif args.session:
|
||||
session_id = args.session[0]
|
||||
|
||||
# Configurar argumentos adicionales para el servicio de AI
|
||||
# Prioridad: CLI Args > Configuración Local
|
||||
settings = self.app.services.config_svc.get_settings().get("ai", {})
|
||||
arguments = {}
|
||||
|
||||
for key in ["engineer_model", "engineer_api_key", "architect_model", "architect_api_key"]:
|
||||
cli_val = getattr(args, key, None)
|
||||
if cli_val:
|
||||
arguments[key] = cli_val[0]
|
||||
elif settings.get(key):
|
||||
arguments[key] = settings.get(key)
|
||||
|
||||
# Check keys only if running in local mode (not remote)
|
||||
if getattr(self.app.services, "mode", "local") == "local":
|
||||
if not arguments.get("engineer_api_key"):
|
||||
printer.error("Engineer API key not configured. The chat cannot start.")
|
||||
printer.info("Use 'connpy config --engineer-api-key <key>' to set it.")
|
||||
sys.exit(1)
|
||||
if not arguments.get("architect_api_key"):
|
||||
printer.warning("Architect API key not configured. Architect will be unavailable.")
|
||||
printer.info("Use 'connpy config --architect-api-key <key>' to enable it.")
|
||||
|
||||
# El resto de la interacción el CLI la maneja con el agente subyacente
|
||||
self.app.myai = self.app.services.ai
|
||||
self.ai_overrides = arguments
|
||||
|
||||
if args.ask:
|
||||
self.single_question(args, session_id)
|
||||
else:
|
||||
self.interactive_chat(args, session_id)
|
||||
|
||||
def single_question(self, args, session_id):
|
||||
query = " ".join(args.ask)
|
||||
with console.status("[ai_status]Agent is thinking and analyzing...") as status:
|
||||
result = self.app.myai.ask(query, status=status, debug=args.debug, session_id=session_id, trust=args.trust, **self.ai_overrides)
|
||||
|
||||
responder = result.get("responder", "engineer")
|
||||
border = "architect" if responder == "architect" else "engineer"
|
||||
title = "[architect][bold]Network Architect[/bold][/architect]" if responder == "architect" else "[engineer][bold]Network Engineer[/bold][/engineer]"
|
||||
|
||||
if not result.get("streamed"):
|
||||
mdprint(Panel(Markdown(result["response"]), title=title, border_style=border, expand=False))
|
||||
|
||||
if "usage" in result:
|
||||
u = result["usage"]
|
||||
console.print(f"[debug]Tokens: {u['total']} (Input: {u['input']}, Output: {u['output']})[/debug]")
|
||||
console.print()
|
||||
|
||||
def interactive_chat(self, args, session_id):
|
||||
history = None
|
||||
if session_id:
|
||||
session_data = self.app.myai.load_session_data(session_id)
|
||||
if session_data:
|
||||
history = session_data.get("history", [])
|
||||
mdprint(Rule(title=f"[header] Resuming Session: {session_data.get('title')} [/header]", style="border"))
|
||||
if history:
|
||||
mdprint(f"[debug]Analyzing {len(history)} previous messages...[/debug]\n")
|
||||
else:
|
||||
printer.error(f"Could not load session {session_id}. Starting clean.")
|
||||
|
||||
if not history:
|
||||
mdprint(Rule(style="engineer"))
|
||||
mdprint(Markdown("**Networking Expert Agent**: Hi! I'm your assistant. I can help you diagnose issues, run commands, and manage your nodes.\nType 'exit' to quit.\n"))
|
||||
mdprint(Rule(style="engineer"))
|
||||
|
||||
while True:
|
||||
try:
|
||||
user_query = Prompt.ask("[user_prompt]User[/user_prompt]")
|
||||
if not user_query.strip(): continue
|
||||
if user_query.lower() in ['exit', 'quit', 'bye']: break
|
||||
|
||||
with console.status("[ai_status]Agent is thinking...") as status:
|
||||
result = self.app.myai.ask(user_query, chat_history=history, status=status, debug=args.debug, trust=args.trust, **self.ai_overrides)
|
||||
|
||||
new_history = result.get("chat_history")
|
||||
if new_history is not None:
|
||||
history = new_history
|
||||
|
||||
responder = result.get("responder", "engineer")
|
||||
border = "architect" if responder == "architect" else "engineer"
|
||||
title = "[architect][bold]Network Architect[/bold][/architect]" if responder == "architect" else "[engineer][bold]Network Engineer[/bold][/engineer]"
|
||||
|
||||
if not result.get("streamed"):
|
||||
response_text = result.get("response", "")
|
||||
if response_text:
|
||||
mdprint(Panel(Markdown(response_text), title=title, border_style=border, expand=False))
|
||||
|
||||
if "usage" in result:
|
||||
u = result["usage"]
|
||||
console.print(f"[debug]Tokens: {u['total']} (Input: {u['input']}, Output: {u['output']})[/debug]")
|
||||
console.print()
|
||||
except KeyboardInterrupt:
|
||||
break</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
<h3>Methods</h3>
|
||||
<dl>
|
||||
<dt id="connpy.cli.ai_handler.AIHandler.dispatch"><code class="name flex">
|
||||
<span>def <span class="ident">dispatch</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def dispatch(self, args):
|
||||
if args.list_sessions:
|
||||
sessions = self.app.services.ai.list_sessions()
|
||||
if not sessions:
|
||||
printer.info("No saved AI sessions found.")
|
||||
return
|
||||
columns = ["ID", "Title", "Created At", "Model"]
|
||||
rows = [[s["id"], s["title"], s["created_at"], s["model"]] for s in sessions]
|
||||
printer.table("AI Persisted Sessions", columns, rows)
|
||||
return
|
||||
|
||||
if args.delete_session:
|
||||
try:
|
||||
self.app.services.ai.delete_session(args.delete_session[0])
|
||||
printer.success(f"Session {args.delete_session[0]} deleted.")
|
||||
except Exception as e:
|
||||
printer.error(str(e))
|
||||
return
|
||||
|
||||
# Determinar session_id para retomar
|
||||
session_id = None
|
||||
if args.resume:
|
||||
sessions = self.app.services.ai.list_sessions()
|
||||
session_id = sessions[0]["id"] if sessions else None
|
||||
if not session_id:
|
||||
printer.warning("No previous session found to resume.")
|
||||
elif args.session:
|
||||
session_id = args.session[0]
|
||||
|
||||
# Configurar argumentos adicionales para el servicio de AI
|
||||
# Prioridad: CLI Args > Configuración Local
|
||||
settings = self.app.services.config_svc.get_settings().get("ai", {})
|
||||
arguments = {}
|
||||
|
||||
for key in ["engineer_model", "engineer_api_key", "architect_model", "architect_api_key"]:
|
||||
cli_val = getattr(args, key, None)
|
||||
if cli_val:
|
||||
arguments[key] = cli_val[0]
|
||||
elif settings.get(key):
|
||||
arguments[key] = settings.get(key)
|
||||
|
||||
# Check keys only if running in local mode (not remote)
|
||||
if getattr(self.app.services, "mode", "local") == "local":
|
||||
if not arguments.get("engineer_api_key"):
|
||||
printer.error("Engineer API key not configured. The chat cannot start.")
|
||||
printer.info("Use 'connpy config --engineer-api-key <key>' to set it.")
|
||||
sys.exit(1)
|
||||
if not arguments.get("architect_api_key"):
|
||||
printer.warning("Architect API key not configured. Architect will be unavailable.")
|
||||
printer.info("Use 'connpy config --architect-api-key <key>' to enable it.")
|
||||
|
||||
# El resto de la interacción el CLI la maneja con el agente subyacente
|
||||
self.app.myai = self.app.services.ai
|
||||
self.ai_overrides = arguments
|
||||
|
||||
if args.ask:
|
||||
self.single_question(args, session_id)
|
||||
else:
|
||||
self.interactive_chat(args, session_id)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.ai_handler.AIHandler.interactive_chat"><code class="name flex">
|
||||
<span>def <span class="ident">interactive_chat</span></span>(<span>self, args, session_id)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def interactive_chat(self, args, session_id):
|
||||
history = None
|
||||
if session_id:
|
||||
session_data = self.app.myai.load_session_data(session_id)
|
||||
if session_data:
|
||||
history = session_data.get("history", [])
|
||||
mdprint(Rule(title=f"[header] Resuming Session: {session_data.get('title')} [/header]", style="border"))
|
||||
if history:
|
||||
mdprint(f"[debug]Analyzing {len(history)} previous messages...[/debug]\n")
|
||||
else:
|
||||
printer.error(f"Could not load session {session_id}. Starting clean.")
|
||||
|
||||
if not history:
|
||||
mdprint(Rule(style="engineer"))
|
||||
mdprint(Markdown("**Networking Expert Agent**: Hi! I'm your assistant. I can help you diagnose issues, run commands, and manage your nodes.\nType 'exit' to quit.\n"))
|
||||
mdprint(Rule(style="engineer"))
|
||||
|
||||
while True:
|
||||
try:
|
||||
user_query = Prompt.ask("[user_prompt]User[/user_prompt]")
|
||||
if not user_query.strip(): continue
|
||||
if user_query.lower() in ['exit', 'quit', 'bye']: break
|
||||
|
||||
with console.status("[ai_status]Agent is thinking...") as status:
|
||||
result = self.app.myai.ask(user_query, chat_history=history, status=status, debug=args.debug, trust=args.trust, **self.ai_overrides)
|
||||
|
||||
new_history = result.get("chat_history")
|
||||
if new_history is not None:
|
||||
history = new_history
|
||||
|
||||
responder = result.get("responder", "engineer")
|
||||
border = "architect" if responder == "architect" else "engineer"
|
||||
title = "[architect][bold]Network Architect[/bold][/architect]" if responder == "architect" else "[engineer][bold]Network Engineer[/bold][/engineer]"
|
||||
|
||||
if not result.get("streamed"):
|
||||
response_text = result.get("response", "")
|
||||
if response_text:
|
||||
mdprint(Panel(Markdown(response_text), title=title, border_style=border, expand=False))
|
||||
|
||||
if "usage" in result:
|
||||
u = result["usage"]
|
||||
console.print(f"[debug]Tokens: {u['total']} (Input: {u['input']}, Output: {u['output']})[/debug]")
|
||||
console.print()
|
||||
except KeyboardInterrupt:
|
||||
break</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.ai_handler.AIHandler.single_question"><code class="name flex">
|
||||
<span>def <span class="ident">single_question</span></span>(<span>self, args, session_id)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def single_question(self, args, session_id):
|
||||
query = " ".join(args.ask)
|
||||
with console.status("[ai_status]Agent is thinking and analyzing...") as status:
|
||||
result = self.app.myai.ask(query, status=status, debug=args.debug, session_id=session_id, trust=args.trust, **self.ai_overrides)
|
||||
|
||||
responder = result.get("responder", "engineer")
|
||||
border = "architect" if responder == "architect" else "engineer"
|
||||
title = "[architect][bold]Network Architect[/bold][/architect]" if responder == "architect" else "[engineer][bold]Network Engineer[/bold][/engineer]"
|
||||
|
||||
if not result.get("streamed"):
|
||||
mdprint(Panel(Markdown(result["response"]), title=title, border_style=border, expand=False))
|
||||
|
||||
if "usage" in result:
|
||||
u = result["usage"]
|
||||
console.print(f"[debug]Tokens: {u['total']} (Input: {u['input']}, Output: {u['output']})[/debug]")
|
||||
console.print()</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.cli" href="index.html">connpy.cli</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<h4><code><a title="connpy.cli.ai_handler.AIHandler" href="#connpy.cli.ai_handler.AIHandler">AIHandler</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.cli.ai_handler.AIHandler.dispatch" href="#connpy.cli.ai_handler.AIHandler.dispatch">dispatch</a></code></li>
|
||||
<li><code><a title="connpy.cli.ai_handler.AIHandler.interactive_chat" href="#connpy.cli.ai_handler.AIHandler.interactive_chat">interactive_chat</a></code></li>
|
||||
<li><code><a title="connpy.cli.ai_handler.AIHandler.single_question" href="#connpy.cli.ai_handler.AIHandler.single_question">single_question</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,199 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.cli.api_handler 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.cli.api_handler</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
||||
<dl>
|
||||
<dt id="connpy.cli.api_handler.APIHandler"><code class="flex name class">
|
||||
<span>class <span class="ident">APIHandler</span></span>
|
||||
<span>(</span><span>app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class APIHandler:
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
|
||||
def dispatch(self, args):
|
||||
try:
|
||||
status = self.app.services.system.get_api_status()
|
||||
|
||||
if args.command == "stop":
|
||||
if not status["running"]:
|
||||
printer.warning("API does not seem to be running.")
|
||||
else:
|
||||
stopped = self.app.services.system.stop_api()
|
||||
if stopped:
|
||||
printer.success("API stopped successfully.")
|
||||
|
||||
elif args.command == "restart":
|
||||
port = args.data if args.data and isinstance(args.data, int) else None
|
||||
if status["running"]:
|
||||
printer.info(f"Stopping server with process ID {status['pid']}...")
|
||||
|
||||
# Service handles port preservation if port is None
|
||||
self.app.services.system.restart_api(port=port)
|
||||
|
||||
if status["running"]:
|
||||
printer.info(f"Server with process ID {status['pid']} stopped.")
|
||||
|
||||
# Re-fetch status to show the actual port used
|
||||
new_status = self.app.services.system.get_api_status()
|
||||
printer.success(f"API restarted on port {new_status.get('port', 'unknown')}.")
|
||||
|
||||
elif args.command == "start":
|
||||
if status["running"]:
|
||||
msg = f"Connpy server is already running (PID: {status['pid']}"
|
||||
if status.get("port"):
|
||||
msg += f", Port: {status['port']}"
|
||||
msg += ")."
|
||||
printer.warning(msg)
|
||||
else:
|
||||
port = args.data if args.data and isinstance(args.data, int) else 8048
|
||||
self.app.services.system.start_api(port=port)
|
||||
printer.success(f"API started on port {port}.")
|
||||
|
||||
elif args.command == "debug":
|
||||
port = args.data if args.data and isinstance(args.data, int) else 8048
|
||||
self.app.services.system.debug_api(port=port)
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(1)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
<h3>Methods</h3>
|
||||
<dl>
|
||||
<dt id="connpy.cli.api_handler.APIHandler.dispatch"><code class="name flex">
|
||||
<span>def <span class="ident">dispatch</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def dispatch(self, args):
|
||||
try:
|
||||
status = self.app.services.system.get_api_status()
|
||||
|
||||
if args.command == "stop":
|
||||
if not status["running"]:
|
||||
printer.warning("API does not seem to be running.")
|
||||
else:
|
||||
stopped = self.app.services.system.stop_api()
|
||||
if stopped:
|
||||
printer.success("API stopped successfully.")
|
||||
|
||||
elif args.command == "restart":
|
||||
port = args.data if args.data and isinstance(args.data, int) else None
|
||||
if status["running"]:
|
||||
printer.info(f"Stopping server with process ID {status['pid']}...")
|
||||
|
||||
# Service handles port preservation if port is None
|
||||
self.app.services.system.restart_api(port=port)
|
||||
|
||||
if status["running"]:
|
||||
printer.info(f"Server with process ID {status['pid']} stopped.")
|
||||
|
||||
# Re-fetch status to show the actual port used
|
||||
new_status = self.app.services.system.get_api_status()
|
||||
printer.success(f"API restarted on port {new_status.get('port', 'unknown')}.")
|
||||
|
||||
elif args.command == "start":
|
||||
if status["running"]:
|
||||
msg = f"Connpy server is already running (PID: {status['pid']}"
|
||||
if status.get("port"):
|
||||
msg += f", Port: {status['port']}"
|
||||
msg += ")."
|
||||
printer.warning(msg)
|
||||
else:
|
||||
port = args.data if args.data and isinstance(args.data, int) else 8048
|
||||
self.app.services.system.start_api(port=port)
|
||||
printer.success(f"API started on port {port}.")
|
||||
|
||||
elif args.command == "debug":
|
||||
port = args.data if args.data and isinstance(args.data, int) else 8048
|
||||
self.app.services.system.debug_api(port=port)
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(1)</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.cli" href="index.html">connpy.cli</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<h4><code><a title="connpy.cli.api_handler.APIHandler" href="#connpy.cli.api_handler.APIHandler">APIHandler</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.cli.api_handler.APIHandler.dispatch" href="#connpy.cli.api_handler.APIHandler.dispatch">dispatch</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,488 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.cli.config_handler 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.cli.config_handler</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
||||
<dl>
|
||||
<dt id="connpy.cli.config_handler.ConfigHandler"><code class="flex name class">
|
||||
<span>class <span class="ident">ConfigHandler</span></span>
|
||||
<span>(</span><span>app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class ConfigHandler:
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
|
||||
def dispatch(self, args):
|
||||
actions = {
|
||||
"completion": self.show_completion,
|
||||
"fzf_wrapper": self.show_fzf_wrapper,
|
||||
"case": self.set_case,
|
||||
"fzf": self.set_fzf,
|
||||
"idletime": self.set_idletime,
|
||||
"configfolder": self.set_configfolder,
|
||||
"theme": self.set_theme,
|
||||
"engineer_model": self.set_ai_config,
|
||||
"engineer_api_key": self.set_ai_config,
|
||||
"architect_model": self.set_ai_config,
|
||||
"architect_api_key": self.set_ai_config,
|
||||
"trusted_commands": self.set_ai_config,
|
||||
"service_mode": self.set_service_mode,
|
||||
"remote_host": self.set_remote_host,
|
||||
"sync_remote": self.set_sync_remote
|
||||
}
|
||||
handler = actions.get(getattr(args, "command", None))
|
||||
if handler:
|
||||
return handler(args)
|
||||
|
||||
# If no specific command was triggered, show current configuration
|
||||
return self.show_config(args)
|
||||
|
||||
def show_config(self, args):
|
||||
settings = self.app.services.config_svc.get_settings()
|
||||
yaml_str = yaml.dump(settings, sort_keys=False, default_flow_style=False)
|
||||
printer.data("Current Configuration", yaml_str)
|
||||
|
||||
def set_service_mode(self, args):
|
||||
new_mode = args.data[0]
|
||||
if new_mode == "remote":
|
||||
settings = self.app.services.config_svc.get_settings()
|
||||
if not settings.get("remote_host"):
|
||||
printer.error("Remote host must be configured before switching to remote mode")
|
||||
return
|
||||
|
||||
self.app.services.config_svc.update_setting("service_mode", new_mode)
|
||||
|
||||
# Immediate sync of fzf/text cache files for the new mode
|
||||
try:
|
||||
# 1. Clear old cache files to avoid discrepancies if fetch fails
|
||||
self.app.config._generate_nodes_cache(nodes=[], folders=[], profiles=[])
|
||||
|
||||
# 2. Re-initialize services for the new mode
|
||||
from ..services.provider import ServiceProvider
|
||||
settings = self.app.services.config_svc.get_settings()
|
||||
new_services = ServiceProvider(self.app.config, mode=new_mode, remote_host=settings.get("remote_host"))
|
||||
|
||||
# 3. Fetch data from new mode and generate cache
|
||||
nodes = new_services.nodes.list_nodes()
|
||||
folders = new_services.nodes.list_folders()
|
||||
profiles = new_services.profiles.list_profiles()
|
||||
new_services.nodes.generate_cache(nodes=nodes, folders=folders, profiles=profiles)
|
||||
|
||||
printer.success("Config saved")
|
||||
except Exception as e:
|
||||
printer.success("Config saved")
|
||||
printer.warning(f"Note: Could not synchronize fzf cache: {e}")
|
||||
|
||||
|
||||
def set_remote_host(self, args):
|
||||
self.app.services.config_svc.update_setting("remote_host", args.data[0])
|
||||
printer.success("Config saved")
|
||||
|
||||
def set_theme(self, args):
|
||||
try:
|
||||
valid_styles = self.app.services.config_svc.apply_theme_from_file(args.data[0])
|
||||
# Apply immediately to current session
|
||||
printer.apply_theme(valid_styles)
|
||||
printer.success(f"Theme '{args.data[0]}' applied and saved")
|
||||
except (ConnpyError, InvalidConfigurationError) as e:
|
||||
printer.error(str(e))
|
||||
|
||||
def show_fzf_wrapper(self, args):
|
||||
print(get_instructions("fzf_wrapper_" + args.data[0]))
|
||||
|
||||
def show_completion(self, args):
|
||||
print(get_instructions(args.data[0] + "completion"))
|
||||
|
||||
def set_case(self, args):
|
||||
val = (args.data[0].lower() == "true")
|
||||
self.app.services.config_svc.update_setting("case", val)
|
||||
self.app.case = val
|
||||
printer.success("Config saved")
|
||||
|
||||
def set_fzf(self, args):
|
||||
val = (args.data[0].lower() == "true")
|
||||
self.app.services.config_svc.update_setting("fzf", val)
|
||||
self.app.fzf = val
|
||||
printer.success("Config saved")
|
||||
|
||||
def set_idletime(self, args):
|
||||
try:
|
||||
val = max(0, int(args.data[0]))
|
||||
self.app.services.config_svc.update_setting("idletime", val)
|
||||
printer.success("Config saved")
|
||||
except ValueError:
|
||||
printer.error("Keepalive must be an integer.")
|
||||
|
||||
def set_configfolder(self, args):
|
||||
try:
|
||||
self.app.services.config_svc.set_config_folder(args.data[0])
|
||||
printer.success("Config saved")
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(1)
|
||||
|
||||
def set_sync_remote(self, args):
|
||||
val = (args.data[0].lower() == "true")
|
||||
self.app.services.config_svc.update_setting("sync_remote", val)
|
||||
self.app.services.sync.sync_remote = val
|
||||
printer.success("Config saved")
|
||||
|
||||
def set_ai_config(self, args):
|
||||
try:
|
||||
settings = self.app.services.config_svc.get_settings()
|
||||
aiconfig = settings.get("ai", {})
|
||||
aiconfig[args.command] = args.data[0]
|
||||
self.app.services.config_svc.update_setting("ai", aiconfig)
|
||||
printer.success("Config saved")
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
<h3>Methods</h3>
|
||||
<dl>
|
||||
<dt id="connpy.cli.config_handler.ConfigHandler.dispatch"><code class="name flex">
|
||||
<span>def <span class="ident">dispatch</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def dispatch(self, args):
|
||||
actions = {
|
||||
"completion": self.show_completion,
|
||||
"fzf_wrapper": self.show_fzf_wrapper,
|
||||
"case": self.set_case,
|
||||
"fzf": self.set_fzf,
|
||||
"idletime": self.set_idletime,
|
||||
"configfolder": self.set_configfolder,
|
||||
"theme": self.set_theme,
|
||||
"engineer_model": self.set_ai_config,
|
||||
"engineer_api_key": self.set_ai_config,
|
||||
"architect_model": self.set_ai_config,
|
||||
"architect_api_key": self.set_ai_config,
|
||||
"trusted_commands": self.set_ai_config,
|
||||
"service_mode": self.set_service_mode,
|
||||
"remote_host": self.set_remote_host,
|
||||
"sync_remote": self.set_sync_remote
|
||||
}
|
||||
handler = actions.get(getattr(args, "command", None))
|
||||
if handler:
|
||||
return handler(args)
|
||||
|
||||
# If no specific command was triggered, show current configuration
|
||||
return self.show_config(args)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.config_handler.ConfigHandler.set_ai_config"><code class="name flex">
|
||||
<span>def <span class="ident">set_ai_config</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def set_ai_config(self, args):
|
||||
try:
|
||||
settings = self.app.services.config_svc.get_settings()
|
||||
aiconfig = settings.get("ai", {})
|
||||
aiconfig[args.command] = args.data[0]
|
||||
self.app.services.config_svc.update_setting("ai", aiconfig)
|
||||
printer.success("Config saved")
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.config_handler.ConfigHandler.set_case"><code class="name flex">
|
||||
<span>def <span class="ident">set_case</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def set_case(self, args):
|
||||
val = (args.data[0].lower() == "true")
|
||||
self.app.services.config_svc.update_setting("case", val)
|
||||
self.app.case = val
|
||||
printer.success("Config saved")</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.config_handler.ConfigHandler.set_configfolder"><code class="name flex">
|
||||
<span>def <span class="ident">set_configfolder</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def set_configfolder(self, args):
|
||||
try:
|
||||
self.app.services.config_svc.set_config_folder(args.data[0])
|
||||
printer.success("Config saved")
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(1)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.config_handler.ConfigHandler.set_fzf"><code class="name flex">
|
||||
<span>def <span class="ident">set_fzf</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def set_fzf(self, args):
|
||||
val = (args.data[0].lower() == "true")
|
||||
self.app.services.config_svc.update_setting("fzf", val)
|
||||
self.app.fzf = val
|
||||
printer.success("Config saved")</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.config_handler.ConfigHandler.set_idletime"><code class="name flex">
|
||||
<span>def <span class="ident">set_idletime</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def set_idletime(self, args):
|
||||
try:
|
||||
val = max(0, int(args.data[0]))
|
||||
self.app.services.config_svc.update_setting("idletime", val)
|
||||
printer.success("Config saved")
|
||||
except ValueError:
|
||||
printer.error("Keepalive must be an integer.")</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.config_handler.ConfigHandler.set_remote_host"><code class="name flex">
|
||||
<span>def <span class="ident">set_remote_host</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def set_remote_host(self, args):
|
||||
self.app.services.config_svc.update_setting("remote_host", args.data[0])
|
||||
printer.success("Config saved")</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.config_handler.ConfigHandler.set_service_mode"><code class="name flex">
|
||||
<span>def <span class="ident">set_service_mode</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def set_service_mode(self, args):
|
||||
new_mode = args.data[0]
|
||||
if new_mode == "remote":
|
||||
settings = self.app.services.config_svc.get_settings()
|
||||
if not settings.get("remote_host"):
|
||||
printer.error("Remote host must be configured before switching to remote mode")
|
||||
return
|
||||
|
||||
self.app.services.config_svc.update_setting("service_mode", new_mode)
|
||||
|
||||
# Immediate sync of fzf/text cache files for the new mode
|
||||
try:
|
||||
# 1. Clear old cache files to avoid discrepancies if fetch fails
|
||||
self.app.config._generate_nodes_cache(nodes=[], folders=[], profiles=[])
|
||||
|
||||
# 2. Re-initialize services for the new mode
|
||||
from ..services.provider import ServiceProvider
|
||||
settings = self.app.services.config_svc.get_settings()
|
||||
new_services = ServiceProvider(self.app.config, mode=new_mode, remote_host=settings.get("remote_host"))
|
||||
|
||||
# 3. Fetch data from new mode and generate cache
|
||||
nodes = new_services.nodes.list_nodes()
|
||||
folders = new_services.nodes.list_folders()
|
||||
profiles = new_services.profiles.list_profiles()
|
||||
new_services.nodes.generate_cache(nodes=nodes, folders=folders, profiles=profiles)
|
||||
|
||||
printer.success("Config saved")
|
||||
except Exception as e:
|
||||
printer.success("Config saved")
|
||||
printer.warning(f"Note: Could not synchronize fzf cache: {e}")</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.config_handler.ConfigHandler.set_sync_remote"><code class="name flex">
|
||||
<span>def <span class="ident">set_sync_remote</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def set_sync_remote(self, args):
|
||||
val = (args.data[0].lower() == "true")
|
||||
self.app.services.config_svc.update_setting("sync_remote", val)
|
||||
self.app.services.sync.sync_remote = val
|
||||
printer.success("Config saved")</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.config_handler.ConfigHandler.set_theme"><code class="name flex">
|
||||
<span>def <span class="ident">set_theme</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def set_theme(self, args):
|
||||
try:
|
||||
valid_styles = self.app.services.config_svc.apply_theme_from_file(args.data[0])
|
||||
# Apply immediately to current session
|
||||
printer.apply_theme(valid_styles)
|
||||
printer.success(f"Theme '{args.data[0]}' applied and saved")
|
||||
except (ConnpyError, InvalidConfigurationError) as e:
|
||||
printer.error(str(e))</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.config_handler.ConfigHandler.show_completion"><code class="name flex">
|
||||
<span>def <span class="ident">show_completion</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def show_completion(self, args):
|
||||
print(get_instructions(args.data[0] + "completion"))</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.config_handler.ConfigHandler.show_config"><code class="name flex">
|
||||
<span>def <span class="ident">show_config</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def show_config(self, args):
|
||||
settings = self.app.services.config_svc.get_settings()
|
||||
yaml_str = yaml.dump(settings, sort_keys=False, default_flow_style=False)
|
||||
printer.data("Current Configuration", yaml_str)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.config_handler.ConfigHandler.show_fzf_wrapper"><code class="name flex">
|
||||
<span>def <span class="ident">show_fzf_wrapper</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def show_fzf_wrapper(self, args):
|
||||
print(get_instructions("fzf_wrapper_" + args.data[0]))</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.cli" href="index.html">connpy.cli</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<h4><code><a title="connpy.cli.config_handler.ConfigHandler" href="#connpy.cli.config_handler.ConfigHandler">ConfigHandler</a></code></h4>
|
||||
<ul class="two-column">
|
||||
<li><code><a title="connpy.cli.config_handler.ConfigHandler.dispatch" href="#connpy.cli.config_handler.ConfigHandler.dispatch">dispatch</a></code></li>
|
||||
<li><code><a title="connpy.cli.config_handler.ConfigHandler.set_ai_config" href="#connpy.cli.config_handler.ConfigHandler.set_ai_config">set_ai_config</a></code></li>
|
||||
<li><code><a title="connpy.cli.config_handler.ConfigHandler.set_case" href="#connpy.cli.config_handler.ConfigHandler.set_case">set_case</a></code></li>
|
||||
<li><code><a title="connpy.cli.config_handler.ConfigHandler.set_configfolder" href="#connpy.cli.config_handler.ConfigHandler.set_configfolder">set_configfolder</a></code></li>
|
||||
<li><code><a title="connpy.cli.config_handler.ConfigHandler.set_fzf" href="#connpy.cli.config_handler.ConfigHandler.set_fzf">set_fzf</a></code></li>
|
||||
<li><code><a title="connpy.cli.config_handler.ConfigHandler.set_idletime" href="#connpy.cli.config_handler.ConfigHandler.set_idletime">set_idletime</a></code></li>
|
||||
<li><code><a title="connpy.cli.config_handler.ConfigHandler.set_remote_host" href="#connpy.cli.config_handler.ConfigHandler.set_remote_host">set_remote_host</a></code></li>
|
||||
<li><code><a title="connpy.cli.config_handler.ConfigHandler.set_service_mode" href="#connpy.cli.config_handler.ConfigHandler.set_service_mode">set_service_mode</a></code></li>
|
||||
<li><code><a title="connpy.cli.config_handler.ConfigHandler.set_sync_remote" href="#connpy.cli.config_handler.ConfigHandler.set_sync_remote">set_sync_remote</a></code></li>
|
||||
<li><code><a title="connpy.cli.config_handler.ConfigHandler.set_theme" href="#connpy.cli.config_handler.ConfigHandler.set_theme">set_theme</a></code></li>
|
||||
<li><code><a title="connpy.cli.config_handler.ConfigHandler.show_completion" href="#connpy.cli.config_handler.ConfigHandler.show_completion">show_completion</a></code></li>
|
||||
<li><code><a title="connpy.cli.config_handler.ConfigHandler.show_config" href="#connpy.cli.config_handler.ConfigHandler.show_config">show_config</a></code></li>
|
||||
<li><code><a title="connpy.cli.config_handler.ConfigHandler.show_fzf_wrapper" href="#connpy.cli.config_handler.ConfigHandler.show_fzf_wrapper">show_fzf_wrapper</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,255 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.cli.context_handler 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.cli.context_handler</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
||||
<dl>
|
||||
<dt id="connpy.cli.context_handler.ContextHandler"><code class="flex name class">
|
||||
<span>class <span class="ident">ContextHandler</span></span>
|
||||
<span>(</span><span>app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class ContextHandler:
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
self.service = self.app.services.context
|
||||
|
||||
def dispatch(self, args):
|
||||
try:
|
||||
if args.add:
|
||||
if len(args.add) < 2:
|
||||
printer.error("--add requires name and at least one regex")
|
||||
return
|
||||
self.service.add_context(args.add[0], args.add[1:])
|
||||
printer.success(f"Context '{args.add[0]}' added successfully.")
|
||||
|
||||
elif args.rm:
|
||||
if not args.context_name:
|
||||
printer.error("--rm requires a context name")
|
||||
return
|
||||
self.service.delete_context(args.context_name)
|
||||
printer.success(f"Context '{args.context_name}' deleted successfully.")
|
||||
|
||||
elif args.ls:
|
||||
contexts = self.service.list_contexts()
|
||||
for ctx in contexts:
|
||||
if ctx["active"]:
|
||||
printer.success(f"{ctx['name']} (active)")
|
||||
else:
|
||||
printer.custom(" ", ctx["name"])
|
||||
|
||||
elif args.set:
|
||||
if not args.context_name:
|
||||
printer.error("--set requires a context name")
|
||||
return
|
||||
self.service.set_active_context(args.context_name)
|
||||
printer.success(f"Context set to: {args.context_name}")
|
||||
|
||||
elif args.show:
|
||||
if not args.context_name:
|
||||
printer.error("--show requires a context name")
|
||||
return
|
||||
contexts = self.service.contexts
|
||||
if args.context_name not in contexts:
|
||||
printer.error(f"Context '{args.context_name}' does not exist")
|
||||
return
|
||||
yaml_output = yaml.dump(contexts[args.context_name], sort_keys=False, default_flow_style=False)
|
||||
printer.custom(args.context_name, "")
|
||||
print(yaml_output)
|
||||
|
||||
elif args.edit:
|
||||
if len(args.edit) < 2:
|
||||
printer.error("--edit requires name and at least one regex")
|
||||
return
|
||||
self.service.update_context(args.edit[0], args.edit[1:])
|
||||
printer.success(f"Context '{args.edit[0]}' modified successfully.")
|
||||
|
||||
else:
|
||||
# Default behavior if no flags: show list
|
||||
self.dispatch_ls(args)
|
||||
|
||||
except ValueError as e:
|
||||
printer.error(str(e))
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(1)
|
||||
|
||||
def dispatch_ls(self, args):
|
||||
contexts = self.service.list_contexts()
|
||||
for ctx in contexts:
|
||||
if ctx["active"]:
|
||||
printer.success(f"{ctx['name']} (active)")
|
||||
else:
|
||||
printer.custom(" ", ctx["name"])</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
<h3>Methods</h3>
|
||||
<dl>
|
||||
<dt id="connpy.cli.context_handler.ContextHandler.dispatch"><code class="name flex">
|
||||
<span>def <span class="ident">dispatch</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def dispatch(self, args):
|
||||
try:
|
||||
if args.add:
|
||||
if len(args.add) < 2:
|
||||
printer.error("--add requires name and at least one regex")
|
||||
return
|
||||
self.service.add_context(args.add[0], args.add[1:])
|
||||
printer.success(f"Context '{args.add[0]}' added successfully.")
|
||||
|
||||
elif args.rm:
|
||||
if not args.context_name:
|
||||
printer.error("--rm requires a context name")
|
||||
return
|
||||
self.service.delete_context(args.context_name)
|
||||
printer.success(f"Context '{args.context_name}' deleted successfully.")
|
||||
|
||||
elif args.ls:
|
||||
contexts = self.service.list_contexts()
|
||||
for ctx in contexts:
|
||||
if ctx["active"]:
|
||||
printer.success(f"{ctx['name']} (active)")
|
||||
else:
|
||||
printer.custom(" ", ctx["name"])
|
||||
|
||||
elif args.set:
|
||||
if not args.context_name:
|
||||
printer.error("--set requires a context name")
|
||||
return
|
||||
self.service.set_active_context(args.context_name)
|
||||
printer.success(f"Context set to: {args.context_name}")
|
||||
|
||||
elif args.show:
|
||||
if not args.context_name:
|
||||
printer.error("--show requires a context name")
|
||||
return
|
||||
contexts = self.service.contexts
|
||||
if args.context_name not in contexts:
|
||||
printer.error(f"Context '{args.context_name}' does not exist")
|
||||
return
|
||||
yaml_output = yaml.dump(contexts[args.context_name], sort_keys=False, default_flow_style=False)
|
||||
printer.custom(args.context_name, "")
|
||||
print(yaml_output)
|
||||
|
||||
elif args.edit:
|
||||
if len(args.edit) < 2:
|
||||
printer.error("--edit requires name and at least one regex")
|
||||
return
|
||||
self.service.update_context(args.edit[0], args.edit[1:])
|
||||
printer.success(f"Context '{args.edit[0]}' modified successfully.")
|
||||
|
||||
else:
|
||||
# Default behavior if no flags: show list
|
||||
self.dispatch_ls(args)
|
||||
|
||||
except ValueError as e:
|
||||
printer.error(str(e))
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(1)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.context_handler.ContextHandler.dispatch_ls"><code class="name flex">
|
||||
<span>def <span class="ident">dispatch_ls</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def dispatch_ls(self, args):
|
||||
contexts = self.service.list_contexts()
|
||||
for ctx in contexts:
|
||||
if ctx["active"]:
|
||||
printer.success(f"{ctx['name']} (active)")
|
||||
else:
|
||||
printer.custom(" ", ctx["name"])</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.cli" href="index.html">connpy.cli</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<h4><code><a title="connpy.cli.context_handler.ContextHandler" href="#connpy.cli.context_handler.ContextHandler">ContextHandler</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.cli.context_handler.ContextHandler.dispatch" href="#connpy.cli.context_handler.ContextHandler.dispatch">dispatch</a></code></li>
|
||||
<li><code><a title="connpy.cli.context_handler.ContextHandler.dispatch_ls" href="#connpy.cli.context_handler.ContextHandler.dispatch_ls">dispatch_ls</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,523 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.cli.forms 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.cli.forms</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
||||
<dl>
|
||||
<dt id="connpy.cli.forms.Forms"><code class="flex name class">
|
||||
<span>class <span class="ident">Forms</span></span>
|
||||
<span>(</span><span>app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class Forms:
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
self.validators = Validators(app)
|
||||
|
||||
def questions_edit(self):
|
||||
questions = []
|
||||
questions.append(inquirer.Confirm("host", message="Edit Hostname/IP?"))
|
||||
questions.append(inquirer.Confirm("protocol", message="Edit Protocol/app?"))
|
||||
questions.append(inquirer.Confirm("port", message="Edit Port?"))
|
||||
questions.append(inquirer.Confirm("options", message="Edit Options?"))
|
||||
questions.append(inquirer.Confirm("logs", message="Edit logging path/file?"))
|
||||
questions.append(inquirer.Confirm("tags", message="Edit tags?"))
|
||||
questions.append(inquirer.Confirm("jumphost", message="Edit jumphost?"))
|
||||
questions.append(inquirer.Confirm("user", message="Edit User?"))
|
||||
questions.append(inquirer.Confirm("password", message="Edit password?"))
|
||||
return inquirer.prompt(questions)
|
||||
|
||||
def questions_nodes(self, unique, uniques=None, edit=None):
|
||||
try:
|
||||
defaults = self.app.services.nodes.get_node_details(unique)
|
||||
if "tags" not in defaults:
|
||||
defaults["tags"] = ""
|
||||
if "jumphost" not in defaults:
|
||||
defaults["jumphost"] = ""
|
||||
except Exception:
|
||||
defaults = {"host": "", "protocol": "", "port": "", "user": "", "options": "", "logs": "", "tags": "", "password": "", "jumphost": ""}
|
||||
node = {}
|
||||
if edit is None:
|
||||
edit = {"host": True, "protocol": True, "port": True, "user": True, "password": True, "options": True, "logs": True, "tags": True, "jumphost": True}
|
||||
questions = []
|
||||
if edit["host"]:
|
||||
questions.append(inquirer.Text("host", message="Add Hostname or IP", validate=self.validators.host_validation, default=defaults["host"]))
|
||||
else:
|
||||
node["host"] = defaults["host"]
|
||||
if edit["protocol"]:
|
||||
questions.append(inquirer.Text("protocol", message="Select Protocol/app", validate=self.validators.protocol_validation, default=defaults["protocol"]))
|
||||
else:
|
||||
node["protocol"] = defaults["protocol"]
|
||||
if edit["port"]:
|
||||
questions.append(inquirer.Text("port", message="Select Port Number", validate=self.validators.port_validation, default=defaults["port"]))
|
||||
else:
|
||||
node["port"] = defaults["port"]
|
||||
if edit["options"]:
|
||||
questions.append(inquirer.Text("options", message="Pass extra options to protocol/app", validate=self.validators.default_validation, default=defaults["options"]))
|
||||
else:
|
||||
node["options"] = defaults["options"]
|
||||
if edit["logs"]:
|
||||
questions.append(inquirer.Text("logs", message="Pick logging path/file ", validate=self.validators.default_validation, default=defaults["logs"].replace("{", "{{").replace("}", "}}")))
|
||||
else:
|
||||
node["logs"] = defaults["logs"]
|
||||
if edit["tags"]:
|
||||
questions.append(inquirer.Text("tags", message="Add tags dictionary", validate=self.validators.tags_validation, default=str(defaults["tags"]).replace("{", "{{").replace("}", "}}")))
|
||||
else:
|
||||
node["tags"] = defaults["tags"]
|
||||
if edit["jumphost"]:
|
||||
questions.append(inquirer.Text("jumphost", message="Add Jumphost node", validate=self.validators.jumphost_validation, default=str(defaults["jumphost"]).replace("{", "{{").replace("}", "}}")))
|
||||
else:
|
||||
node["jumphost"] = defaults["jumphost"]
|
||||
if edit["user"]:
|
||||
questions.append(inquirer.Text("user", message="Pick username", validate=self.validators.default_validation, default=defaults["user"]))
|
||||
else:
|
||||
node["user"] = defaults["user"]
|
||||
if edit["password"]:
|
||||
questions.append(inquirer.List("password", message="Password: Use a local password, no password or a list of profiles to reference?", choices=["Local Password", "Profiles", "No Password"]))
|
||||
else:
|
||||
node["password"] = defaults["password"]
|
||||
|
||||
answer = inquirer.prompt(questions)
|
||||
if answer is None:
|
||||
return False
|
||||
|
||||
if "password" in answer:
|
||||
if answer["password"] == "Local Password":
|
||||
passq = [inquirer.Password("password", message="Set Password")]
|
||||
passa = inquirer.prompt(passq)
|
||||
if passa is None:
|
||||
return False
|
||||
answer["password"] = self.app.services.config_svc.encrypt_password(passa["password"])
|
||||
elif answer["password"] == "Profiles":
|
||||
passq = [(inquirer.Text("password", message="Set a @profile or a comma separated list of @profiles", validate=self.validators.pass_validation))]
|
||||
passa = inquirer.prompt(passq)
|
||||
if passa is None:
|
||||
return False
|
||||
answer["password"] = passa["password"].split(",")
|
||||
elif answer["password"] == "No Password":
|
||||
answer["password"] = ""
|
||||
|
||||
if "tags" in answer and not answer["tags"].startswith("@") and answer["tags"]:
|
||||
answer["tags"] = ast.literal_eval(answer["tags"])
|
||||
|
||||
result = {**uniques, **answer, **node}
|
||||
result["type"] = "connection"
|
||||
return result
|
||||
|
||||
def questions_profiles(self, unique, edit=None):
|
||||
try:
|
||||
defaults = self.app.services.profiles.get_profile(unique, resolve=False)
|
||||
if "tags" not in defaults:
|
||||
defaults["tags"] = ""
|
||||
if "jumphost" not in defaults:
|
||||
defaults["jumphost"] = ""
|
||||
except Exception:
|
||||
defaults = {"host": "", "protocol": "", "port": "", "user": "", "options": "", "logs": "", "tags": "", "jumphost": ""}
|
||||
profile = {}
|
||||
if edit is None:
|
||||
edit = {"host": True, "protocol": True, "port": True, "user": True, "password": True, "options": True, "logs": True, "tags": True, "jumphost": True}
|
||||
questions = []
|
||||
if edit["host"]:
|
||||
questions.append(inquirer.Text("host", message="Add Hostname or IP", default=defaults["host"]))
|
||||
else:
|
||||
profile["host"] = defaults["host"]
|
||||
if edit["protocol"]:
|
||||
questions.append(inquirer.Text("protocol", message="Select Protocol/app", validate=self.validators.profile_protocol_validation, default=defaults["protocol"]))
|
||||
else:
|
||||
profile["protocol"] = defaults["protocol"]
|
||||
if edit["port"]:
|
||||
questions.append(inquirer.Text("port", message="Select Port Number", validate=self.validators.profile_port_validation, default=defaults["port"]))
|
||||
else:
|
||||
profile["port"] = defaults["port"]
|
||||
if edit["options"]:
|
||||
questions.append(inquirer.Text("options", message="Pass extra options to protocol/app", default=defaults["options"]))
|
||||
else:
|
||||
profile["options"] = defaults["options"]
|
||||
if edit["logs"]:
|
||||
questions.append(inquirer.Text("logs", message="Pick logging path/file ", default=defaults["logs"].replace("{", "{{").replace("}", "}}")))
|
||||
else:
|
||||
profile["logs"] = defaults["logs"]
|
||||
if edit["tags"]:
|
||||
questions.append(inquirer.Text("tags", message="Add tags dictionary", validate=self.validators.profile_tags_validation, default=str(defaults["tags"]).replace("{", "{{").replace("}", "}}")))
|
||||
else:
|
||||
profile["tags"] = defaults["tags"]
|
||||
if edit["jumphost"]:
|
||||
questions.append(inquirer.Text("jumphost", message="Add Jumphost node", validate=self.validators.profile_jumphost_validation, default=str(defaults["jumphost"]).replace("{", "{{").replace("}", "}}")))
|
||||
else:
|
||||
profile["jumphost"] = defaults["jumphost"]
|
||||
if edit["user"]:
|
||||
questions.append(inquirer.Text("user", message="Pick username", default=defaults["user"]))
|
||||
else:
|
||||
profile["user"] = defaults["user"]
|
||||
if edit["password"]:
|
||||
questions.append(inquirer.Password("password", message="Set Password"))
|
||||
else:
|
||||
profile["password"] = defaults["password"]
|
||||
|
||||
answer = inquirer.prompt(questions)
|
||||
if answer is None:
|
||||
return False
|
||||
|
||||
if "password" in answer:
|
||||
if answer["password"] != "":
|
||||
answer["password"] = self.app.services.config_svc.encrypt_password(answer["password"])
|
||||
|
||||
if "tags" in answer and answer["tags"]:
|
||||
answer["tags"] = ast.literal_eval(answer["tags"])
|
||||
|
||||
result = {**answer, **profile}
|
||||
result["id"] = unique
|
||||
return result
|
||||
|
||||
def questions_bulk(self, nodes="", hosts=""):
|
||||
questions = []
|
||||
questions.append(inquirer.Text("ids", message="add a comma separated list of nodes to add", default=nodes, validate=self.validators.bulk_node_validation))
|
||||
questions.append(inquirer.Text("location", message="Add a @folder, @subfolder@folder or leave empty", validate=self.validators.bulk_folder_validation))
|
||||
questions.append(inquirer.Text("host", message="Add comma separated list of Hostnames or IPs", default=hosts, validate=self.validators.bulk_host_validation))
|
||||
questions.append(inquirer.Text("protocol", message="Select Protocol/app", validate=self.validators.protocol_validation))
|
||||
questions.append(inquirer.Text("port", message="Select Port Number", validate=self.validators.port_validation))
|
||||
questions.append(inquirer.Text("options", message="Pass extra options to protocol/app", validate=self.validators.default_validation))
|
||||
questions.append(inquirer.Text("logs", message="Pick logging path/file ", validate=self.validators.default_validation))
|
||||
questions.append(inquirer.Text("tags", message="Add tags dictionary", validate=self.validators.tags_validation))
|
||||
questions.append(inquirer.Text("jumphost", message="Add Jumphost node", validate=self.validators.jumphost_validation))
|
||||
questions.append(inquirer.Text("user", message="Pick username", validate=self.validators.default_validation))
|
||||
questions.append(inquirer.List("password", message="Password: Use a local password, no password or a list of profiles to reference?", choices=["Local Password", "Profiles", "No Password"]))
|
||||
|
||||
answer = inquirer.prompt(questions)
|
||||
if answer is None:
|
||||
return False
|
||||
|
||||
if "password" in answer:
|
||||
if answer["password"] == "Local Password":
|
||||
passq = [inquirer.Password("password", message="Set Password")]
|
||||
passa = inquirer.prompt(passq)
|
||||
answer["password"] = self.app.services.config_svc.encrypt_password(passa["password"])
|
||||
elif answer["password"] == "Profiles":
|
||||
passq = [(inquirer.Text("password", message="Set a @profile or a comma separated list of @profiles", validate=self.validators.pass_validation))]
|
||||
passa = inquirer.prompt(passq)
|
||||
answer["password"] = passa["password"].split(",")
|
||||
elif answer["password"] == "No Password":
|
||||
answer["password"] = ""
|
||||
|
||||
answer["type"] = "connection"
|
||||
if "tags" in answer and not answer["tags"].startswith("@") and answer["tags"]:
|
||||
answer["tags"] = ast.literal_eval(answer["tags"])
|
||||
|
||||
return answer</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
<h3>Methods</h3>
|
||||
<dl>
|
||||
<dt id="connpy.cli.forms.Forms.questions_bulk"><code class="name flex">
|
||||
<span>def <span class="ident">questions_bulk</span></span>(<span>self, nodes='', hosts='')</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def questions_bulk(self, nodes="", hosts=""):
|
||||
questions = []
|
||||
questions.append(inquirer.Text("ids", message="add a comma separated list of nodes to add", default=nodes, validate=self.validators.bulk_node_validation))
|
||||
questions.append(inquirer.Text("location", message="Add a @folder, @subfolder@folder or leave empty", validate=self.validators.bulk_folder_validation))
|
||||
questions.append(inquirer.Text("host", message="Add comma separated list of Hostnames or IPs", default=hosts, validate=self.validators.bulk_host_validation))
|
||||
questions.append(inquirer.Text("protocol", message="Select Protocol/app", validate=self.validators.protocol_validation))
|
||||
questions.append(inquirer.Text("port", message="Select Port Number", validate=self.validators.port_validation))
|
||||
questions.append(inquirer.Text("options", message="Pass extra options to protocol/app", validate=self.validators.default_validation))
|
||||
questions.append(inquirer.Text("logs", message="Pick logging path/file ", validate=self.validators.default_validation))
|
||||
questions.append(inquirer.Text("tags", message="Add tags dictionary", validate=self.validators.tags_validation))
|
||||
questions.append(inquirer.Text("jumphost", message="Add Jumphost node", validate=self.validators.jumphost_validation))
|
||||
questions.append(inquirer.Text("user", message="Pick username", validate=self.validators.default_validation))
|
||||
questions.append(inquirer.List("password", message="Password: Use a local password, no password or a list of profiles to reference?", choices=["Local Password", "Profiles", "No Password"]))
|
||||
|
||||
answer = inquirer.prompt(questions)
|
||||
if answer is None:
|
||||
return False
|
||||
|
||||
if "password" in answer:
|
||||
if answer["password"] == "Local Password":
|
||||
passq = [inquirer.Password("password", message="Set Password")]
|
||||
passa = inquirer.prompt(passq)
|
||||
answer["password"] = self.app.services.config_svc.encrypt_password(passa["password"])
|
||||
elif answer["password"] == "Profiles":
|
||||
passq = [(inquirer.Text("password", message="Set a @profile or a comma separated list of @profiles", validate=self.validators.pass_validation))]
|
||||
passa = inquirer.prompt(passq)
|
||||
answer["password"] = passa["password"].split(",")
|
||||
elif answer["password"] == "No Password":
|
||||
answer["password"] = ""
|
||||
|
||||
answer["type"] = "connection"
|
||||
if "tags" in answer and not answer["tags"].startswith("@") and answer["tags"]:
|
||||
answer["tags"] = ast.literal_eval(answer["tags"])
|
||||
|
||||
return answer</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.forms.Forms.questions_edit"><code class="name flex">
|
||||
<span>def <span class="ident">questions_edit</span></span>(<span>self)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def questions_edit(self):
|
||||
questions = []
|
||||
questions.append(inquirer.Confirm("host", message="Edit Hostname/IP?"))
|
||||
questions.append(inquirer.Confirm("protocol", message="Edit Protocol/app?"))
|
||||
questions.append(inquirer.Confirm("port", message="Edit Port?"))
|
||||
questions.append(inquirer.Confirm("options", message="Edit Options?"))
|
||||
questions.append(inquirer.Confirm("logs", message="Edit logging path/file?"))
|
||||
questions.append(inquirer.Confirm("tags", message="Edit tags?"))
|
||||
questions.append(inquirer.Confirm("jumphost", message="Edit jumphost?"))
|
||||
questions.append(inquirer.Confirm("user", message="Edit User?"))
|
||||
questions.append(inquirer.Confirm("password", message="Edit password?"))
|
||||
return inquirer.prompt(questions)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.forms.Forms.questions_nodes"><code class="name flex">
|
||||
<span>def <span class="ident">questions_nodes</span></span>(<span>self, unique, uniques=None, edit=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def questions_nodes(self, unique, uniques=None, edit=None):
|
||||
try:
|
||||
defaults = self.app.services.nodes.get_node_details(unique)
|
||||
if "tags" not in defaults:
|
||||
defaults["tags"] = ""
|
||||
if "jumphost" not in defaults:
|
||||
defaults["jumphost"] = ""
|
||||
except Exception:
|
||||
defaults = {"host": "", "protocol": "", "port": "", "user": "", "options": "", "logs": "", "tags": "", "password": "", "jumphost": ""}
|
||||
node = {}
|
||||
if edit is None:
|
||||
edit = {"host": True, "protocol": True, "port": True, "user": True, "password": True, "options": True, "logs": True, "tags": True, "jumphost": True}
|
||||
questions = []
|
||||
if edit["host"]:
|
||||
questions.append(inquirer.Text("host", message="Add Hostname or IP", validate=self.validators.host_validation, default=defaults["host"]))
|
||||
else:
|
||||
node["host"] = defaults["host"]
|
||||
if edit["protocol"]:
|
||||
questions.append(inquirer.Text("protocol", message="Select Protocol/app", validate=self.validators.protocol_validation, default=defaults["protocol"]))
|
||||
else:
|
||||
node["protocol"] = defaults["protocol"]
|
||||
if edit["port"]:
|
||||
questions.append(inquirer.Text("port", message="Select Port Number", validate=self.validators.port_validation, default=defaults["port"]))
|
||||
else:
|
||||
node["port"] = defaults["port"]
|
||||
if edit["options"]:
|
||||
questions.append(inquirer.Text("options", message="Pass extra options to protocol/app", validate=self.validators.default_validation, default=defaults["options"]))
|
||||
else:
|
||||
node["options"] = defaults["options"]
|
||||
if edit["logs"]:
|
||||
questions.append(inquirer.Text("logs", message="Pick logging path/file ", validate=self.validators.default_validation, default=defaults["logs"].replace("{", "{{").replace("}", "}}")))
|
||||
else:
|
||||
node["logs"] = defaults["logs"]
|
||||
if edit["tags"]:
|
||||
questions.append(inquirer.Text("tags", message="Add tags dictionary", validate=self.validators.tags_validation, default=str(defaults["tags"]).replace("{", "{{").replace("}", "}}")))
|
||||
else:
|
||||
node["tags"] = defaults["tags"]
|
||||
if edit["jumphost"]:
|
||||
questions.append(inquirer.Text("jumphost", message="Add Jumphost node", validate=self.validators.jumphost_validation, default=str(defaults["jumphost"]).replace("{", "{{").replace("}", "}}")))
|
||||
else:
|
||||
node["jumphost"] = defaults["jumphost"]
|
||||
if edit["user"]:
|
||||
questions.append(inquirer.Text("user", message="Pick username", validate=self.validators.default_validation, default=defaults["user"]))
|
||||
else:
|
||||
node["user"] = defaults["user"]
|
||||
if edit["password"]:
|
||||
questions.append(inquirer.List("password", message="Password: Use a local password, no password or a list of profiles to reference?", choices=["Local Password", "Profiles", "No Password"]))
|
||||
else:
|
||||
node["password"] = defaults["password"]
|
||||
|
||||
answer = inquirer.prompt(questions)
|
||||
if answer is None:
|
||||
return False
|
||||
|
||||
if "password" in answer:
|
||||
if answer["password"] == "Local Password":
|
||||
passq = [inquirer.Password("password", message="Set Password")]
|
||||
passa = inquirer.prompt(passq)
|
||||
if passa is None:
|
||||
return False
|
||||
answer["password"] = self.app.services.config_svc.encrypt_password(passa["password"])
|
||||
elif answer["password"] == "Profiles":
|
||||
passq = [(inquirer.Text("password", message="Set a @profile or a comma separated list of @profiles", validate=self.validators.pass_validation))]
|
||||
passa = inquirer.prompt(passq)
|
||||
if passa is None:
|
||||
return False
|
||||
answer["password"] = passa["password"].split(",")
|
||||
elif answer["password"] == "No Password":
|
||||
answer["password"] = ""
|
||||
|
||||
if "tags" in answer and not answer["tags"].startswith("@") and answer["tags"]:
|
||||
answer["tags"] = ast.literal_eval(answer["tags"])
|
||||
|
||||
result = {**uniques, **answer, **node}
|
||||
result["type"] = "connection"
|
||||
return result</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.forms.Forms.questions_profiles"><code class="name flex">
|
||||
<span>def <span class="ident">questions_profiles</span></span>(<span>self, unique, edit=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def questions_profiles(self, unique, edit=None):
|
||||
try:
|
||||
defaults = self.app.services.profiles.get_profile(unique, resolve=False)
|
||||
if "tags" not in defaults:
|
||||
defaults["tags"] = ""
|
||||
if "jumphost" not in defaults:
|
||||
defaults["jumphost"] = ""
|
||||
except Exception:
|
||||
defaults = {"host": "", "protocol": "", "port": "", "user": "", "options": "", "logs": "", "tags": "", "jumphost": ""}
|
||||
profile = {}
|
||||
if edit is None:
|
||||
edit = {"host": True, "protocol": True, "port": True, "user": True, "password": True, "options": True, "logs": True, "tags": True, "jumphost": True}
|
||||
questions = []
|
||||
if edit["host"]:
|
||||
questions.append(inquirer.Text("host", message="Add Hostname or IP", default=defaults["host"]))
|
||||
else:
|
||||
profile["host"] = defaults["host"]
|
||||
if edit["protocol"]:
|
||||
questions.append(inquirer.Text("protocol", message="Select Protocol/app", validate=self.validators.profile_protocol_validation, default=defaults["protocol"]))
|
||||
else:
|
||||
profile["protocol"] = defaults["protocol"]
|
||||
if edit["port"]:
|
||||
questions.append(inquirer.Text("port", message="Select Port Number", validate=self.validators.profile_port_validation, default=defaults["port"]))
|
||||
else:
|
||||
profile["port"] = defaults["port"]
|
||||
if edit["options"]:
|
||||
questions.append(inquirer.Text("options", message="Pass extra options to protocol/app", default=defaults["options"]))
|
||||
else:
|
||||
profile["options"] = defaults["options"]
|
||||
if edit["logs"]:
|
||||
questions.append(inquirer.Text("logs", message="Pick logging path/file ", default=defaults["logs"].replace("{", "{{").replace("}", "}}")))
|
||||
else:
|
||||
profile["logs"] = defaults["logs"]
|
||||
if edit["tags"]:
|
||||
questions.append(inquirer.Text("tags", message="Add tags dictionary", validate=self.validators.profile_tags_validation, default=str(defaults["tags"]).replace("{", "{{").replace("}", "}}")))
|
||||
else:
|
||||
profile["tags"] = defaults["tags"]
|
||||
if edit["jumphost"]:
|
||||
questions.append(inquirer.Text("jumphost", message="Add Jumphost node", validate=self.validators.profile_jumphost_validation, default=str(defaults["jumphost"]).replace("{", "{{").replace("}", "}}")))
|
||||
else:
|
||||
profile["jumphost"] = defaults["jumphost"]
|
||||
if edit["user"]:
|
||||
questions.append(inquirer.Text("user", message="Pick username", default=defaults["user"]))
|
||||
else:
|
||||
profile["user"] = defaults["user"]
|
||||
if edit["password"]:
|
||||
questions.append(inquirer.Password("password", message="Set Password"))
|
||||
else:
|
||||
profile["password"] = defaults["password"]
|
||||
|
||||
answer = inquirer.prompt(questions)
|
||||
if answer is None:
|
||||
return False
|
||||
|
||||
if "password" in answer:
|
||||
if answer["password"] != "":
|
||||
answer["password"] = self.app.services.config_svc.encrypt_password(answer["password"])
|
||||
|
||||
if "tags" in answer and answer["tags"]:
|
||||
answer["tags"] = ast.literal_eval(answer["tags"])
|
||||
|
||||
result = {**answer, **profile}
|
||||
result["id"] = unique
|
||||
return result</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.cli" href="index.html">connpy.cli</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<h4><code><a title="connpy.cli.forms.Forms" href="#connpy.cli.forms.Forms">Forms</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.cli.forms.Forms.questions_bulk" href="#connpy.cli.forms.Forms.questions_bulk">questions_bulk</a></code></li>
|
||||
<li><code><a title="connpy.cli.forms.Forms.questions_edit" href="#connpy.cli.forms.Forms.questions_edit">questions_edit</a></code></li>
|
||||
<li><code><a title="connpy.cli.forms.Forms.questions_nodes" href="#connpy.cli.forms.Forms.questions_nodes">questions_nodes</a></code></li>
|
||||
<li><code><a title="connpy.cli.forms.Forms.questions_profiles" href="#connpy.cli.forms.Forms.questions_profiles">questions_profiles</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,309 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.cli.help_text 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.cli.help_text</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-functions">Functions</h2>
|
||||
<dl>
|
||||
<dt id="connpy.cli.help_text.get_help"><code class="name flex">
|
||||
<span>def <span class="ident">get_help</span></span>(<span>type, parsers=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def get_help(type, parsers=None):
|
||||
if type == "export":
|
||||
return "Export /path/to/file.yml \[@subfolder1]\[@folder1] \[@subfolderN]\[@folderN]"
|
||||
if type == "import":
|
||||
return "Import /path/to/file.yml"
|
||||
if type == "node":
|
||||
return "node\[@subfolder]\[@folder]\nConnect to specific node or show all matching nodes\n\[@subfolder]\[@folder]\nShow all available connections globally or in specified path"
|
||||
if type == "usage":
|
||||
commands = []
|
||||
for subcommand, subparser in parsers.choices.items():
|
||||
if subparser.description != None:
|
||||
commands.append(subcommand)
|
||||
commands = ",".join(commands)
|
||||
usage_help = f"connpy [-h] [--add | --del | --mod | --show | --debug] [node|folder] [--sftp]\n connpy {{{commands}}} ..."
|
||||
return usage_help
|
||||
return get_instructions(type)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.help_text.get_instructions"><code class="name flex">
|
||||
<span>def <span class="ident">get_instructions</span></span>(<span>type='add')</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def get_instructions(type="add"):
|
||||
if type == "add":
|
||||
return """
|
||||
Welcome to Connpy node Addition Wizard!
|
||||
|
||||
Here are some important instructions and tips for configuring your new node:
|
||||
|
||||
1. **Profiles**:
|
||||
- You can use the configured settings in a profile using `@profilename`.
|
||||
|
||||
2. **Available Protocols and Apps**:
|
||||
- ssh
|
||||
- telnet
|
||||
- kubectl (`kubectl exec`)
|
||||
- docker (`docker exec`)
|
||||
|
||||
3. **Optional Values**:
|
||||
- You can leave any value empty except for the hostname/IP.
|
||||
|
||||
4. **Passwords**:
|
||||
- You can pass one or more passwords using comma-separated `@profiles`.
|
||||
|
||||
5. **Logging**:
|
||||
- You can use the following variables in the logging file name:
|
||||
- `${id}`
|
||||
- `${unique}`
|
||||
- `${host}`
|
||||
- `${port}`
|
||||
- `${user}`
|
||||
- `${protocol}`
|
||||
|
||||
6. **Well-Known Tags**:
|
||||
- `os`: Identified by AI to generate commands based on the operating system.
|
||||
- `screen_length_command`: Used by automation to avoid pagination on different devices (e.g., `terminal length 0` for Cisco devices).
|
||||
- `prompt`: Replaces default app prompt to identify the end of output or where the user can start inputting commands.
|
||||
- `kube_command`: Replaces the default command (`/bin/bash`) for `kubectl exec`.
|
||||
- `docker_command`: Replaces the default command for `docker exec`.
|
||||
"""
|
||||
if type == "bashcompletion":
|
||||
return '''
|
||||
# Bash completion for connpy
|
||||
# Run: eval "$(connpy config --completion bash)"
|
||||
# Or add it to your .bashrc
|
||||
|
||||
_connpy_autocomplete()
|
||||
{
|
||||
local strings
|
||||
strings=$(python3 -m connpy.completion bash ${#COMP_WORDS[@]} "${COMP_WORDS[@]}")
|
||||
|
||||
local IFS=$'\\t'
|
||||
COMPREPLY=( $(compgen -W "$strings" -- "${COMP_WORDS[$COMP_CWORD]}") )
|
||||
}
|
||||
complete -o nosort -F _connpy_autocomplete conn
|
||||
complete -o nosort -F _connpy_autocomplete connpy
|
||||
'''
|
||||
if type == "zshcompletion":
|
||||
return '''
|
||||
# Zsh completion for connpy
|
||||
# Run: eval "$(connpy config --completion zsh)"
|
||||
# Or add it to your .zshrc
|
||||
# Make sure compinit is loaded
|
||||
|
||||
autoload -U compinit && compinit
|
||||
_connpy_autocomplete()
|
||||
{
|
||||
local COMP_WORDS num strings
|
||||
COMP_WORDS=( $words )
|
||||
num=${#COMP_WORDS[@]}
|
||||
if [[ $words =~ '.* $' ]]; then
|
||||
num=$(($num + 1))
|
||||
fi
|
||||
strings=$(python3 -m connpy.completion zsh ${num} ${COMP_WORDS[@]})
|
||||
|
||||
local IFS=$'\\t'
|
||||
compadd "$@" -- ${=strings}
|
||||
}
|
||||
compdef _connpy_autocomplete conn
|
||||
compdef _connpy_autocomplete connpy
|
||||
'''
|
||||
if type == "fzf_wrapper_bash":
|
||||
return '''\n#Here starts bash 0ms fzf wrapper for connpy
|
||||
connpy() {
|
||||
if [ $# -eq 0 ]; then
|
||||
local selected
|
||||
local configdir=$(cat ~/.config/conn/.folder 2>/dev/null || echo ~/.config/conn)
|
||||
if [ -s "$configdir/.fzf_nodes_cache.txt" ]; then
|
||||
selected=$(cat "$configdir/.fzf_nodes_cache.txt" | fzf-tmux -i -d 25%)
|
||||
else
|
||||
command connpy
|
||||
return
|
||||
fi
|
||||
if [ -n "$selected" ]; then
|
||||
command connpy "$selected"
|
||||
fi
|
||||
else
|
||||
command connpy "$@"
|
||||
fi
|
||||
}
|
||||
alias c="connpy"
|
||||
#Here ends bash 0ms fzf wrapper for connpy
|
||||
'''
|
||||
if type == "fzf_wrapper_zsh":
|
||||
return '''\n#Here starts zsh 0ms fzf wrapper for connpy
|
||||
connpy() {
|
||||
if [ $# -eq 0 ]; then
|
||||
local selected
|
||||
local configdir=$(cat ~/.config/conn/.folder 2>/dev/null || echo ~/.config/conn)
|
||||
if [ -s "$configdir/.fzf_nodes_cache.txt" ]; then
|
||||
selected=$(cat "$configdir/.fzf_nodes_cache.txt" | fzf-tmux -i -d 25%)
|
||||
else
|
||||
command connpy
|
||||
return
|
||||
fi
|
||||
if [ -n "$selected" ]; then
|
||||
command connpy "$selected"
|
||||
fi
|
||||
else
|
||||
command connpy "$@"
|
||||
fi
|
||||
}
|
||||
alias c="connpy"
|
||||
#Here ends zsh 0ms fzf wrapper for connpy
|
||||
'''
|
||||
if type == "run":
|
||||
return "node[@subfolder][@folder] commmand to run\nRun the specific command on the node and print output\n/path/to/file.yaml\nUse a yaml file to run an automation script"
|
||||
if type == "generate":
|
||||
return r'''---
|
||||
tasks:
|
||||
- name: "Config"
|
||||
|
||||
action: 'run' #Action can be test or run. Mandatory
|
||||
|
||||
nodes: #List of nodes to work on. Mandatory
|
||||
- 'router1@office' #You can add specific nodes
|
||||
- '@aws' #entire folders or subfolders
|
||||
- '@office': #or filter inside a folder or subfolder
|
||||
- 'router2'
|
||||
- 'router7'
|
||||
|
||||
commands: #List of commands to send, use {name} to pass variables
|
||||
- 'term len 0'
|
||||
- 'conf t'
|
||||
- 'interface {if}'
|
||||
- 'ip address 10.100.100.{id} 255.255.255.255'
|
||||
- '{commit}'
|
||||
- 'end'
|
||||
|
||||
variables: #Variables to use on commands and expected. Optional
|
||||
__global__: #Global variables to use on all nodes, fallback if missing in the node.
|
||||
commit: ''
|
||||
if: 'loopback100'
|
||||
router1@office:
|
||||
id: 1
|
||||
router2@office:
|
||||
id: 2
|
||||
commit: 'commit'
|
||||
router3@office:
|
||||
id: 3
|
||||
vrouter1@aws:
|
||||
id: 4
|
||||
vrouterN@aws:
|
||||
id: 5
|
||||
|
||||
output: /home/user/logs #Type of output, if null you only get Connection and test result. Choices are: null,stdout,/path/to/folder. Folder path only works on 'run' action.
|
||||
|
||||
options:
|
||||
prompt: r'>$|#$|\$$|>.$|#.$|\$.$' #Optional prompt to check on your devices, default should work on most devices.
|
||||
parallel: 10 #Optional number of nodes to run commands on parallel. Default 10.
|
||||
timeout: 20 #Optional time to wait in seconds for prompt, expected or EOF. Default 20.
|
||||
|
||||
- name: "TestConfig"
|
||||
action: 'test'
|
||||
nodes:
|
||||
- 'router1@office'
|
||||
- '@aws'
|
||||
- '@office':
|
||||
- 'router2'
|
||||
- 'router7'
|
||||
commands:
|
||||
- 'ping 10.100.100.{id}'
|
||||
expected: '!' #Expected text to find when running test action. Mandatory for 'test'
|
||||
variables:
|
||||
router1@office:
|
||||
id: 1
|
||||
router2@office:
|
||||
id: 2
|
||||
commit: 'commit'
|
||||
router3@office:
|
||||
id: 3
|
||||
vrouter1@aws:
|
||||
id: 4
|
||||
vrouterN@aws:
|
||||
id: 5
|
||||
output: null
|
||||
...'''
|
||||
return ""</code></pre>
|
||||
</details>
|
||||
<div class="desc"></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.cli" href="index.html">connpy.cli</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-functions">Functions</a></h3>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.cli.help_text.get_help" href="#connpy.cli.help_text.get_help">get_help</a></code></li>
|
||||
<li><code><a title="connpy.cli.help_text.get_instructions" href="#connpy.cli.help_text.get_instructions">get_instructions</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.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,213 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.cli.helpers 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.cli.helpers</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-functions">Functions</h2>
|
||||
<dl>
|
||||
<dt id="connpy.cli.helpers.choose"><code class="name flex">
|
||||
<span>def <span class="ident">choose</span></span>(<span>app, list_, name, action)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def choose(app, list_, name, action):
|
||||
# Generates an inquirer list to pick
|
||||
# Safeguard: Never prompt if running in autocomplete shell
|
||||
if os.environ.get("_ARGCOMPLETE") or os.environ.get("COMP_LINE"):
|
||||
return None
|
||||
|
||||
if FzfPrompt and app.fzf and os.environ.get("_ARGCOMPLETE") is None and os.environ.get("COMP_LINE") is None:
|
||||
fzf_prompt = FzfPrompt(executable_path="fzf-tmux")
|
||||
if not app.case:
|
||||
fzf_prompt = FzfPrompt(executable_path="fzf-tmux -i")
|
||||
answer = fzf_prompt.prompt(list_, fzf_options="-d 25%")
|
||||
if len(answer) == 0:
|
||||
return None
|
||||
else:
|
||||
return answer[0]
|
||||
else:
|
||||
questions = [inquirer.List(name, message="Pick {} to {}:".format(name,action), choices=list_, carousel=True)]
|
||||
answer = inquirer.prompt(questions)
|
||||
if answer == None:
|
||||
return None
|
||||
else:
|
||||
return answer[name]</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.helpers.folders_completer"><code class="name flex">
|
||||
<span>def <span class="ident">folders_completer</span></span>(<span>prefix, parsed_args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def folders_completer(prefix, parsed_args, **kwargs):
|
||||
configdir = get_config_dir()
|
||||
cache_file = os.path.join(configdir, '.folders_cache.txt')
|
||||
if os.path.exists(cache_file):
|
||||
with open(cache_file, "r") as f:
|
||||
return [line.strip() for line in f if line.startswith(prefix)]
|
||||
return []</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.helpers.get_config_dir"><code class="name flex">
|
||||
<span>def <span class="ident">get_config_dir</span></span>(<span>)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def get_config_dir():
|
||||
home = os.path.expanduser("~")
|
||||
defaultdir = os.path.join(home, '.config/conn')
|
||||
pathfile = os.path.join(defaultdir, '.folder')
|
||||
try:
|
||||
with open(pathfile, "r") as f:
|
||||
return f.read().strip()
|
||||
except:
|
||||
return defaultdir</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.helpers.nodes_completer"><code class="name flex">
|
||||
<span>def <span class="ident">nodes_completer</span></span>(<span>prefix, parsed_args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def nodes_completer(prefix, parsed_args, **kwargs):
|
||||
configdir = get_config_dir()
|
||||
cache_file = os.path.join(configdir, '.fzf_nodes_cache.txt')
|
||||
if os.path.exists(cache_file):
|
||||
with open(cache_file, "r") as f:
|
||||
return [line.strip() for line in f if line.startswith(prefix)]
|
||||
return []</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.helpers.profiles_completer"><code class="name flex">
|
||||
<span>def <span class="ident">profiles_completer</span></span>(<span>prefix, parsed_args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def profiles_completer(prefix, parsed_args, **kwargs):
|
||||
configdir = get_config_dir()
|
||||
cache_file = os.path.join(configdir, '.profiles_cache.txt')
|
||||
if os.path.exists(cache_file):
|
||||
with open(cache_file, "r") as f:
|
||||
return [line.strip() for line in f if line.startswith(prefix)]
|
||||
return []</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.helpers.toplevel_completer"><code class="name flex">
|
||||
<span>def <span class="ident">toplevel_completer</span></span>(<span>prefix, parsed_args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def toplevel_completer(prefix, parsed_args, **kwargs):
|
||||
commands = ["node", "profile", "move", "mv", "copy", "cp", "list", "ls", "bulk", "export", "import", "ai", "run", "api", "context", "plugin", "config", "sync"]
|
||||
|
||||
configdir = get_config_dir()
|
||||
cache_file = os.path.join(configdir, '.fzf_nodes_cache.txt')
|
||||
nodes = []
|
||||
if os.path.exists(cache_file):
|
||||
with open(cache_file, "r") as f:
|
||||
nodes = [line.strip() for line in f if line.startswith(prefix)]
|
||||
|
||||
cache_folders = os.path.join(configdir, '.folders_cache.txt')
|
||||
if os.path.exists(cache_folders):
|
||||
with open(cache_folders, "r") as f:
|
||||
nodes += [line.strip() for line in f if line.startswith(prefix)]
|
||||
|
||||
return [c for c in commands + nodes if c.startswith(prefix)]</code></pre>
|
||||
</details>
|
||||
<div class="desc"></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.cli" href="index.html">connpy.cli</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-functions">Functions</a></h3>
|
||||
<ul class="two-column">
|
||||
<li><code><a title="connpy.cli.helpers.choose" href="#connpy.cli.helpers.choose">choose</a></code></li>
|
||||
<li><code><a title="connpy.cli.helpers.folders_completer" href="#connpy.cli.helpers.folders_completer">folders_completer</a></code></li>
|
||||
<li><code><a title="connpy.cli.helpers.get_config_dir" href="#connpy.cli.helpers.get_config_dir">get_config_dir</a></code></li>
|
||||
<li><code><a title="connpy.cli.helpers.nodes_completer" href="#connpy.cli.helpers.nodes_completer">nodes_completer</a></code></li>
|
||||
<li><code><a title="connpy.cli.helpers.profiles_completer" href="#connpy.cli.helpers.profiles_completer">profiles_completer</a></code></li>
|
||||
<li><code><a title="connpy.cli.helpers.toplevel_completer" href="#connpy.cli.helpers.toplevel_completer">toplevel_completer</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.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,278 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.cli.import_export_handler 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.cli.import_export_handler</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
||||
<dl>
|
||||
<dt id="connpy.cli.import_export_handler.ImportExportHandler"><code class="flex name class">
|
||||
<span>class <span class="ident">ImportExportHandler</span></span>
|
||||
<span>(</span><span>app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class ImportExportHandler:
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
self.forms = Forms(app)
|
||||
|
||||
def dispatch_import(self, args):
|
||||
file_path = args.data[0]
|
||||
try:
|
||||
printer.warning("This could overwrite your current configuration!")
|
||||
question = [inquirer.Confirm("import", message=f"Are you sure you want to import {file_path}?")]
|
||||
confirm = inquirer.prompt(question)
|
||||
if confirm == None or not confirm["import"]:
|
||||
sys.exit(7)
|
||||
|
||||
self.app.services.import_export.import_from_file(file_path)
|
||||
printer.success(f"File {file_path} imported successfully.")
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(1)
|
||||
|
||||
def dispatch_export(self, args):
|
||||
file_path = args.data[0]
|
||||
folders = args.data[1:] if len(args.data) > 1 else None
|
||||
try:
|
||||
self.app.services.import_export.export_to_file(file_path, folders=folders)
|
||||
printer.success(f"File {file_path} generated successfully")
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(1)
|
||||
sys.exit()
|
||||
|
||||
def bulk(self, args):
|
||||
if args.file and os.path.isfile(args.file[0]):
|
||||
with open(args.file[0], 'r') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
# Expecting exactly 2 lines
|
||||
if len(lines) < 2:
|
||||
printer.error("The file must contain at least two lines: one for nodes, one for hosts.")
|
||||
sys.exit(11)
|
||||
|
||||
nodes = lines[0].strip()
|
||||
hosts = lines[1].strip()
|
||||
newnodes = self.forms.questions_bulk(nodes, hosts)
|
||||
else:
|
||||
newnodes = self.forms.questions_bulk()
|
||||
|
||||
if newnodes == False:
|
||||
sys.exit(7)
|
||||
|
||||
if not self.app.case:
|
||||
newnodes["location"] = newnodes["location"].lower()
|
||||
newnodes["ids"] = newnodes["ids"].lower()
|
||||
|
||||
# Handle the case where location might be a file reference (e.g. from a prompt)
|
||||
location = newnodes["location"]
|
||||
if location.startswith("@") and "/" in location:
|
||||
# Extract the actual @folder part (e.g. @testall from @testall/.folders_cache.txt)
|
||||
location = location.split("/")[0]
|
||||
newnodes["location"] = location
|
||||
|
||||
ids = newnodes["ids"].split(",")
|
||||
# Append location to each id for proper folder assignment
|
||||
location = newnodes["location"]
|
||||
if location:
|
||||
ids = [f"{i}{location}" for i in ids]
|
||||
|
||||
hosts = newnodes["host"].split(",")
|
||||
|
||||
try:
|
||||
count = self.app.services.nodes.bulk_add(ids, hosts, newnodes)
|
||||
if count > 0:
|
||||
printer.success(f"Successfully added {count} nodes.")
|
||||
else:
|
||||
printer.info("0 nodes added")
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(1)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
<h3>Methods</h3>
|
||||
<dl>
|
||||
<dt id="connpy.cli.import_export_handler.ImportExportHandler.bulk"><code class="name flex">
|
||||
<span>def <span class="ident">bulk</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def bulk(self, args):
|
||||
if args.file and os.path.isfile(args.file[0]):
|
||||
with open(args.file[0], 'r') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
# Expecting exactly 2 lines
|
||||
if len(lines) < 2:
|
||||
printer.error("The file must contain at least two lines: one for nodes, one for hosts.")
|
||||
sys.exit(11)
|
||||
|
||||
nodes = lines[0].strip()
|
||||
hosts = lines[1].strip()
|
||||
newnodes = self.forms.questions_bulk(nodes, hosts)
|
||||
else:
|
||||
newnodes = self.forms.questions_bulk()
|
||||
|
||||
if newnodes == False:
|
||||
sys.exit(7)
|
||||
|
||||
if not self.app.case:
|
||||
newnodes["location"] = newnodes["location"].lower()
|
||||
newnodes["ids"] = newnodes["ids"].lower()
|
||||
|
||||
# Handle the case where location might be a file reference (e.g. from a prompt)
|
||||
location = newnodes["location"]
|
||||
if location.startswith("@") and "/" in location:
|
||||
# Extract the actual @folder part (e.g. @testall from @testall/.folders_cache.txt)
|
||||
location = location.split("/")[0]
|
||||
newnodes["location"] = location
|
||||
|
||||
ids = newnodes["ids"].split(",")
|
||||
# Append location to each id for proper folder assignment
|
||||
location = newnodes["location"]
|
||||
if location:
|
||||
ids = [f"{i}{location}" for i in ids]
|
||||
|
||||
hosts = newnodes["host"].split(",")
|
||||
|
||||
try:
|
||||
count = self.app.services.nodes.bulk_add(ids, hosts, newnodes)
|
||||
if count > 0:
|
||||
printer.success(f"Successfully added {count} nodes.")
|
||||
else:
|
||||
printer.info("0 nodes added")
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(1)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.import_export_handler.ImportExportHandler.dispatch_export"><code class="name flex">
|
||||
<span>def <span class="ident">dispatch_export</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def dispatch_export(self, args):
|
||||
file_path = args.data[0]
|
||||
folders = args.data[1:] if len(args.data) > 1 else None
|
||||
try:
|
||||
self.app.services.import_export.export_to_file(file_path, folders=folders)
|
||||
printer.success(f"File {file_path} generated successfully")
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(1)
|
||||
sys.exit()</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.import_export_handler.ImportExportHandler.dispatch_import"><code class="name flex">
|
||||
<span>def <span class="ident">dispatch_import</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def dispatch_import(self, args):
|
||||
file_path = args.data[0]
|
||||
try:
|
||||
printer.warning("This could overwrite your current configuration!")
|
||||
question = [inquirer.Confirm("import", message=f"Are you sure you want to import {file_path}?")]
|
||||
confirm = inquirer.prompt(question)
|
||||
if confirm == None or not confirm["import"]:
|
||||
sys.exit(7)
|
||||
|
||||
self.app.services.import_export.import_from_file(file_path)
|
||||
printer.success(f"File {file_path} imported successfully.")
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(1)</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.cli" href="index.html">connpy.cli</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<h4><code><a title="connpy.cli.import_export_handler.ImportExportHandler" href="#connpy.cli.import_export_handler.ImportExportHandler">ImportExportHandler</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.cli.import_export_handler.ImportExportHandler.bulk" href="#connpy.cli.import_export_handler.ImportExportHandler.bulk">bulk</a></code></li>
|
||||
<li><code><a title="connpy.cli.import_export_handler.ImportExportHandler.dispatch_export" href="#connpy.cli.import_export_handler.ImportExportHandler.dispatch_export">dispatch_export</a></code></li>
|
||||
<li><code><a title="connpy.cli.import_export_handler.ImportExportHandler.dispatch_import" href="#connpy.cli.import_export_handler.ImportExportHandler.dispatch_import">dispatch_import</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,143 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.cli 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.cli</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.cli.ai_handler" href="ai_handler.html">connpy.cli.ai_handler</a></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt><code class="name"><a title="connpy.cli.api_handler" href="api_handler.html">connpy.cli.api_handler</a></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt><code class="name"><a title="connpy.cli.config_handler" href="config_handler.html">connpy.cli.config_handler</a></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt><code class="name"><a title="connpy.cli.context_handler" href="context_handler.html">connpy.cli.context_handler</a></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt><code class="name"><a title="connpy.cli.forms" href="forms.html">connpy.cli.forms</a></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt><code class="name"><a title="connpy.cli.help_text" href="help_text.html">connpy.cli.help_text</a></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt><code class="name"><a title="connpy.cli.helpers" href="helpers.html">connpy.cli.helpers</a></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt><code class="name"><a title="connpy.cli.import_export_handler" href="import_export_handler.html">connpy.cli.import_export_handler</a></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt><code class="name"><a title="connpy.cli.node_handler" href="node_handler.html">connpy.cli.node_handler</a></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt><code class="name"><a title="connpy.cli.plugin_handler" href="plugin_handler.html">connpy.cli.plugin_handler</a></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt><code class="name"><a title="connpy.cli.profile_handler" href="profile_handler.html">connpy.cli.profile_handler</a></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt><code class="name"><a title="connpy.cli.run_handler" href="run_handler.html">connpy.cli.run_handler</a></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt><code class="name"><a title="connpy.cli.sync_handler" href="sync_handler.html">connpy.cli.sync_handler</a></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt><code class="name"><a title="connpy.cli.validators" href="validators.html">connpy.cli.validators</a></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></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.cli.ai_handler" href="ai_handler.html">connpy.cli.ai_handler</a></code></li>
|
||||
<li><code><a title="connpy.cli.api_handler" href="api_handler.html">connpy.cli.api_handler</a></code></li>
|
||||
<li><code><a title="connpy.cli.config_handler" href="config_handler.html">connpy.cli.config_handler</a></code></li>
|
||||
<li><code><a title="connpy.cli.context_handler" href="context_handler.html">connpy.cli.context_handler</a></code></li>
|
||||
<li><code><a title="connpy.cli.forms" href="forms.html">connpy.cli.forms</a></code></li>
|
||||
<li><code><a title="connpy.cli.help_text" href="help_text.html">connpy.cli.help_text</a></code></li>
|
||||
<li><code><a title="connpy.cli.helpers" href="helpers.html">connpy.cli.helpers</a></code></li>
|
||||
<li><code><a title="connpy.cli.import_export_handler" href="import_export_handler.html">connpy.cli.import_export_handler</a></code></li>
|
||||
<li><code><a title="connpy.cli.node_handler" href="node_handler.html">connpy.cli.node_handler</a></code></li>
|
||||
<li><code><a title="connpy.cli.plugin_handler" href="plugin_handler.html">connpy.cli.plugin_handler</a></code></li>
|
||||
<li><code><a title="connpy.cli.profile_handler" href="profile_handler.html">connpy.cli.profile_handler</a></code></li>
|
||||
<li><code><a title="connpy.cli.run_handler" href="run_handler.html">connpy.cli.run_handler</a></code></li>
|
||||
<li><code><a title="connpy.cli.sync_handler" href="sync_handler.html">connpy.cli.sync_handler</a></code></li>
|
||||
<li><code><a title="connpy.cli.validators" href="validators.html">connpy.cli.validators</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.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,604 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.cli.node_handler 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.cli.node_handler</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
||||
<dl>
|
||||
<dt id="connpy.cli.node_handler.NodeHandler"><code class="flex name class">
|
||||
<span>class <span class="ident">NodeHandler</span></span>
|
||||
<span>(</span><span>app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class NodeHandler:
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
self.forms = Forms(app)
|
||||
|
||||
def dispatch(self, args):
|
||||
if not self.app.case and args.data != None:
|
||||
args.data = args.data.lower()
|
||||
actions = {"version": self.version, "connect": self.connect, "add": self.add, "del": self.delete, "mod": self.modify, "show": self.show}
|
||||
return actions.get(args.action)(args)
|
||||
|
||||
def version(self, args):
|
||||
from .._version import __version__
|
||||
printer.info(f"Connpy {__version__}")
|
||||
|
||||
def connect(self, args):
|
||||
if args.data == None:
|
||||
try:
|
||||
matches = self.app.services.nodes.list_nodes()
|
||||
except Exception as e:
|
||||
printer.error(f"Failed to list nodes: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
if len(matches) == 0:
|
||||
printer.warning("There are no nodes created")
|
||||
printer.info("try: connpy --help")
|
||||
sys.exit(9)
|
||||
else:
|
||||
try:
|
||||
matches = self.app.services.nodes.list_nodes(args.data)
|
||||
except Exception:
|
||||
matches = []
|
||||
|
||||
if len(matches) == 0:
|
||||
printer.error(f"{args.data} not found")
|
||||
sys.exit(2)
|
||||
elif len(matches) > 1:
|
||||
matches[0] = choose(self.app, matches, "node", "connect")
|
||||
|
||||
if matches[0] == None:
|
||||
sys.exit(7)
|
||||
|
||||
try:
|
||||
self.app.services.nodes.connect_node(
|
||||
matches[0],
|
||||
sftp=args.sftp,
|
||||
debug=args.debug,
|
||||
logger=self.app._service_logger
|
||||
)
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(1)
|
||||
|
||||
def delete(self, args):
|
||||
if args.data == None:
|
||||
printer.error("Missing argument node")
|
||||
sys.exit(3)
|
||||
|
||||
is_folder = args.data.startswith("@")
|
||||
try:
|
||||
if is_folder:
|
||||
matches = self.app.services.nodes.list_folders(args.data)
|
||||
else:
|
||||
matches = self.app.services.nodes.list_nodes(args.data)
|
||||
except Exception:
|
||||
matches = []
|
||||
|
||||
if len(matches) == 0:
|
||||
printer.error(f"{args.data} not found")
|
||||
sys.exit(2)
|
||||
|
||||
printer.info(f"Removing: {matches}")
|
||||
question = [inquirer.Confirm("delete", message="Are you sure you want to continue?")]
|
||||
confirm = inquirer.prompt(question)
|
||||
if confirm == None or not confirm["delete"]:
|
||||
sys.exit(7)
|
||||
|
||||
try:
|
||||
for item in matches:
|
||||
self.app.services.nodes.delete_node(item, is_folder=is_folder)
|
||||
|
||||
if len(matches) == 1:
|
||||
printer.success(f"{matches[0]} deleted successfully")
|
||||
else:
|
||||
printer.success(f"{len(matches)} items deleted successfully")
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(1)
|
||||
|
||||
def add(self, args):
|
||||
try:
|
||||
args.data = self.app._type_node(args.data)
|
||||
except ValueError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(3)
|
||||
|
||||
if args.data == None:
|
||||
printer.error("Missing argument node")
|
||||
sys.exit(3)
|
||||
|
||||
is_folder = args.data.startswith("@")
|
||||
try:
|
||||
if is_folder:
|
||||
uniques = self.app.services.nodes.explode_unique(args.data)
|
||||
if not uniques:
|
||||
raise InvalidConfigurationError(f"Invalid folder {args.data}")
|
||||
self.app.services.nodes.add_node(args.data, {}, is_folder=True)
|
||||
printer.success(f"{args.data} added successfully")
|
||||
else:
|
||||
if args.data in self.app.nodes_list:
|
||||
printer.error(f"Node '{args.data}' already exists.")
|
||||
sys.exit(1)
|
||||
uniques = self.app.services.nodes.explode_unique(args.data)
|
||||
printer.console.print(Markdown(get_instructions()))
|
||||
|
||||
new_node_data = self.forms.questions_nodes(args.data, uniques)
|
||||
if not new_node_data:
|
||||
sys.exit(7)
|
||||
self.app.services.nodes.add_node(args.data, new_node_data)
|
||||
printer.success(f"{args.data} added successfully")
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(1)
|
||||
|
||||
def show(self, args):
|
||||
if args.data == None:
|
||||
printer.error("Missing argument node")
|
||||
sys.exit(3)
|
||||
|
||||
try:
|
||||
matches = self.app.services.nodes.list_nodes(args.data)
|
||||
except Exception:
|
||||
matches = []
|
||||
|
||||
if len(matches) == 0:
|
||||
printer.error(f"{args.data} not found")
|
||||
sys.exit(2)
|
||||
elif len(matches) > 1:
|
||||
matches[0] = choose(self.app, matches, "node", "show")
|
||||
|
||||
if matches[0] == None:
|
||||
sys.exit(7)
|
||||
|
||||
try:
|
||||
node = self.app.services.nodes.get_node_details(matches[0])
|
||||
yaml_output = yaml.dump(node, sort_keys=False, default_flow_style=False)
|
||||
printer.data(matches[0], yaml_output)
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(1)
|
||||
|
||||
def modify(self, args):
|
||||
if args.data == None:
|
||||
printer.error("Missing argument node")
|
||||
sys.exit(3)
|
||||
|
||||
try:
|
||||
matches = self.app.services.nodes.list_nodes(args.data)
|
||||
except Exception:
|
||||
matches = []
|
||||
|
||||
if len(matches) == 0:
|
||||
printer.error(f"No connection found with filter: {args.data}")
|
||||
sys.exit(2)
|
||||
|
||||
unique = matches[0] if len(matches) == 1 else None
|
||||
uniques = self.app.services.nodes.explode_unique(unique) if unique else {"id": None, "folder": None}
|
||||
|
||||
printer.info(f"Editing: {matches}")
|
||||
node_details = {}
|
||||
for i in matches:
|
||||
node_details[i] = self.app.services.nodes.get_node_details(i)
|
||||
|
||||
edits = self.forms.questions_edit()
|
||||
if edits == None:
|
||||
sys.exit(7)
|
||||
|
||||
# Use first match as base for defaults if multiple matches exist
|
||||
base_unique = matches[0]
|
||||
base_uniques = self.app.services.nodes.explode_unique(base_unique)
|
||||
updatenode = self.forms.questions_nodes(base_unique, base_uniques, edit=edits)
|
||||
if not updatenode:
|
||||
sys.exit(7)
|
||||
|
||||
try:
|
||||
if len(matches) == 1:
|
||||
# Comparison for "Nothing to do"
|
||||
current = node_details[matches[0]].copy()
|
||||
current.update(uniques)
|
||||
current["type"] = "connection"
|
||||
if sorted(updatenode.items()) == sorted(current.items()):
|
||||
printer.info("Nothing to do here")
|
||||
return
|
||||
self.app.services.nodes.update_node(matches[0], updatenode)
|
||||
printer.success(f"{args.data} edited successfully")
|
||||
else:
|
||||
editcount = 0
|
||||
for k in matches:
|
||||
updated_item = self.app.services.nodes.explode_unique(k)
|
||||
updated_item["type"] = "connection"
|
||||
updated_item.update(node_details[k])
|
||||
|
||||
this_item_changed = False
|
||||
for key, should_edit in edits.items():
|
||||
if should_edit:
|
||||
this_item_changed = True
|
||||
updated_item[key] = updatenode[key]
|
||||
|
||||
if this_item_changed:
|
||||
editcount += 1
|
||||
self.app.services.nodes.update_node(k, updated_item)
|
||||
|
||||
if editcount == 0:
|
||||
printer.info("Nothing to do here")
|
||||
else:
|
||||
printer.success(f"{matches} edited successfully")
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(1)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
<h3>Methods</h3>
|
||||
<dl>
|
||||
<dt id="connpy.cli.node_handler.NodeHandler.add"><code class="name flex">
|
||||
<span>def <span class="ident">add</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def add(self, args):
|
||||
try:
|
||||
args.data = self.app._type_node(args.data)
|
||||
except ValueError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(3)
|
||||
|
||||
if args.data == None:
|
||||
printer.error("Missing argument node")
|
||||
sys.exit(3)
|
||||
|
||||
is_folder = args.data.startswith("@")
|
||||
try:
|
||||
if is_folder:
|
||||
uniques = self.app.services.nodes.explode_unique(args.data)
|
||||
if not uniques:
|
||||
raise InvalidConfigurationError(f"Invalid folder {args.data}")
|
||||
self.app.services.nodes.add_node(args.data, {}, is_folder=True)
|
||||
printer.success(f"{args.data} added successfully")
|
||||
else:
|
||||
if args.data in self.app.nodes_list:
|
||||
printer.error(f"Node '{args.data}' already exists.")
|
||||
sys.exit(1)
|
||||
uniques = self.app.services.nodes.explode_unique(args.data)
|
||||
printer.console.print(Markdown(get_instructions()))
|
||||
|
||||
new_node_data = self.forms.questions_nodes(args.data, uniques)
|
||||
if not new_node_data:
|
||||
sys.exit(7)
|
||||
self.app.services.nodes.add_node(args.data, new_node_data)
|
||||
printer.success(f"{args.data} added successfully")
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(1)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.node_handler.NodeHandler.connect"><code class="name flex">
|
||||
<span>def <span class="ident">connect</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def connect(self, args):
|
||||
if args.data == None:
|
||||
try:
|
||||
matches = self.app.services.nodes.list_nodes()
|
||||
except Exception as e:
|
||||
printer.error(f"Failed to list nodes: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
if len(matches) == 0:
|
||||
printer.warning("There are no nodes created")
|
||||
printer.info("try: connpy --help")
|
||||
sys.exit(9)
|
||||
else:
|
||||
try:
|
||||
matches = self.app.services.nodes.list_nodes(args.data)
|
||||
except Exception:
|
||||
matches = []
|
||||
|
||||
if len(matches) == 0:
|
||||
printer.error(f"{args.data} not found")
|
||||
sys.exit(2)
|
||||
elif len(matches) > 1:
|
||||
matches[0] = choose(self.app, matches, "node", "connect")
|
||||
|
||||
if matches[0] == None:
|
||||
sys.exit(7)
|
||||
|
||||
try:
|
||||
self.app.services.nodes.connect_node(
|
||||
matches[0],
|
||||
sftp=args.sftp,
|
||||
debug=args.debug,
|
||||
logger=self.app._service_logger
|
||||
)
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(1)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.node_handler.NodeHandler.delete"><code class="name flex">
|
||||
<span>def <span class="ident">delete</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def delete(self, args):
|
||||
if args.data == None:
|
||||
printer.error("Missing argument node")
|
||||
sys.exit(3)
|
||||
|
||||
is_folder = args.data.startswith("@")
|
||||
try:
|
||||
if is_folder:
|
||||
matches = self.app.services.nodes.list_folders(args.data)
|
||||
else:
|
||||
matches = self.app.services.nodes.list_nodes(args.data)
|
||||
except Exception:
|
||||
matches = []
|
||||
|
||||
if len(matches) == 0:
|
||||
printer.error(f"{args.data} not found")
|
||||
sys.exit(2)
|
||||
|
||||
printer.info(f"Removing: {matches}")
|
||||
question = [inquirer.Confirm("delete", message="Are you sure you want to continue?")]
|
||||
confirm = inquirer.prompt(question)
|
||||
if confirm == None or not confirm["delete"]:
|
||||
sys.exit(7)
|
||||
|
||||
try:
|
||||
for item in matches:
|
||||
self.app.services.nodes.delete_node(item, is_folder=is_folder)
|
||||
|
||||
if len(matches) == 1:
|
||||
printer.success(f"{matches[0]} deleted successfully")
|
||||
else:
|
||||
printer.success(f"{len(matches)} items deleted successfully")
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(1)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.node_handler.NodeHandler.dispatch"><code class="name flex">
|
||||
<span>def <span class="ident">dispatch</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def dispatch(self, args):
|
||||
if not self.app.case and args.data != None:
|
||||
args.data = args.data.lower()
|
||||
actions = {"version": self.version, "connect": self.connect, "add": self.add, "del": self.delete, "mod": self.modify, "show": self.show}
|
||||
return actions.get(args.action)(args)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.node_handler.NodeHandler.modify"><code class="name flex">
|
||||
<span>def <span class="ident">modify</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def modify(self, args):
|
||||
if args.data == None:
|
||||
printer.error("Missing argument node")
|
||||
sys.exit(3)
|
||||
|
||||
try:
|
||||
matches = self.app.services.nodes.list_nodes(args.data)
|
||||
except Exception:
|
||||
matches = []
|
||||
|
||||
if len(matches) == 0:
|
||||
printer.error(f"No connection found with filter: {args.data}")
|
||||
sys.exit(2)
|
||||
|
||||
unique = matches[0] if len(matches) == 1 else None
|
||||
uniques = self.app.services.nodes.explode_unique(unique) if unique else {"id": None, "folder": None}
|
||||
|
||||
printer.info(f"Editing: {matches}")
|
||||
node_details = {}
|
||||
for i in matches:
|
||||
node_details[i] = self.app.services.nodes.get_node_details(i)
|
||||
|
||||
edits = self.forms.questions_edit()
|
||||
if edits == None:
|
||||
sys.exit(7)
|
||||
|
||||
# Use first match as base for defaults if multiple matches exist
|
||||
base_unique = matches[0]
|
||||
base_uniques = self.app.services.nodes.explode_unique(base_unique)
|
||||
updatenode = self.forms.questions_nodes(base_unique, base_uniques, edit=edits)
|
||||
if not updatenode:
|
||||
sys.exit(7)
|
||||
|
||||
try:
|
||||
if len(matches) == 1:
|
||||
# Comparison for "Nothing to do"
|
||||
current = node_details[matches[0]].copy()
|
||||
current.update(uniques)
|
||||
current["type"] = "connection"
|
||||
if sorted(updatenode.items()) == sorted(current.items()):
|
||||
printer.info("Nothing to do here")
|
||||
return
|
||||
self.app.services.nodes.update_node(matches[0], updatenode)
|
||||
printer.success(f"{args.data} edited successfully")
|
||||
else:
|
||||
editcount = 0
|
||||
for k in matches:
|
||||
updated_item = self.app.services.nodes.explode_unique(k)
|
||||
updated_item["type"] = "connection"
|
||||
updated_item.update(node_details[k])
|
||||
|
||||
this_item_changed = False
|
||||
for key, should_edit in edits.items():
|
||||
if should_edit:
|
||||
this_item_changed = True
|
||||
updated_item[key] = updatenode[key]
|
||||
|
||||
if this_item_changed:
|
||||
editcount += 1
|
||||
self.app.services.nodes.update_node(k, updated_item)
|
||||
|
||||
if editcount == 0:
|
||||
printer.info("Nothing to do here")
|
||||
else:
|
||||
printer.success(f"{matches} edited successfully")
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(1)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.node_handler.NodeHandler.show"><code class="name flex">
|
||||
<span>def <span class="ident">show</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def show(self, args):
|
||||
if args.data == None:
|
||||
printer.error("Missing argument node")
|
||||
sys.exit(3)
|
||||
|
||||
try:
|
||||
matches = self.app.services.nodes.list_nodes(args.data)
|
||||
except Exception:
|
||||
matches = []
|
||||
|
||||
if len(matches) == 0:
|
||||
printer.error(f"{args.data} not found")
|
||||
sys.exit(2)
|
||||
elif len(matches) > 1:
|
||||
matches[0] = choose(self.app, matches, "node", "show")
|
||||
|
||||
if matches[0] == None:
|
||||
sys.exit(7)
|
||||
|
||||
try:
|
||||
node = self.app.services.nodes.get_node_details(matches[0])
|
||||
yaml_output = yaml.dump(node, sort_keys=False, default_flow_style=False)
|
||||
printer.data(matches[0], yaml_output)
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(1)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.node_handler.NodeHandler.version"><code class="name flex">
|
||||
<span>def <span class="ident">version</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def version(self, args):
|
||||
from .._version import __version__
|
||||
printer.info(f"Connpy {__version__}")</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.cli" href="index.html">connpy.cli</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<h4><code><a title="connpy.cli.node_handler.NodeHandler" href="#connpy.cli.node_handler.NodeHandler">NodeHandler</a></code></h4>
|
||||
<ul class="two-column">
|
||||
<li><code><a title="connpy.cli.node_handler.NodeHandler.add" href="#connpy.cli.node_handler.NodeHandler.add">add</a></code></li>
|
||||
<li><code><a title="connpy.cli.node_handler.NodeHandler.connect" href="#connpy.cli.node_handler.NodeHandler.connect">connect</a></code></li>
|
||||
<li><code><a title="connpy.cli.node_handler.NodeHandler.delete" href="#connpy.cli.node_handler.NodeHandler.delete">delete</a></code></li>
|
||||
<li><code><a title="connpy.cli.node_handler.NodeHandler.dispatch" href="#connpy.cli.node_handler.NodeHandler.dispatch">dispatch</a></code></li>
|
||||
<li><code><a title="connpy.cli.node_handler.NodeHandler.modify" href="#connpy.cli.node_handler.NodeHandler.modify">modify</a></code></li>
|
||||
<li><code><a title="connpy.cli.node_handler.NodeHandler.show" href="#connpy.cli.node_handler.NodeHandler.show">show</a></code></li>
|
||||
<li><code><a title="connpy.cli.node_handler.NodeHandler.version" href="#connpy.cli.node_handler.NodeHandler.version">version</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,391 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.cli.plugin_handler 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.cli.plugin_handler</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
||||
<dl>
|
||||
<dt id="connpy.cli.plugin_handler.PluginHandler"><code class="flex name class">
|
||||
<span>class <span class="ident">PluginHandler</span></span>
|
||||
<span>(</span><span>app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class PluginHandler:
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
|
||||
def dispatch(self, args):
|
||||
try:
|
||||
# We determine the target PluginService/PluginStub based on standard 'mode'
|
||||
# But wait, local plugins should go to app.services._init_local version
|
||||
# Or we can just use the provided app.services.plugins and pass the appropriate grpc calls if needed.
|
||||
|
||||
is_remote = getattr(args, "remote", False)
|
||||
if is_remote and self.app.services.mode != "remote":
|
||||
printer.error("Cannot use --remote flag when not running in remote mode.")
|
||||
return
|
||||
|
||||
if args.add:
|
||||
self.app.services.plugins.add_plugin(args.add[0], args.add[1])
|
||||
printer.success(f"Plugin {args.add[0]} added successfully{' remotely' if is_remote else ''}.")
|
||||
elif args.update:
|
||||
self.app.services.plugins.add_plugin(args.update[0], args.update[1], update=True)
|
||||
printer.success(f"Plugin {args.update[0]} updated successfully{' remotely' if is_remote else ''}.")
|
||||
elif args.delete:
|
||||
self.app.services.plugins.delete_plugin(args.delete[0])
|
||||
printer.success(f"Plugin {args.delete[0]} deleted successfully{' remotely' if is_remote else ''}.")
|
||||
elif args.enable:
|
||||
name = args.enable[0]
|
||||
if is_remote:
|
||||
self.app.plugins.preferences[name] = "remote"
|
||||
else:
|
||||
if name in self.app.plugins.preferences:
|
||||
del self.app.plugins.preferences[name]
|
||||
|
||||
self.app.plugins._save_preferences(self.app.services.config_svc.get_default_dir())
|
||||
|
||||
# Always try to enable it locally (remove .bkp) if it exists
|
||||
# regardless of mode, to keep files consistent with "enabled" state
|
||||
try:
|
||||
# We use a local service instance to ensure we touch local files
|
||||
from ..services.plugin_service import PluginService
|
||||
local_svc = PluginService(self.app.services.config)
|
||||
local_svc.enable_plugin(name)
|
||||
except Exception:
|
||||
pass # Ignore if not found locally or already enabled
|
||||
|
||||
if is_remote and self.app.services.mode == "remote":
|
||||
self.app.services.plugins.enable_plugin(name)
|
||||
|
||||
printer.success(f"Plugin {name} enabled successfully{' remotely' if is_remote else ' locally'}.")
|
||||
elif args.disable:
|
||||
name = args.disable[0]
|
||||
success = False
|
||||
if is_remote:
|
||||
if self.app.services.mode == "remote":
|
||||
self.app.services.plugins.disable_plugin(name)
|
||||
success = True
|
||||
else:
|
||||
# Disable locally
|
||||
from ..services.plugin_service import PluginService
|
||||
local_svc = PluginService(self.app.services.config)
|
||||
try:
|
||||
if local_svc.disable_plugin(name):
|
||||
success = True
|
||||
except Exception as e:
|
||||
printer.warning(f"Could not disable local plugin: {e}")
|
||||
|
||||
if success:
|
||||
printer.success(f"Plugin {name} disabled successfully{' remotely' if is_remote else ' locally'}.")
|
||||
|
||||
# If any remote operation was performed, trigger a sync to update local cache immediately
|
||||
if is_remote and self.app.services.mode == "remote":
|
||||
try:
|
||||
import os
|
||||
cache_dir = os.path.join(self.app.services.config_svc.get_default_dir(), "remote_plugins")
|
||||
# We use a dummy subparser choice check bypass by passing force_sync=True
|
||||
# or just letting the hasher handle it.
|
||||
self.app.plugins._import_remote_plugins_to_argparse(
|
||||
self.app.services.plugins,
|
||||
self.app.subparsers, # We'll need to make sure this is available
|
||||
cache_dir,
|
||||
force_sync=True
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
elif getattr(args, "sync", False):
|
||||
# The actual sync logic is performed in connapp.py during init
|
||||
# if the --sync flag is detected in sys.argv
|
||||
printer.success("Remote plugins synchronized successfully.")
|
||||
elif args.list:
|
||||
# We need to fetch both local and remote if in remote mode
|
||||
local_plugins = {}
|
||||
remote_plugins = {}
|
||||
|
||||
# Fetch depending on mode
|
||||
if self.app.services.mode == "remote":
|
||||
# For local we need to instantiate a local plugin service bypassing stub
|
||||
from ..services.plugin_service import PluginService
|
||||
local_svc = PluginService(self.app.services.config)
|
||||
local_plugins = local_svc.list_plugins()
|
||||
remote_plugins = self.app.services.plugins.list_plugins()
|
||||
else:
|
||||
local_plugins = self.app.services.plugins.list_plugins()
|
||||
|
||||
from rich.table import Table
|
||||
|
||||
table = Table(title="Available Plugins", show_header=True, header_style="bold cyan")
|
||||
table.add_column("Plugin", style="cyan")
|
||||
table.add_column("State", style="bold")
|
||||
table.add_column("Origin", style="magenta")
|
||||
|
||||
# Populate local plugins
|
||||
for name, details in local_plugins.items():
|
||||
state = "Disabled" if not details.get("enabled", True) else "Active"
|
||||
color = "red" if state == "Disabled" else "green"
|
||||
|
||||
if self.app.services.mode == "remote" and state == "Active":
|
||||
if self.app.plugins.preferences.get(name) == "remote":
|
||||
state = "Shadowed (Override by Remote)"
|
||||
color = "yellow"
|
||||
|
||||
table.add_row(name, f"[{color}]{state}[/{color}]", "Local")
|
||||
|
||||
# Populate remote plugins
|
||||
if self.app.services.mode == "remote":
|
||||
for name, details in remote_plugins.items():
|
||||
state = "Disabled" if not details.get("enabled", True) else "Active"
|
||||
color = "red" if state == "Disabled" else "green"
|
||||
|
||||
if state == "Active":
|
||||
pref = self.app.plugins.preferences.get(name, "local")
|
||||
# If preference isn't remote and the plugin exists locally, local takes priority
|
||||
if pref != "remote" and name in local_plugins:
|
||||
state = "Shadowed (Override by Local)"
|
||||
color = "yellow"
|
||||
|
||||
table.add_row(name, f"[{color}]{state}[/{color}]", "Remote")
|
||||
|
||||
if not local_plugins and not remote_plugins:
|
||||
printer.console.print(" No plugins found.")
|
||||
else:
|
||||
printer.console.print(table)
|
||||
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(1)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
<h3>Methods</h3>
|
||||
<dl>
|
||||
<dt id="connpy.cli.plugin_handler.PluginHandler.dispatch"><code class="name flex">
|
||||
<span>def <span class="ident">dispatch</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def dispatch(self, args):
|
||||
try:
|
||||
# We determine the target PluginService/PluginStub based on standard 'mode'
|
||||
# But wait, local plugins should go to app.services._init_local version
|
||||
# Or we can just use the provided app.services.plugins and pass the appropriate grpc calls if needed.
|
||||
|
||||
is_remote = getattr(args, "remote", False)
|
||||
if is_remote and self.app.services.mode != "remote":
|
||||
printer.error("Cannot use --remote flag when not running in remote mode.")
|
||||
return
|
||||
|
||||
if args.add:
|
||||
self.app.services.plugins.add_plugin(args.add[0], args.add[1])
|
||||
printer.success(f"Plugin {args.add[0]} added successfully{' remotely' if is_remote else ''}.")
|
||||
elif args.update:
|
||||
self.app.services.plugins.add_plugin(args.update[0], args.update[1], update=True)
|
||||
printer.success(f"Plugin {args.update[0]} updated successfully{' remotely' if is_remote else ''}.")
|
||||
elif args.delete:
|
||||
self.app.services.plugins.delete_plugin(args.delete[0])
|
||||
printer.success(f"Plugin {args.delete[0]} deleted successfully{' remotely' if is_remote else ''}.")
|
||||
elif args.enable:
|
||||
name = args.enable[0]
|
||||
if is_remote:
|
||||
self.app.plugins.preferences[name] = "remote"
|
||||
else:
|
||||
if name in self.app.plugins.preferences:
|
||||
del self.app.plugins.preferences[name]
|
||||
|
||||
self.app.plugins._save_preferences(self.app.services.config_svc.get_default_dir())
|
||||
|
||||
# Always try to enable it locally (remove .bkp) if it exists
|
||||
# regardless of mode, to keep files consistent with "enabled" state
|
||||
try:
|
||||
# We use a local service instance to ensure we touch local files
|
||||
from ..services.plugin_service import PluginService
|
||||
local_svc = PluginService(self.app.services.config)
|
||||
local_svc.enable_plugin(name)
|
||||
except Exception:
|
||||
pass # Ignore if not found locally or already enabled
|
||||
|
||||
if is_remote and self.app.services.mode == "remote":
|
||||
self.app.services.plugins.enable_plugin(name)
|
||||
|
||||
printer.success(f"Plugin {name} enabled successfully{' remotely' if is_remote else ' locally'}.")
|
||||
elif args.disable:
|
||||
name = args.disable[0]
|
||||
success = False
|
||||
if is_remote:
|
||||
if self.app.services.mode == "remote":
|
||||
self.app.services.plugins.disable_plugin(name)
|
||||
success = True
|
||||
else:
|
||||
# Disable locally
|
||||
from ..services.plugin_service import PluginService
|
||||
local_svc = PluginService(self.app.services.config)
|
||||
try:
|
||||
if local_svc.disable_plugin(name):
|
||||
success = True
|
||||
except Exception as e:
|
||||
printer.warning(f"Could not disable local plugin: {e}")
|
||||
|
||||
if success:
|
||||
printer.success(f"Plugin {name} disabled successfully{' remotely' if is_remote else ' locally'}.")
|
||||
|
||||
# If any remote operation was performed, trigger a sync to update local cache immediately
|
||||
if is_remote and self.app.services.mode == "remote":
|
||||
try:
|
||||
import os
|
||||
cache_dir = os.path.join(self.app.services.config_svc.get_default_dir(), "remote_plugins")
|
||||
# We use a dummy subparser choice check bypass by passing force_sync=True
|
||||
# or just letting the hasher handle it.
|
||||
self.app.plugins._import_remote_plugins_to_argparse(
|
||||
self.app.services.plugins,
|
||||
self.app.subparsers, # We'll need to make sure this is available
|
||||
cache_dir,
|
||||
force_sync=True
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
elif getattr(args, "sync", False):
|
||||
# The actual sync logic is performed in connapp.py during init
|
||||
# if the --sync flag is detected in sys.argv
|
||||
printer.success("Remote plugins synchronized successfully.")
|
||||
elif args.list:
|
||||
# We need to fetch both local and remote if in remote mode
|
||||
local_plugins = {}
|
||||
remote_plugins = {}
|
||||
|
||||
# Fetch depending on mode
|
||||
if self.app.services.mode == "remote":
|
||||
# For local we need to instantiate a local plugin service bypassing stub
|
||||
from ..services.plugin_service import PluginService
|
||||
local_svc = PluginService(self.app.services.config)
|
||||
local_plugins = local_svc.list_plugins()
|
||||
remote_plugins = self.app.services.plugins.list_plugins()
|
||||
else:
|
||||
local_plugins = self.app.services.plugins.list_plugins()
|
||||
|
||||
from rich.table import Table
|
||||
|
||||
table = Table(title="Available Plugins", show_header=True, header_style="bold cyan")
|
||||
table.add_column("Plugin", style="cyan")
|
||||
table.add_column("State", style="bold")
|
||||
table.add_column("Origin", style="magenta")
|
||||
|
||||
# Populate local plugins
|
||||
for name, details in local_plugins.items():
|
||||
state = "Disabled" if not details.get("enabled", True) else "Active"
|
||||
color = "red" if state == "Disabled" else "green"
|
||||
|
||||
if self.app.services.mode == "remote" and state == "Active":
|
||||
if self.app.plugins.preferences.get(name) == "remote":
|
||||
state = "Shadowed (Override by Remote)"
|
||||
color = "yellow"
|
||||
|
||||
table.add_row(name, f"[{color}]{state}[/{color}]", "Local")
|
||||
|
||||
# Populate remote plugins
|
||||
if self.app.services.mode == "remote":
|
||||
for name, details in remote_plugins.items():
|
||||
state = "Disabled" if not details.get("enabled", True) else "Active"
|
||||
color = "red" if state == "Disabled" else "green"
|
||||
|
||||
if state == "Active":
|
||||
pref = self.app.plugins.preferences.get(name, "local")
|
||||
# If preference isn't remote and the plugin exists locally, local takes priority
|
||||
if pref != "remote" and name in local_plugins:
|
||||
state = "Shadowed (Override by Local)"
|
||||
color = "yellow"
|
||||
|
||||
table.add_row(name, f"[{color}]{state}[/{color}]", "Remote")
|
||||
|
||||
if not local_plugins and not remote_plugins:
|
||||
printer.console.print(" No plugins found.")
|
||||
else:
|
||||
printer.console.print(table)
|
||||
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(1)</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.cli" href="index.html">connpy.cli</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<h4><code><a title="connpy.cli.plugin_handler.PluginHandler" href="#connpy.cli.plugin_handler.PluginHandler">PluginHandler</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.cli.plugin_handler.PluginHandler.dispatch" href="#connpy.cli.plugin_handler.PluginHandler.dispatch">dispatch</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,320 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.cli.profile_handler 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.cli.profile_handler</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
||||
<dl>
|
||||
<dt id="connpy.cli.profile_handler.ProfileHandler"><code class="flex name class">
|
||||
<span>class <span class="ident">ProfileHandler</span></span>
|
||||
<span>(</span><span>app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class ProfileHandler:
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
self.forms = Forms(app)
|
||||
|
||||
def dispatch(self, args):
|
||||
if not self.app.case:
|
||||
args.data[0] = args.data[0].lower()
|
||||
actions = {"add": self.add, "del": self.delete, "mod": self.modify, "show": self.show}
|
||||
return actions.get(args.action)(args)
|
||||
|
||||
def delete(self, args):
|
||||
name = args.data[0]
|
||||
try:
|
||||
self.app.services.profiles.get_profile(name)
|
||||
except ProfileNotFoundError:
|
||||
printer.error(f"{name} not found")
|
||||
sys.exit(2)
|
||||
|
||||
if name == "default":
|
||||
printer.error("Can't delete default profile")
|
||||
sys.exit(6)
|
||||
|
||||
question = [inquirer.Confirm("delete", message=f"Are you sure you want to delete {name}?")]
|
||||
confirm = inquirer.prompt(question)
|
||||
if confirm == None or not confirm["delete"]:
|
||||
sys.exit(7)
|
||||
|
||||
try:
|
||||
self.app.services.profiles.delete_profile(name)
|
||||
printer.success(f"{name} deleted successfully")
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(8)
|
||||
|
||||
def show(self, args):
|
||||
try:
|
||||
profile = self.app.services.profiles.get_profile(args.data[0])
|
||||
yaml_output = yaml.dump(profile, sort_keys=False, default_flow_style=False)
|
||||
printer.data(args.data[0], yaml_output)
|
||||
except ProfileNotFoundError:
|
||||
printer.error(f"{args.data[0]} not found")
|
||||
sys.exit(2)
|
||||
|
||||
def add(self, args):
|
||||
name = args.data[0]
|
||||
if name in self.app.services.profiles.list_profiles():
|
||||
printer.error(f"Profile '{name}' already exists.")
|
||||
sys.exit(4)
|
||||
|
||||
new_profile_data = self.forms.questions_profiles(name)
|
||||
if not new_profile_data:
|
||||
sys.exit(7)
|
||||
|
||||
try:
|
||||
self.app.services.profiles.add_profile(name, new_profile_data)
|
||||
printer.success(f"{name} added successfully")
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(1)
|
||||
|
||||
def modify(self, args):
|
||||
name = args.data[0]
|
||||
try:
|
||||
profile = self.app.services.profiles.get_profile(name, resolve=False)
|
||||
except ProfileNotFoundError:
|
||||
printer.error(f"Profile '{name}' not found")
|
||||
sys.exit(2)
|
||||
|
||||
old_profile = {"id": name, **profile}
|
||||
edits = self.forms.questions_edit()
|
||||
if edits == None:
|
||||
sys.exit(7)
|
||||
|
||||
update_profile_data = self.forms.questions_profiles(name, edit=edits)
|
||||
if not update_profile_data:
|
||||
sys.exit(7)
|
||||
|
||||
if sorted(update_profile_data.items()) == sorted(old_profile.items()):
|
||||
printer.info("Nothing to do here")
|
||||
return
|
||||
|
||||
try:
|
||||
self.app.services.profiles.update_profile(name, update_profile_data)
|
||||
printer.success(f"{name} edited successfully")
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(1)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
<h3>Methods</h3>
|
||||
<dl>
|
||||
<dt id="connpy.cli.profile_handler.ProfileHandler.add"><code class="name flex">
|
||||
<span>def <span class="ident">add</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def add(self, args):
|
||||
name = args.data[0]
|
||||
if name in self.app.services.profiles.list_profiles():
|
||||
printer.error(f"Profile '{name}' already exists.")
|
||||
sys.exit(4)
|
||||
|
||||
new_profile_data = self.forms.questions_profiles(name)
|
||||
if not new_profile_data:
|
||||
sys.exit(7)
|
||||
|
||||
try:
|
||||
self.app.services.profiles.add_profile(name, new_profile_data)
|
||||
printer.success(f"{name} added successfully")
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(1)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.profile_handler.ProfileHandler.delete"><code class="name flex">
|
||||
<span>def <span class="ident">delete</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def delete(self, args):
|
||||
name = args.data[0]
|
||||
try:
|
||||
self.app.services.profiles.get_profile(name)
|
||||
except ProfileNotFoundError:
|
||||
printer.error(f"{name} not found")
|
||||
sys.exit(2)
|
||||
|
||||
if name == "default":
|
||||
printer.error("Can't delete default profile")
|
||||
sys.exit(6)
|
||||
|
||||
question = [inquirer.Confirm("delete", message=f"Are you sure you want to delete {name}?")]
|
||||
confirm = inquirer.prompt(question)
|
||||
if confirm == None or not confirm["delete"]:
|
||||
sys.exit(7)
|
||||
|
||||
try:
|
||||
self.app.services.profiles.delete_profile(name)
|
||||
printer.success(f"{name} deleted successfully")
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(8)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.profile_handler.ProfileHandler.dispatch"><code class="name flex">
|
||||
<span>def <span class="ident">dispatch</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def dispatch(self, args):
|
||||
if not self.app.case:
|
||||
args.data[0] = args.data[0].lower()
|
||||
actions = {"add": self.add, "del": self.delete, "mod": self.modify, "show": self.show}
|
||||
return actions.get(args.action)(args)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.profile_handler.ProfileHandler.modify"><code class="name flex">
|
||||
<span>def <span class="ident">modify</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def modify(self, args):
|
||||
name = args.data[0]
|
||||
try:
|
||||
profile = self.app.services.profiles.get_profile(name, resolve=False)
|
||||
except ProfileNotFoundError:
|
||||
printer.error(f"Profile '{name}' not found")
|
||||
sys.exit(2)
|
||||
|
||||
old_profile = {"id": name, **profile}
|
||||
edits = self.forms.questions_edit()
|
||||
if edits == None:
|
||||
sys.exit(7)
|
||||
|
||||
update_profile_data = self.forms.questions_profiles(name, edit=edits)
|
||||
if not update_profile_data:
|
||||
sys.exit(7)
|
||||
|
||||
if sorted(update_profile_data.items()) == sorted(old_profile.items()):
|
||||
printer.info("Nothing to do here")
|
||||
return
|
||||
|
||||
try:
|
||||
self.app.services.profiles.update_profile(name, update_profile_data)
|
||||
printer.success(f"{name} edited successfully")
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(1)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.profile_handler.ProfileHandler.show"><code class="name flex">
|
||||
<span>def <span class="ident">show</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def show(self, args):
|
||||
try:
|
||||
profile = self.app.services.profiles.get_profile(args.data[0])
|
||||
yaml_output = yaml.dump(profile, sort_keys=False, default_flow_style=False)
|
||||
printer.data(args.data[0], yaml_output)
|
||||
except ProfileNotFoundError:
|
||||
printer.error(f"{args.data[0]} not found")
|
||||
sys.exit(2)</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.cli" href="index.html">connpy.cli</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<h4><code><a title="connpy.cli.profile_handler.ProfileHandler" href="#connpy.cli.profile_handler.ProfileHandler">ProfileHandler</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.cli.profile_handler.ProfileHandler.add" href="#connpy.cli.profile_handler.ProfileHandler.add">add</a></code></li>
|
||||
<li><code><a title="connpy.cli.profile_handler.ProfileHandler.delete" href="#connpy.cli.profile_handler.ProfileHandler.delete">delete</a></code></li>
|
||||
<li><code><a title="connpy.cli.profile_handler.ProfileHandler.dispatch" href="#connpy.cli.profile_handler.ProfileHandler.dispatch">dispatch</a></code></li>
|
||||
<li><code><a title="connpy.cli.profile_handler.ProfileHandler.modify" href="#connpy.cli.profile_handler.ProfileHandler.modify">modify</a></code></li>
|
||||
<li><code><a title="connpy.cli.profile_handler.ProfileHandler.show" href="#connpy.cli.profile_handler.ProfileHandler.show">show</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,369 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.cli.run_handler 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.cli.run_handler</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
||||
<dl>
|
||||
<dt id="connpy.cli.run_handler.RunHandler"><code class="flex name class">
|
||||
<span>class <span class="ident">RunHandler</span></span>
|
||||
<span>(</span><span>app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class RunHandler:
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
|
||||
def dispatch(self, args):
|
||||
if len(args.data) > 1:
|
||||
args.action = "noderun"
|
||||
actions = {"noderun": self.node_run, "generate": self.yaml_generate, "run": self.yaml_run}
|
||||
return actions.get(args.action)(args)
|
||||
|
||||
def node_run(self, args):
|
||||
nodes_filter = args.data[0]
|
||||
commands = [" ".join(args.data[1:])]
|
||||
|
||||
try:
|
||||
header_printed = False
|
||||
# Inline execution with streaming results
|
||||
def _on_node_complete(unique, node_output, node_status):
|
||||
nonlocal header_printed
|
||||
if not header_printed:
|
||||
printer.console.print(Rule("OUTPUT", style="header"))
|
||||
header_printed = True
|
||||
printer.node_panel(unique, node_output, node_status)
|
||||
|
||||
self.app.services.execution.run_commands(
|
||||
nodes_filter=nodes_filter,
|
||||
commands=commands,
|
||||
on_node_complete=_on_node_complete
|
||||
)
|
||||
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(1)
|
||||
|
||||
def yaml_generate(self, args):
|
||||
if os.path.exists(args.data[0]):
|
||||
printer.error(f"File '{args.data[0]}' already exists.")
|
||||
sys.exit(14)
|
||||
else:
|
||||
with open(args.data[0], "w") as file:
|
||||
file.write(get_instructions("generate"))
|
||||
printer.success(f"File {args.data[0]} generated successfully")
|
||||
sys.exit()
|
||||
|
||||
def yaml_run(self, args):
|
||||
path = args.data[0]
|
||||
try:
|
||||
with open(path, "r") as f:
|
||||
playbook = yaml.load(f, Loader=yaml.FullLoader)
|
||||
|
||||
for task in playbook.get("tasks", []):
|
||||
self.cli_run(task)
|
||||
|
||||
except Exception as e:
|
||||
printer.error(f"Failed to run playbook {path}: {e}")
|
||||
sys.exit(10)
|
||||
|
||||
def cli_run(self, script):
|
||||
try:
|
||||
action = script["action"]
|
||||
nodelist = script["nodes"]
|
||||
commands = script["commands"]
|
||||
variables = script.get("variables")
|
||||
output_cfg = script["output"]
|
||||
name = script.get("name", "Task")
|
||||
options = script.get("options", {})
|
||||
except KeyError as e:
|
||||
printer.error(f"'{e.args[0]}' is mandatory in script")
|
||||
sys.exit(11)
|
||||
|
||||
stdout = (output_cfg == "stdout")
|
||||
folder = output_cfg if output_cfg not in [None, "stdout"] else None
|
||||
prompt = options.get("prompt")
|
||||
printer.header(name.upper())
|
||||
|
||||
try:
|
||||
if action == "run":
|
||||
# If stdout is true, we stream results as they arrive
|
||||
on_complete = printer.node_panel if stdout else None
|
||||
results = self.app.services.execution.run_commands(
|
||||
nodes_filter=nodelist,
|
||||
commands=commands,
|
||||
variables=variables,
|
||||
parallel=options.get("parallel", 10),
|
||||
timeout=options.get("timeout", 10),
|
||||
folder=folder,
|
||||
prompt=prompt,
|
||||
on_node_complete=on_complete
|
||||
)
|
||||
# If not streaming, we could print a summary table here if needed
|
||||
if not stdout:
|
||||
for unique, output in results.items():
|
||||
printer.node_panel(unique, output, 0)
|
||||
|
||||
elif action == "test":
|
||||
expected = script.get("expected", [])
|
||||
on_complete = printer.test_panel if stdout else None
|
||||
results = self.app.services.execution.test_commands(
|
||||
nodes_filter=nodelist,
|
||||
commands=commands,
|
||||
expected=expected,
|
||||
variables=variables,
|
||||
parallel=options.get("parallel", 10),
|
||||
timeout=options.get("timeout", 10),
|
||||
prompt=prompt,
|
||||
on_node_complete=on_complete
|
||||
)
|
||||
if not stdout:
|
||||
printer.test_summary(results)
|
||||
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
<h3>Methods</h3>
|
||||
<dl>
|
||||
<dt id="connpy.cli.run_handler.RunHandler.cli_run"><code class="name flex">
|
||||
<span>def <span class="ident">cli_run</span></span>(<span>self, script)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def cli_run(self, script):
|
||||
try:
|
||||
action = script["action"]
|
||||
nodelist = script["nodes"]
|
||||
commands = script["commands"]
|
||||
variables = script.get("variables")
|
||||
output_cfg = script["output"]
|
||||
name = script.get("name", "Task")
|
||||
options = script.get("options", {})
|
||||
except KeyError as e:
|
||||
printer.error(f"'{e.args[0]}' is mandatory in script")
|
||||
sys.exit(11)
|
||||
|
||||
stdout = (output_cfg == "stdout")
|
||||
folder = output_cfg if output_cfg not in [None, "stdout"] else None
|
||||
prompt = options.get("prompt")
|
||||
printer.header(name.upper())
|
||||
|
||||
try:
|
||||
if action == "run":
|
||||
# If stdout is true, we stream results as they arrive
|
||||
on_complete = printer.node_panel if stdout else None
|
||||
results = self.app.services.execution.run_commands(
|
||||
nodes_filter=nodelist,
|
||||
commands=commands,
|
||||
variables=variables,
|
||||
parallel=options.get("parallel", 10),
|
||||
timeout=options.get("timeout", 10),
|
||||
folder=folder,
|
||||
prompt=prompt,
|
||||
on_node_complete=on_complete
|
||||
)
|
||||
# If not streaming, we could print a summary table here if needed
|
||||
if not stdout:
|
||||
for unique, output in results.items():
|
||||
printer.node_panel(unique, output, 0)
|
||||
|
||||
elif action == "test":
|
||||
expected = script.get("expected", [])
|
||||
on_complete = printer.test_panel if stdout else None
|
||||
results = self.app.services.execution.test_commands(
|
||||
nodes_filter=nodelist,
|
||||
commands=commands,
|
||||
expected=expected,
|
||||
variables=variables,
|
||||
parallel=options.get("parallel", 10),
|
||||
timeout=options.get("timeout", 10),
|
||||
prompt=prompt,
|
||||
on_node_complete=on_complete
|
||||
)
|
||||
if not stdout:
|
||||
printer.test_summary(results)
|
||||
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.run_handler.RunHandler.dispatch"><code class="name flex">
|
||||
<span>def <span class="ident">dispatch</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def dispatch(self, args):
|
||||
if len(args.data) > 1:
|
||||
args.action = "noderun"
|
||||
actions = {"noderun": self.node_run, "generate": self.yaml_generate, "run": self.yaml_run}
|
||||
return actions.get(args.action)(args)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.run_handler.RunHandler.node_run"><code class="name flex">
|
||||
<span>def <span class="ident">node_run</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def node_run(self, args):
|
||||
nodes_filter = args.data[0]
|
||||
commands = [" ".join(args.data[1:])]
|
||||
|
||||
try:
|
||||
header_printed = False
|
||||
# Inline execution with streaming results
|
||||
def _on_node_complete(unique, node_output, node_status):
|
||||
nonlocal header_printed
|
||||
if not header_printed:
|
||||
printer.console.print(Rule("OUTPUT", style="header"))
|
||||
header_printed = True
|
||||
printer.node_panel(unique, node_output, node_status)
|
||||
|
||||
self.app.services.execution.run_commands(
|
||||
nodes_filter=nodes_filter,
|
||||
commands=commands,
|
||||
on_node_complete=_on_node_complete
|
||||
)
|
||||
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))
|
||||
sys.exit(1)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.run_handler.RunHandler.yaml_generate"><code class="name flex">
|
||||
<span>def <span class="ident">yaml_generate</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def yaml_generate(self, args):
|
||||
if os.path.exists(args.data[0]):
|
||||
printer.error(f"File '{args.data[0]}' already exists.")
|
||||
sys.exit(14)
|
||||
else:
|
||||
with open(args.data[0], "w") as file:
|
||||
file.write(get_instructions("generate"))
|
||||
printer.success(f"File {args.data[0]} generated successfully")
|
||||
sys.exit()</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.run_handler.RunHandler.yaml_run"><code class="name flex">
|
||||
<span>def <span class="ident">yaml_run</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def yaml_run(self, args):
|
||||
path = args.data[0]
|
||||
try:
|
||||
with open(path, "r") as f:
|
||||
playbook = yaml.load(f, Loader=yaml.FullLoader)
|
||||
|
||||
for task in playbook.get("tasks", []):
|
||||
self.cli_run(task)
|
||||
|
||||
except Exception as e:
|
||||
printer.error(f"Failed to run playbook {path}: {e}")
|
||||
sys.exit(10)</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.cli" href="index.html">connpy.cli</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<h4><code><a title="connpy.cli.run_handler.RunHandler" href="#connpy.cli.run_handler.RunHandler">RunHandler</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.cli.run_handler.RunHandler.cli_run" href="#connpy.cli.run_handler.RunHandler.cli_run">cli_run</a></code></li>
|
||||
<li><code><a title="connpy.cli.run_handler.RunHandler.dispatch" href="#connpy.cli.run_handler.RunHandler.dispatch">dispatch</a></code></li>
|
||||
<li><code><a title="connpy.cli.run_handler.RunHandler.node_run" href="#connpy.cli.run_handler.RunHandler.node_run">node_run</a></code></li>
|
||||
<li><code><a title="connpy.cli.run_handler.RunHandler.yaml_generate" href="#connpy.cli.run_handler.RunHandler.yaml_generate">yaml_generate</a></code></li>
|
||||
<li><code><a title="connpy.cli.run_handler.RunHandler.yaml_run" href="#connpy.cli.run_handler.RunHandler.yaml_run">yaml_run</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,433 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.cli.sync_handler 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.cli.sync_handler</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
||||
<dl>
|
||||
<dt id="connpy.cli.sync_handler.SyncHandler"><code class="flex name class">
|
||||
<span>class <span class="ident">SyncHandler</span></span>
|
||||
<span>(</span><span>app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class SyncHandler:
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
|
||||
def dispatch(self, args):
|
||||
action = getattr(args, "action", None)
|
||||
actions = {
|
||||
"login": self.login,
|
||||
"logout": self.logout,
|
||||
"status": self.status,
|
||||
"list": self.list_backups,
|
||||
"once": self.once,
|
||||
"restore": self.restore,
|
||||
"start": self.start,
|
||||
"stop": self.stop
|
||||
}
|
||||
handler = actions.get(action)
|
||||
if handler:
|
||||
return handler(args)
|
||||
|
||||
return self.status(args)
|
||||
|
||||
def login(self, args):
|
||||
self.app.services.sync.login()
|
||||
|
||||
def logout(self, args):
|
||||
self.app.services.sync.logout()
|
||||
|
||||
def status(self, args):
|
||||
status = self.app.services.sync.check_login_status()
|
||||
enabled = self.app.services.sync.sync_enabled
|
||||
remote = self.app.services.sync.sync_remote
|
||||
|
||||
printer.info(f"Login Status: {status}")
|
||||
printer.info(f"Auto-Sync: {'Enabled' if enabled else 'Disabled'}")
|
||||
printer.info(f"Sync Remote Nodes: {'Yes' if remote else 'No'}")
|
||||
|
||||
def list_backups(self, args):
|
||||
backups = self.app.services.sync.list_backups()
|
||||
if backups:
|
||||
yaml_output = yaml.dump(backups, sort_keys=False, default_flow_style=False)
|
||||
printer.custom("backups", "")
|
||||
print(yaml_output)
|
||||
else:
|
||||
printer.info("No backups found or not logged in.")
|
||||
|
||||
def once(self, args):
|
||||
# Manual backup. We check if we should include remote nodes
|
||||
remote_data = None
|
||||
if self.app.services.sync.sync_remote and self.app.services.mode == "remote":
|
||||
inventory = self.app.services.nodes.get_inventory()
|
||||
# Merge with local settings
|
||||
local_settings = self.app.services.config_svc.get_settings()
|
||||
local_settings.pop("configfolder", None)
|
||||
|
||||
# Maintain proper config structure: {config: {}, connections: {}, profiles: {}}
|
||||
remote_data = {
|
||||
"config": local_settings,
|
||||
"connections": inventory.get("connections", {}),
|
||||
"profiles": inventory.get("profiles", {})
|
||||
}
|
||||
|
||||
if self.app.services.sync.compress_and_upload(remote_data):
|
||||
printer.success("Manual backup completed.")
|
||||
|
||||
def restore(self, args):
|
||||
import inquirer
|
||||
file_id = getattr(args, "id", None)
|
||||
|
||||
# Segmented flags
|
||||
restore_config = getattr(args, "restore_config", False)
|
||||
restore_nodes = getattr(args, "restore_nodes", False)
|
||||
|
||||
# If neither is specified, we restore ALL (backwards compatibility)
|
||||
if not restore_config and not restore_nodes:
|
||||
restore_config = True
|
||||
restore_nodes = True
|
||||
|
||||
# 1. Analyze what we are about to restore
|
||||
info = self.app.services.sync.analyze_backup_content(file_id)
|
||||
if not info:
|
||||
printer.error("Could not analyze backup content.")
|
||||
return
|
||||
|
||||
# 2. Show detailed info
|
||||
printer.info("Restoration Details:")
|
||||
if restore_config:
|
||||
print(f" - Local Settings: Yes")
|
||||
print(f" - RSA Key (.osk): {'Yes' if info['has_key'] else 'No'}")
|
||||
if restore_nodes:
|
||||
target = "REMOTE" if self.app.services.mode == "remote" else "LOCAL"
|
||||
print(f" - Nodes: {info['nodes']}")
|
||||
print(f" - Folders: {info['folders']}")
|
||||
print(f" - Profiles: {info['profiles']}")
|
||||
print(f" - Destination: {target}")
|
||||
print("")
|
||||
|
||||
questions = [inquirer.Confirm("confirm", message="Do you want to proceed with the restoration?", default=False)]
|
||||
answers = inquirer.prompt(questions)
|
||||
|
||||
if not answers or not answers["confirm"]:
|
||||
printer.info("Restore cancelled.")
|
||||
return
|
||||
|
||||
# 3. Perform the actual restore
|
||||
if self.app.services.sync.restore_backup(
|
||||
file_id=file_id,
|
||||
restore_config=restore_config,
|
||||
restore_nodes=restore_nodes,
|
||||
app_instance=self.app
|
||||
):
|
||||
printer.success("Restore completed successfully.")
|
||||
|
||||
def start(self, args):
|
||||
self.app.services.config_svc.update_setting("sync", True)
|
||||
self.app.services.sync.sync_enabled = True
|
||||
printer.success("Auto-sync enabled.")
|
||||
|
||||
def stop(self, args):
|
||||
self.app.services.config_svc.update_setting("sync", False)
|
||||
self.app.services.sync.sync_enabled = False
|
||||
printer.success("Auto-sync disabled.")</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
<h3>Methods</h3>
|
||||
<dl>
|
||||
<dt id="connpy.cli.sync_handler.SyncHandler.dispatch"><code class="name flex">
|
||||
<span>def <span class="ident">dispatch</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def dispatch(self, args):
|
||||
action = getattr(args, "action", None)
|
||||
actions = {
|
||||
"login": self.login,
|
||||
"logout": self.logout,
|
||||
"status": self.status,
|
||||
"list": self.list_backups,
|
||||
"once": self.once,
|
||||
"restore": self.restore,
|
||||
"start": self.start,
|
||||
"stop": self.stop
|
||||
}
|
||||
handler = actions.get(action)
|
||||
if handler:
|
||||
return handler(args)
|
||||
|
||||
return self.status(args)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.sync_handler.SyncHandler.list_backups"><code class="name flex">
|
||||
<span>def <span class="ident">list_backups</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def list_backups(self, args):
|
||||
backups = self.app.services.sync.list_backups()
|
||||
if backups:
|
||||
yaml_output = yaml.dump(backups, sort_keys=False, default_flow_style=False)
|
||||
printer.custom("backups", "")
|
||||
print(yaml_output)
|
||||
else:
|
||||
printer.info("No backups found or not logged in.")</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.sync_handler.SyncHandler.login"><code class="name flex">
|
||||
<span>def <span class="ident">login</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def login(self, args):
|
||||
self.app.services.sync.login()</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.sync_handler.SyncHandler.logout"><code class="name flex">
|
||||
<span>def <span class="ident">logout</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def logout(self, args):
|
||||
self.app.services.sync.logout()</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.sync_handler.SyncHandler.once"><code class="name flex">
|
||||
<span>def <span class="ident">once</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def once(self, args):
|
||||
# Manual backup. We check if we should include remote nodes
|
||||
remote_data = None
|
||||
if self.app.services.sync.sync_remote and self.app.services.mode == "remote":
|
||||
inventory = self.app.services.nodes.get_inventory()
|
||||
# Merge with local settings
|
||||
local_settings = self.app.services.config_svc.get_settings()
|
||||
local_settings.pop("configfolder", None)
|
||||
|
||||
# Maintain proper config structure: {config: {}, connections: {}, profiles: {}}
|
||||
remote_data = {
|
||||
"config": local_settings,
|
||||
"connections": inventory.get("connections", {}),
|
||||
"profiles": inventory.get("profiles", {})
|
||||
}
|
||||
|
||||
if self.app.services.sync.compress_and_upload(remote_data):
|
||||
printer.success("Manual backup completed.")</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.sync_handler.SyncHandler.restore"><code class="name flex">
|
||||
<span>def <span class="ident">restore</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def restore(self, args):
|
||||
import inquirer
|
||||
file_id = getattr(args, "id", None)
|
||||
|
||||
# Segmented flags
|
||||
restore_config = getattr(args, "restore_config", False)
|
||||
restore_nodes = getattr(args, "restore_nodes", False)
|
||||
|
||||
# If neither is specified, we restore ALL (backwards compatibility)
|
||||
if not restore_config and not restore_nodes:
|
||||
restore_config = True
|
||||
restore_nodes = True
|
||||
|
||||
# 1. Analyze what we are about to restore
|
||||
info = self.app.services.sync.analyze_backup_content(file_id)
|
||||
if not info:
|
||||
printer.error("Could not analyze backup content.")
|
||||
return
|
||||
|
||||
# 2. Show detailed info
|
||||
printer.info("Restoration Details:")
|
||||
if restore_config:
|
||||
print(f" - Local Settings: Yes")
|
||||
print(f" - RSA Key (.osk): {'Yes' if info['has_key'] else 'No'}")
|
||||
if restore_nodes:
|
||||
target = "REMOTE" if self.app.services.mode == "remote" else "LOCAL"
|
||||
print(f" - Nodes: {info['nodes']}")
|
||||
print(f" - Folders: {info['folders']}")
|
||||
print(f" - Profiles: {info['profiles']}")
|
||||
print(f" - Destination: {target}")
|
||||
print("")
|
||||
|
||||
questions = [inquirer.Confirm("confirm", message="Do you want to proceed with the restoration?", default=False)]
|
||||
answers = inquirer.prompt(questions)
|
||||
|
||||
if not answers or not answers["confirm"]:
|
||||
printer.info("Restore cancelled.")
|
||||
return
|
||||
|
||||
# 3. Perform the actual restore
|
||||
if self.app.services.sync.restore_backup(
|
||||
file_id=file_id,
|
||||
restore_config=restore_config,
|
||||
restore_nodes=restore_nodes,
|
||||
app_instance=self.app
|
||||
):
|
||||
printer.success("Restore completed successfully.")</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.sync_handler.SyncHandler.start"><code class="name flex">
|
||||
<span>def <span class="ident">start</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def start(self, args):
|
||||
self.app.services.config_svc.update_setting("sync", True)
|
||||
self.app.services.sync.sync_enabled = True
|
||||
printer.success("Auto-sync enabled.")</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.sync_handler.SyncHandler.status"><code class="name flex">
|
||||
<span>def <span class="ident">status</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def status(self, args):
|
||||
status = self.app.services.sync.check_login_status()
|
||||
enabled = self.app.services.sync.sync_enabled
|
||||
remote = self.app.services.sync.sync_remote
|
||||
|
||||
printer.info(f"Login Status: {status}")
|
||||
printer.info(f"Auto-Sync: {'Enabled' if enabled else 'Disabled'}")
|
||||
printer.info(f"Sync Remote Nodes: {'Yes' if remote else 'No'}")</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.sync_handler.SyncHandler.stop"><code class="name flex">
|
||||
<span>def <span class="ident">stop</span></span>(<span>self, args)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def stop(self, args):
|
||||
self.app.services.config_svc.update_setting("sync", False)
|
||||
self.app.services.sync.sync_enabled = False
|
||||
printer.success("Auto-sync disabled.")</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.cli" href="index.html">connpy.cli</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<h4><code><a title="connpy.cli.sync_handler.SyncHandler" href="#connpy.cli.sync_handler.SyncHandler">SyncHandler</a></code></h4>
|
||||
<ul class="two-column">
|
||||
<li><code><a title="connpy.cli.sync_handler.SyncHandler.dispatch" href="#connpy.cli.sync_handler.SyncHandler.dispatch">dispatch</a></code></li>
|
||||
<li><code><a title="connpy.cli.sync_handler.SyncHandler.list_backups" href="#connpy.cli.sync_handler.SyncHandler.list_backups">list_backups</a></code></li>
|
||||
<li><code><a title="connpy.cli.sync_handler.SyncHandler.login" href="#connpy.cli.sync_handler.SyncHandler.login">login</a></code></li>
|
||||
<li><code><a title="connpy.cli.sync_handler.SyncHandler.logout" href="#connpy.cli.sync_handler.SyncHandler.logout">logout</a></code></li>
|
||||
<li><code><a title="connpy.cli.sync_handler.SyncHandler.once" href="#connpy.cli.sync_handler.SyncHandler.once">once</a></code></li>
|
||||
<li><code><a title="connpy.cli.sync_handler.SyncHandler.restore" href="#connpy.cli.sync_handler.SyncHandler.restore">restore</a></code></li>
|
||||
<li><code><a title="connpy.cli.sync_handler.SyncHandler.start" href="#connpy.cli.sync_handler.SyncHandler.start">start</a></code></li>
|
||||
<li><code><a title="connpy.cli.sync_handler.SyncHandler.status" href="#connpy.cli.sync_handler.SyncHandler.status">status</a></code></li>
|
||||
<li><code><a title="connpy.cli.sync_handler.SyncHandler.stop" href="#connpy.cli.sync_handler.SyncHandler.stop">stop</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,514 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.cli.validators 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.cli.validators</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
||||
<dl>
|
||||
<dt id="connpy.cli.validators.Validators"><code class="flex name class">
|
||||
<span>class <span class="ident">Validators</span></span>
|
||||
<span>(</span><span>app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class Validators:
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
|
||||
def host_validation(self, answers, current, regex = "^.+$"):
|
||||
if not re.match(regex, current):
|
||||
raise inquirer.errors.ValidationError("", reason="Host cannot be empty")
|
||||
if current.startswith("@"):
|
||||
if current[1:] not in self.app.profiles:
|
||||
raise inquirer.errors.ValidationError("", reason="Profile {} don't exist".format(current))
|
||||
return True
|
||||
|
||||
def profile_protocol_validation(self, answers, current, regex = "(^ssh$|^telnet$|^kubectl$|^docker$|^$)"):
|
||||
if not re.match(regex, current):
|
||||
raise inquirer.errors.ValidationError("", reason="Pick between ssh, telnet, kubectl, docker or leave empty")
|
||||
return True
|
||||
|
||||
def protocol_validation(self, answers, current, regex = "(^ssh$|^telnet$|^kubectl$|^docker$|^$|^@.+$)"):
|
||||
if not re.match(regex, current):
|
||||
raise inquirer.errors.ValidationError("", reason="Pick between ssh, telnet, kubectl, docker leave empty or @profile")
|
||||
if current.startswith("@"):
|
||||
if current[1:] not in self.app.profiles:
|
||||
raise inquirer.errors.ValidationError("", reason="Profile {} don't exist".format(current))
|
||||
return True
|
||||
|
||||
def profile_port_validation(self, answers, current, regex = "(^[0-9]*$)"):
|
||||
if not re.match(regex, current):
|
||||
raise inquirer.errors.ValidationError("", reason="Pick a port between 1-65535, @profile o leave empty")
|
||||
try:
|
||||
port = int(current)
|
||||
except ValueError:
|
||||
port = 0
|
||||
if current != "" and not 1 <= int(port) <= 65535:
|
||||
raise inquirer.errors.ValidationError("", reason="Pick a port between 1-65535 or leave empty")
|
||||
return True
|
||||
|
||||
def port_validation(self, answers, current, regex = "(^[0-9]*$|^@.+$)"):
|
||||
if not re.match(regex, current):
|
||||
raise inquirer.errors.ValidationError("", reason="Pick a port between 1-65535, @profile or leave empty")
|
||||
try:
|
||||
port = int(current)
|
||||
except ValueError:
|
||||
port = 0
|
||||
if current.startswith("@"):
|
||||
if current[1:] not in self.app.profiles:
|
||||
raise inquirer.errors.ValidationError("", reason="Profile {} don't exist".format(current))
|
||||
elif current != "" and not 1 <= int(port) <= 65535:
|
||||
raise inquirer.errors.ValidationError("", reason="Pick a port between 1-65535, @profile o leave empty")
|
||||
return True
|
||||
|
||||
def pass_validation(self, answers, current, regex = "(^@.+$)"):
|
||||
profiles = current.split(",")
|
||||
for i in profiles:
|
||||
if not re.match(regex, i) or i[1:] not in self.app.profiles:
|
||||
raise inquirer.errors.ValidationError("", reason="Profile {} don't exist".format(i))
|
||||
return True
|
||||
|
||||
def tags_validation(self, answers, current):
|
||||
if current.startswith("@"):
|
||||
if current[1:] not in self.app.profiles:
|
||||
raise inquirer.errors.ValidationError("", reason="Profile {} don't exist".format(current))
|
||||
elif current != "":
|
||||
isdict = False
|
||||
try:
|
||||
isdict = ast.literal_eval(current)
|
||||
except Exception:
|
||||
pass
|
||||
if not isinstance (isdict, dict):
|
||||
raise inquirer.errors.ValidationError("", reason="Tags should be a python dictionary.".format(current))
|
||||
return True
|
||||
|
||||
def profile_tags_validation(self, answers, current):
|
||||
if current != "":
|
||||
isdict = False
|
||||
try:
|
||||
isdict = ast.literal_eval(current)
|
||||
except Exception:
|
||||
pass
|
||||
if not isinstance (isdict, dict):
|
||||
raise inquirer.errors.ValidationError("", reason="Tags should be a python dictionary.".format(current))
|
||||
return True
|
||||
|
||||
def jumphost_validation(self, answers, current):
|
||||
if current.startswith("@"):
|
||||
if current[1:] not in self.app.profiles:
|
||||
raise inquirer.errors.ValidationError("", reason="Profile {} don't exist".format(current))
|
||||
elif current != "":
|
||||
if current not in self.app.nodes_list:
|
||||
raise inquirer.errors.ValidationError("", reason="Node {} don't exist.".format(current))
|
||||
return True
|
||||
|
||||
def profile_jumphost_validation(self, answers, current):
|
||||
if current != "":
|
||||
if current not in self.app.nodes_list:
|
||||
raise inquirer.errors.ValidationError("", reason="Node {} don't exist.".format(current))
|
||||
return True
|
||||
|
||||
def default_validation(self, answers, current):
|
||||
if current.startswith("@"):
|
||||
if current[1:] not in self.app.profiles:
|
||||
raise inquirer.errors.ValidationError("", reason="Profile {} don't exist".format(current))
|
||||
return True
|
||||
|
||||
def bulk_node_validation(self, answers, current, regex = "^[0-9a-zA-Z_.,$#-]+$"):
|
||||
if not re.match(regex, current):
|
||||
raise inquirer.errors.ValidationError("", reason="Host cannot be empty")
|
||||
if current.startswith("@"):
|
||||
if current[1:] not in self.app.profiles:
|
||||
raise inquirer.errors.ValidationError("", reason="Profile {} don't exist".format(current))
|
||||
return True
|
||||
|
||||
def bulk_folder_validation(self, answers, current):
|
||||
if not self.app.case:
|
||||
current = current.lower()
|
||||
|
||||
candidate = current
|
||||
if "/" in current:
|
||||
candidate = current.split("/")[0]
|
||||
|
||||
matches = list(filter(lambda k: k == candidate, self.app.folders))
|
||||
if current != "" and len(matches) == 0:
|
||||
raise inquirer.errors.ValidationError("", reason="Location {} don't exist".format(current))
|
||||
return True
|
||||
|
||||
def bulk_host_validation(self, answers, current, regex = "^.+$"):
|
||||
if not re.match(regex, current):
|
||||
raise inquirer.errors.ValidationError("", reason="Host cannot be empty")
|
||||
if current.startswith("@"):
|
||||
if current[1:] not in self.app.profiles:
|
||||
raise inquirer.errors.ValidationError("", reason="Profile {} don't exist".format(current))
|
||||
hosts = current.split(",")
|
||||
nodes = answers["ids"].split(",")
|
||||
if len(hosts) > 1 and len(hosts) != len(nodes):
|
||||
raise inquirer.errors.ValidationError("", reason="Hosts list should be the same length of nodes list")
|
||||
return True</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
<h3>Methods</h3>
|
||||
<dl>
|
||||
<dt id="connpy.cli.validators.Validators.bulk_folder_validation"><code class="name flex">
|
||||
<span>def <span class="ident">bulk_folder_validation</span></span>(<span>self, answers, current)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def bulk_folder_validation(self, answers, current):
|
||||
if not self.app.case:
|
||||
current = current.lower()
|
||||
|
||||
candidate = current
|
||||
if "/" in current:
|
||||
candidate = current.split("/")[0]
|
||||
|
||||
matches = list(filter(lambda k: k == candidate, self.app.folders))
|
||||
if current != "" and len(matches) == 0:
|
||||
raise inquirer.errors.ValidationError("", reason="Location {} don't exist".format(current))
|
||||
return True</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.validators.Validators.bulk_host_validation"><code class="name flex">
|
||||
<span>def <span class="ident">bulk_host_validation</span></span>(<span>self, answers, current, regex='^.+$')</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def bulk_host_validation(self, answers, current, regex = "^.+$"):
|
||||
if not re.match(regex, current):
|
||||
raise inquirer.errors.ValidationError("", reason="Host cannot be empty")
|
||||
if current.startswith("@"):
|
||||
if current[1:] not in self.app.profiles:
|
||||
raise inquirer.errors.ValidationError("", reason="Profile {} don't exist".format(current))
|
||||
hosts = current.split(",")
|
||||
nodes = answers["ids"].split(",")
|
||||
if len(hosts) > 1 and len(hosts) != len(nodes):
|
||||
raise inquirer.errors.ValidationError("", reason="Hosts list should be the same length of nodes list")
|
||||
return True</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.validators.Validators.bulk_node_validation"><code class="name flex">
|
||||
<span>def <span class="ident">bulk_node_validation</span></span>(<span>self, answers, current, regex='^[0-9a-zA-Z_.,$#-]+$')</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def bulk_node_validation(self, answers, current, regex = "^[0-9a-zA-Z_.,$#-]+$"):
|
||||
if not re.match(regex, current):
|
||||
raise inquirer.errors.ValidationError("", reason="Host cannot be empty")
|
||||
if current.startswith("@"):
|
||||
if current[1:] not in self.app.profiles:
|
||||
raise inquirer.errors.ValidationError("", reason="Profile {} don't exist".format(current))
|
||||
return True</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.validators.Validators.default_validation"><code class="name flex">
|
||||
<span>def <span class="ident">default_validation</span></span>(<span>self, answers, current)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def default_validation(self, answers, current):
|
||||
if current.startswith("@"):
|
||||
if current[1:] not in self.app.profiles:
|
||||
raise inquirer.errors.ValidationError("", reason="Profile {} don't exist".format(current))
|
||||
return True</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.validators.Validators.host_validation"><code class="name flex">
|
||||
<span>def <span class="ident">host_validation</span></span>(<span>self, answers, current, regex='^.+$')</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def host_validation(self, answers, current, regex = "^.+$"):
|
||||
if not re.match(regex, current):
|
||||
raise inquirer.errors.ValidationError("", reason="Host cannot be empty")
|
||||
if current.startswith("@"):
|
||||
if current[1:] not in self.app.profiles:
|
||||
raise inquirer.errors.ValidationError("", reason="Profile {} don't exist".format(current))
|
||||
return True</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.validators.Validators.jumphost_validation"><code class="name flex">
|
||||
<span>def <span class="ident">jumphost_validation</span></span>(<span>self, answers, current)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def jumphost_validation(self, answers, current):
|
||||
if current.startswith("@"):
|
||||
if current[1:] not in self.app.profiles:
|
||||
raise inquirer.errors.ValidationError("", reason="Profile {} don't exist".format(current))
|
||||
elif current != "":
|
||||
if current not in self.app.nodes_list:
|
||||
raise inquirer.errors.ValidationError("", reason="Node {} don't exist.".format(current))
|
||||
return True</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.validators.Validators.pass_validation"><code class="name flex">
|
||||
<span>def <span class="ident">pass_validation</span></span>(<span>self, answers, current, regex='(^@.+$)')</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def pass_validation(self, answers, current, regex = "(^@.+$)"):
|
||||
profiles = current.split(",")
|
||||
for i in profiles:
|
||||
if not re.match(regex, i) or i[1:] not in self.app.profiles:
|
||||
raise inquirer.errors.ValidationError("", reason="Profile {} don't exist".format(i))
|
||||
return True</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.validators.Validators.port_validation"><code class="name flex">
|
||||
<span>def <span class="ident">port_validation</span></span>(<span>self, answers, current, regex='(^[0-9]*$|^@.+$)')</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def port_validation(self, answers, current, regex = "(^[0-9]*$|^@.+$)"):
|
||||
if not re.match(regex, current):
|
||||
raise inquirer.errors.ValidationError("", reason="Pick a port between 1-65535, @profile or leave empty")
|
||||
try:
|
||||
port = int(current)
|
||||
except ValueError:
|
||||
port = 0
|
||||
if current.startswith("@"):
|
||||
if current[1:] not in self.app.profiles:
|
||||
raise inquirer.errors.ValidationError("", reason="Profile {} don't exist".format(current))
|
||||
elif current != "" and not 1 <= int(port) <= 65535:
|
||||
raise inquirer.errors.ValidationError("", reason="Pick a port between 1-65535, @profile o leave empty")
|
||||
return True</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.validators.Validators.profile_jumphost_validation"><code class="name flex">
|
||||
<span>def <span class="ident">profile_jumphost_validation</span></span>(<span>self, answers, current)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def profile_jumphost_validation(self, answers, current):
|
||||
if current != "":
|
||||
if current not in self.app.nodes_list:
|
||||
raise inquirer.errors.ValidationError("", reason="Node {} don't exist.".format(current))
|
||||
return True</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.validators.Validators.profile_port_validation"><code class="name flex">
|
||||
<span>def <span class="ident">profile_port_validation</span></span>(<span>self, answers, current, regex='(^[0-9]*$)')</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def profile_port_validation(self, answers, current, regex = "(^[0-9]*$)"):
|
||||
if not re.match(regex, current):
|
||||
raise inquirer.errors.ValidationError("", reason="Pick a port between 1-65535, @profile o leave empty")
|
||||
try:
|
||||
port = int(current)
|
||||
except ValueError:
|
||||
port = 0
|
||||
if current != "" and not 1 <= int(port) <= 65535:
|
||||
raise inquirer.errors.ValidationError("", reason="Pick a port between 1-65535 or leave empty")
|
||||
return True</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.validators.Validators.profile_protocol_validation"><code class="name flex">
|
||||
<span>def <span class="ident">profile_protocol_validation</span></span>(<span>self, answers, current, regex='(^ssh$|^telnet$|^kubectl$|^docker$|^$)')</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def profile_protocol_validation(self, answers, current, regex = "(^ssh$|^telnet$|^kubectl$|^docker$|^$)"):
|
||||
if not re.match(regex, current):
|
||||
raise inquirer.errors.ValidationError("", reason="Pick between ssh, telnet, kubectl, docker or leave empty")
|
||||
return True</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.validators.Validators.profile_tags_validation"><code class="name flex">
|
||||
<span>def <span class="ident">profile_tags_validation</span></span>(<span>self, answers, current)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def profile_tags_validation(self, answers, current):
|
||||
if current != "":
|
||||
isdict = False
|
||||
try:
|
||||
isdict = ast.literal_eval(current)
|
||||
except Exception:
|
||||
pass
|
||||
if not isinstance (isdict, dict):
|
||||
raise inquirer.errors.ValidationError("", reason="Tags should be a python dictionary.".format(current))
|
||||
return True</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.validators.Validators.protocol_validation"><code class="name flex">
|
||||
<span>def <span class="ident">protocol_validation</span></span>(<span>self, answers, current, regex='(^ssh$|^telnet$|^kubectl$|^docker$|^$|^@.+$)')</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def protocol_validation(self, answers, current, regex = "(^ssh$|^telnet$|^kubectl$|^docker$|^$|^@.+$)"):
|
||||
if not re.match(regex, current):
|
||||
raise inquirer.errors.ValidationError("", reason="Pick between ssh, telnet, kubectl, docker leave empty or @profile")
|
||||
if current.startswith("@"):
|
||||
if current[1:] not in self.app.profiles:
|
||||
raise inquirer.errors.ValidationError("", reason="Profile {} don't exist".format(current))
|
||||
return True</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.validators.Validators.tags_validation"><code class="name flex">
|
||||
<span>def <span class="ident">tags_validation</span></span>(<span>self, answers, current)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def tags_validation(self, answers, current):
|
||||
if current.startswith("@"):
|
||||
if current[1:] not in self.app.profiles:
|
||||
raise inquirer.errors.ValidationError("", reason="Profile {} don't exist".format(current))
|
||||
elif current != "":
|
||||
isdict = False
|
||||
try:
|
||||
isdict = ast.literal_eval(current)
|
||||
except Exception:
|
||||
pass
|
||||
if not isinstance (isdict, dict):
|
||||
raise inquirer.errors.ValidationError("", reason="Tags should be a python dictionary.".format(current))
|
||||
return True</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.cli" href="index.html">connpy.cli</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<h4><code><a title="connpy.cli.validators.Validators" href="#connpy.cli.validators.Validators">Validators</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.cli.validators.Validators.bulk_folder_validation" href="#connpy.cli.validators.Validators.bulk_folder_validation">bulk_folder_validation</a></code></li>
|
||||
<li><code><a title="connpy.cli.validators.Validators.bulk_host_validation" href="#connpy.cli.validators.Validators.bulk_host_validation">bulk_host_validation</a></code></li>
|
||||
<li><code><a title="connpy.cli.validators.Validators.bulk_node_validation" href="#connpy.cli.validators.Validators.bulk_node_validation">bulk_node_validation</a></code></li>
|
||||
<li><code><a title="connpy.cli.validators.Validators.default_validation" href="#connpy.cli.validators.Validators.default_validation">default_validation</a></code></li>
|
||||
<li><code><a title="connpy.cli.validators.Validators.host_validation" href="#connpy.cli.validators.Validators.host_validation">host_validation</a></code></li>
|
||||
<li><code><a title="connpy.cli.validators.Validators.jumphost_validation" href="#connpy.cli.validators.Validators.jumphost_validation">jumphost_validation</a></code></li>
|
||||
<li><code><a title="connpy.cli.validators.Validators.pass_validation" href="#connpy.cli.validators.Validators.pass_validation">pass_validation</a></code></li>
|
||||
<li><code><a title="connpy.cli.validators.Validators.port_validation" href="#connpy.cli.validators.Validators.port_validation">port_validation</a></code></li>
|
||||
<li><code><a title="connpy.cli.validators.Validators.profile_jumphost_validation" href="#connpy.cli.validators.Validators.profile_jumphost_validation">profile_jumphost_validation</a></code></li>
|
||||
<li><code><a title="connpy.cli.validators.Validators.profile_port_validation" href="#connpy.cli.validators.Validators.profile_port_validation">profile_port_validation</a></code></li>
|
||||
<li><code><a title="connpy.cli.validators.Validators.profile_protocol_validation" href="#connpy.cli.validators.Validators.profile_protocol_validation">profile_protocol_validation</a></code></li>
|
||||
<li><code><a title="connpy.cli.validators.Validators.profile_tags_validation" href="#connpy.cli.validators.Validators.profile_tags_validation">profile_tags_validation</a></code></li>
|
||||
<li><code><a title="connpy.cli.validators.Validators.protocol_validation" href="#connpy.cli.validators.Validators.protocol_validation">protocol_validation</a></code></li>
|
||||
<li><code><a title="connpy.cli.validators.Validators.tags_validation" href="#connpy.cli.validators.Validators.tags_validation">tags_validation</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,799 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.grpc.connpy_pb2 API documentation</title>
|
||||
<meta name="description" content="Generated protocol buffer code.">
|
||||
<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.grpc.connpy_pb2</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
<p>Generated protocol buffer code.</p>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.connpy_pb2.AIResponse"><code class="flex name class">
|
||||
<span>class <span class="ident">AIResponse</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.connpy_pb2.AIResponse.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.connpy_pb2.AskRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">AskRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.connpy_pb2.AskRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.connpy_pb2.BoolResponse"><code class="flex name class">
|
||||
<span>class <span class="ident">BoolResponse</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.connpy_pb2.BoolResponse.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.connpy_pb2.BulkRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">BulkRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.connpy_pb2.BulkRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.connpy_pb2.DeleteRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">DeleteRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.connpy_pb2.DeleteRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.connpy_pb2.ExportRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">ExportRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.connpy_pb2.ExportRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.connpy_pb2.FilterRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">FilterRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.connpy_pb2.FilterRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.connpy_pb2.FullReplaceRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">FullReplaceRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.connpy_pb2.FullReplaceRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.connpy_pb2.IdRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">IdRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.connpy_pb2.IdRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.connpy_pb2.IntRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">IntRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.connpy_pb2.IntRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.connpy_pb2.InteractRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">InteractRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.connpy_pb2.InteractRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.connpy_pb2.InteractResponse"><code class="flex name class">
|
||||
<span>class <span class="ident">InteractResponse</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.connpy_pb2.InteractResponse.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.connpy_pb2.ListRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">ListRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.connpy_pb2.ListRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.connpy_pb2.MessageValue"><code class="flex name class">
|
||||
<span>class <span class="ident">MessageValue</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.connpy_pb2.MessageValue.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.connpy_pb2.MoveRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">MoveRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.connpy_pb2.MoveRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.connpy_pb2.NodeRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">NodeRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.connpy_pb2.NodeRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.connpy_pb2.NodeRunResult"><code class="flex name class">
|
||||
<span>class <span class="ident">NodeRunResult</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.connpy_pb2.NodeRunResult.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.connpy_pb2.PluginRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">PluginRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.connpy_pb2.PluginRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.connpy_pb2.ProfileRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">ProfileRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.connpy_pb2.ProfileRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.connpy_pb2.ProviderRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">ProviderRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.connpy_pb2.ProviderRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.connpy_pb2.RunRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">RunRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.connpy_pb2.RunRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.connpy_pb2.ScriptRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">ScriptRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.connpy_pb2.ScriptRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.connpy_pb2.StringRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">StringRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.connpy_pb2.StringRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.connpy_pb2.StringResponse"><code class="flex name class">
|
||||
<span>class <span class="ident">StringResponse</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.connpy_pb2.StringResponse.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.connpy_pb2.StructRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">StructRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.connpy_pb2.StructRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.connpy_pb2.StructResponse"><code class="flex name class">
|
||||
<span>class <span class="ident">StructResponse</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.connpy_pb2.StructResponse.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.connpy_pb2.TestRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">TestRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.connpy_pb2.TestRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.connpy_pb2.UpdateRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">UpdateRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.connpy_pb2.UpdateRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.connpy_pb2.ValueResponse"><code class="flex name class">
|
||||
<span>class <span class="ident">ValueResponse</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.connpy_pb2.ValueResponse.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<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.grpc" href="index.html">connpy.grpc</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.connpy_pb2.AIResponse" href="#connpy.grpc.connpy_pb2.AIResponse">AIResponse</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.connpy_pb2.AIResponse.DESCRIPTOR" href="#connpy.grpc.connpy_pb2.AIResponse.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.connpy_pb2.AskRequest" href="#connpy.grpc.connpy_pb2.AskRequest">AskRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.connpy_pb2.AskRequest.DESCRIPTOR" href="#connpy.grpc.connpy_pb2.AskRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.connpy_pb2.BoolResponse" href="#connpy.grpc.connpy_pb2.BoolResponse">BoolResponse</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.connpy_pb2.BoolResponse.DESCRIPTOR" href="#connpy.grpc.connpy_pb2.BoolResponse.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.connpy_pb2.BulkRequest" href="#connpy.grpc.connpy_pb2.BulkRequest">BulkRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.connpy_pb2.BulkRequest.DESCRIPTOR" href="#connpy.grpc.connpy_pb2.BulkRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.connpy_pb2.DeleteRequest" href="#connpy.grpc.connpy_pb2.DeleteRequest">DeleteRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.connpy_pb2.DeleteRequest.DESCRIPTOR" href="#connpy.grpc.connpy_pb2.DeleteRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.connpy_pb2.ExportRequest" href="#connpy.grpc.connpy_pb2.ExportRequest">ExportRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.connpy_pb2.ExportRequest.DESCRIPTOR" href="#connpy.grpc.connpy_pb2.ExportRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.connpy_pb2.FilterRequest" href="#connpy.grpc.connpy_pb2.FilterRequest">FilterRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.connpy_pb2.FilterRequest.DESCRIPTOR" href="#connpy.grpc.connpy_pb2.FilterRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.connpy_pb2.FullReplaceRequest" href="#connpy.grpc.connpy_pb2.FullReplaceRequest">FullReplaceRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.connpy_pb2.FullReplaceRequest.DESCRIPTOR" href="#connpy.grpc.connpy_pb2.FullReplaceRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.connpy_pb2.IdRequest" href="#connpy.grpc.connpy_pb2.IdRequest">IdRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.connpy_pb2.IdRequest.DESCRIPTOR" href="#connpy.grpc.connpy_pb2.IdRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.connpy_pb2.IntRequest" href="#connpy.grpc.connpy_pb2.IntRequest">IntRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.connpy_pb2.IntRequest.DESCRIPTOR" href="#connpy.grpc.connpy_pb2.IntRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.connpy_pb2.InteractRequest" href="#connpy.grpc.connpy_pb2.InteractRequest">InteractRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.connpy_pb2.InteractRequest.DESCRIPTOR" href="#connpy.grpc.connpy_pb2.InteractRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.connpy_pb2.InteractResponse" href="#connpy.grpc.connpy_pb2.InteractResponse">InteractResponse</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.connpy_pb2.InteractResponse.DESCRIPTOR" href="#connpy.grpc.connpy_pb2.InteractResponse.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.connpy_pb2.ListRequest" href="#connpy.grpc.connpy_pb2.ListRequest">ListRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.connpy_pb2.ListRequest.DESCRIPTOR" href="#connpy.grpc.connpy_pb2.ListRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.connpy_pb2.MessageValue" href="#connpy.grpc.connpy_pb2.MessageValue">MessageValue</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.connpy_pb2.MessageValue.DESCRIPTOR" href="#connpy.grpc.connpy_pb2.MessageValue.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.connpy_pb2.MoveRequest" href="#connpy.grpc.connpy_pb2.MoveRequest">MoveRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.connpy_pb2.MoveRequest.DESCRIPTOR" href="#connpy.grpc.connpy_pb2.MoveRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.connpy_pb2.NodeRequest" href="#connpy.grpc.connpy_pb2.NodeRequest">NodeRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.connpy_pb2.NodeRequest.DESCRIPTOR" href="#connpy.grpc.connpy_pb2.NodeRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.connpy_pb2.NodeRunResult" href="#connpy.grpc.connpy_pb2.NodeRunResult">NodeRunResult</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.connpy_pb2.NodeRunResult.DESCRIPTOR" href="#connpy.grpc.connpy_pb2.NodeRunResult.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.connpy_pb2.PluginRequest" href="#connpy.grpc.connpy_pb2.PluginRequest">PluginRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.connpy_pb2.PluginRequest.DESCRIPTOR" href="#connpy.grpc.connpy_pb2.PluginRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.connpy_pb2.ProfileRequest" href="#connpy.grpc.connpy_pb2.ProfileRequest">ProfileRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.connpy_pb2.ProfileRequest.DESCRIPTOR" href="#connpy.grpc.connpy_pb2.ProfileRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.connpy_pb2.ProviderRequest" href="#connpy.grpc.connpy_pb2.ProviderRequest">ProviderRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.connpy_pb2.ProviderRequest.DESCRIPTOR" href="#connpy.grpc.connpy_pb2.ProviderRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.connpy_pb2.RunRequest" href="#connpy.grpc.connpy_pb2.RunRequest">RunRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.connpy_pb2.RunRequest.DESCRIPTOR" href="#connpy.grpc.connpy_pb2.RunRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.connpy_pb2.ScriptRequest" href="#connpy.grpc.connpy_pb2.ScriptRequest">ScriptRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.connpy_pb2.ScriptRequest.DESCRIPTOR" href="#connpy.grpc.connpy_pb2.ScriptRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.connpy_pb2.StringRequest" href="#connpy.grpc.connpy_pb2.StringRequest">StringRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.connpy_pb2.StringRequest.DESCRIPTOR" href="#connpy.grpc.connpy_pb2.StringRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.connpy_pb2.StringResponse" href="#connpy.grpc.connpy_pb2.StringResponse">StringResponse</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.connpy_pb2.StringResponse.DESCRIPTOR" href="#connpy.grpc.connpy_pb2.StringResponse.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.connpy_pb2.StructRequest" href="#connpy.grpc.connpy_pb2.StructRequest">StructRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.connpy_pb2.StructRequest.DESCRIPTOR" href="#connpy.grpc.connpy_pb2.StructRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.connpy_pb2.StructResponse" href="#connpy.grpc.connpy_pb2.StructResponse">StructResponse</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.connpy_pb2.StructResponse.DESCRIPTOR" href="#connpy.grpc.connpy_pb2.StructResponse.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.connpy_pb2.TestRequest" href="#connpy.grpc.connpy_pb2.TestRequest">TestRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.connpy_pb2.TestRequest.DESCRIPTOR" href="#connpy.grpc.connpy_pb2.TestRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.connpy_pb2.UpdateRequest" href="#connpy.grpc.connpy_pb2.UpdateRequest">UpdateRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.connpy_pb2.UpdateRequest.DESCRIPTOR" href="#connpy.grpc.connpy_pb2.UpdateRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.connpy_pb2.ValueResponse" href="#connpy.grpc.connpy_pb2.ValueResponse">ValueResponse</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.connpy_pb2.ValueResponse.DESCRIPTOR" href="#connpy.grpc.connpy_pb2.ValueResponse.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,108 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.grpc 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">Namespace <code>connpy.grpc</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.grpc.connpy_pb2" href="connpy_pb2.html">connpy.grpc.connpy_pb2</a></code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>Generated protocol buffer code.</p></div>
|
||||
</dd>
|
||||
<dt><code class="name"><a title="connpy.grpc.connpy_pb2_grpc" href="connpy_pb2_grpc.html">connpy.grpc.connpy_pb2_grpc</a></code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>Client and server classes corresponding to protobuf-defined services.</p></div>
|
||||
</dd>
|
||||
<dt><code class="name"><a title="connpy.grpc.remote_plugin_pb2" href="remote_plugin_pb2.html">connpy.grpc.remote_plugin_pb2</a></code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>Generated protocol buffer code.</p></div>
|
||||
</dd>
|
||||
<dt><code class="name"><a title="connpy.grpc.remote_plugin_pb2_grpc" href="remote_plugin_pb2_grpc.html">connpy.grpc.remote_plugin_pb2_grpc</a></code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>Client and server classes corresponding to protobuf-defined services.</p></div>
|
||||
</dd>
|
||||
<dt><code class="name"><a title="connpy.grpc.server" href="server.html">connpy.grpc.server</a></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt><code class="name"><a title="connpy.grpc.stubs" href="stubs.html">connpy.grpc.stubs</a></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt><code class="name"><a title="connpy.grpc.utils" href="utils.html">connpy.grpc.utils</a></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></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.grpc.connpy_pb2" href="connpy_pb2.html">connpy.grpc.connpy_pb2</a></code></li>
|
||||
<li><code><a title="connpy.grpc.connpy_pb2_grpc" href="connpy_pb2_grpc.html">connpy.grpc.connpy_pb2_grpc</a></code></li>
|
||||
<li><code><a title="connpy.grpc.remote_plugin_pb2" href="remote_plugin_pb2.html">connpy.grpc.remote_plugin_pb2</a></code></li>
|
||||
<li><code><a title="connpy.grpc.remote_plugin_pb2_grpc" href="remote_plugin_pb2_grpc.html">connpy.grpc.remote_plugin_pb2_grpc</a></code></li>
|
||||
<li><code><a title="connpy.grpc.server" href="server.html">connpy.grpc.server</a></code></li>
|
||||
<li><code><a title="connpy.grpc.stubs" href="stubs.html">connpy.grpc.stubs</a></code></li>
|
||||
<li><code><a title="connpy.grpc.utils" href="utils.html">connpy.grpc.utils</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.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,174 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.grpc.remote_plugin_pb2 API documentation</title>
|
||||
<meta name="description" content="Generated protocol buffer code.">
|
||||
<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.grpc.remote_plugin_pb2</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
<p>Generated protocol buffer code.</p>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.remote_plugin_pb2.IdRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">IdRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.remote_plugin_pb2.IdRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.remote_plugin_pb2.OutputChunk"><code class="flex name class">
|
||||
<span>class <span class="ident">OutputChunk</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.remote_plugin_pb2.OutputChunk.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.remote_plugin_pb2.PluginInvokeRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">PluginInvokeRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.remote_plugin_pb2.PluginInvokeRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.remote_plugin_pb2.StringResponse"><code class="flex name class">
|
||||
<span>class <span class="ident">StringResponse</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.remote_plugin_pb2.StringResponse.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<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.grpc" href="index.html">connpy.grpc</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.remote_plugin_pb2.IdRequest" href="#connpy.grpc.remote_plugin_pb2.IdRequest">IdRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.remote_plugin_pb2.IdRequest.DESCRIPTOR" href="#connpy.grpc.remote_plugin_pb2.IdRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.remote_plugin_pb2.OutputChunk" href="#connpy.grpc.remote_plugin_pb2.OutputChunk">OutputChunk</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.remote_plugin_pb2.OutputChunk.DESCRIPTOR" href="#connpy.grpc.remote_plugin_pb2.OutputChunk.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.remote_plugin_pb2.PluginInvokeRequest" href="#connpy.grpc.remote_plugin_pb2.PluginInvokeRequest">PluginInvokeRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.remote_plugin_pb2.PluginInvokeRequest.DESCRIPTOR" href="#connpy.grpc.remote_plugin_pb2.PluginInvokeRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.remote_plugin_pb2.StringResponse" href="#connpy.grpc.remote_plugin_pb2.StringResponse">StringResponse</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.remote_plugin_pb2.StringResponse.DESCRIPTOR" href="#connpy.grpc.remote_plugin_pb2.StringResponse.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,372 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.grpc.remote_plugin_pb2_grpc API documentation</title>
|
||||
<meta name="description" content="Client and server classes corresponding to protobuf-defined services.">
|
||||
<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.grpc.remote_plugin_pb2_grpc</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
<p>Client and server classes corresponding to protobuf-defined services.</p>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-functions">Functions</h2>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.remote_plugin_pb2_grpc.add_RemotePluginServiceServicer_to_server"><code class="name flex">
|
||||
<span>def <span class="ident">add_RemotePluginServiceServicer_to_server</span></span>(<span>servicer, server)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def add_RemotePluginServiceServicer_to_server(servicer, server):
|
||||
rpc_method_handlers = {
|
||||
'get_plugin_source': grpc.unary_unary_rpc_method_handler(
|
||||
servicer.get_plugin_source,
|
||||
request_deserializer=remote__plugin__pb2.IdRequest.FromString,
|
||||
response_serializer=remote__plugin__pb2.StringResponse.SerializeToString,
|
||||
),
|
||||
'invoke_plugin': grpc.unary_stream_rpc_method_handler(
|
||||
servicer.invoke_plugin,
|
||||
request_deserializer=remote__plugin__pb2.PluginInvokeRequest.FromString,
|
||||
response_serializer=remote__plugin__pb2.OutputChunk.SerializeToString,
|
||||
),
|
||||
}
|
||||
generic_handler = grpc.method_handlers_generic_handler(
|
||||
'connpy_remote.RemotePluginService', rpc_method_handlers)
|
||||
server.add_generic_rpc_handlers((generic_handler,))
|
||||
server.add_registered_method_handlers('connpy_remote.RemotePluginService', rpc_method_handlers)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.remote_plugin_pb2_grpc.RemotePluginService"><code class="flex name class">
|
||||
<span>class <span class="ident">RemotePluginService</span></span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class RemotePluginService(object):
|
||||
"""Missing associated documentation comment in .proto file."""
|
||||
|
||||
@staticmethod
|
||||
def get_plugin_source(request,
|
||||
target,
|
||||
options=(),
|
||||
channel_credentials=None,
|
||||
call_credentials=None,
|
||||
insecure=False,
|
||||
compression=None,
|
||||
wait_for_ready=None,
|
||||
timeout=None,
|
||||
metadata=None):
|
||||
return grpc.experimental.unary_unary(
|
||||
request,
|
||||
target,
|
||||
'/connpy_remote.RemotePluginService/get_plugin_source',
|
||||
remote__plugin__pb2.IdRequest.SerializeToString,
|
||||
remote__plugin__pb2.StringResponse.FromString,
|
||||
options,
|
||||
channel_credentials,
|
||||
insecure,
|
||||
call_credentials,
|
||||
compression,
|
||||
wait_for_ready,
|
||||
timeout,
|
||||
metadata,
|
||||
_registered_method=True)
|
||||
|
||||
@staticmethod
|
||||
def invoke_plugin(request,
|
||||
target,
|
||||
options=(),
|
||||
channel_credentials=None,
|
||||
call_credentials=None,
|
||||
insecure=False,
|
||||
compression=None,
|
||||
wait_for_ready=None,
|
||||
timeout=None,
|
||||
metadata=None):
|
||||
return grpc.experimental.unary_stream(
|
||||
request,
|
||||
target,
|
||||
'/connpy_remote.RemotePluginService/invoke_plugin',
|
||||
remote__plugin__pb2.PluginInvokeRequest.SerializeToString,
|
||||
remote__plugin__pb2.OutputChunk.FromString,
|
||||
options,
|
||||
channel_credentials,
|
||||
insecure,
|
||||
call_credentials,
|
||||
compression,
|
||||
wait_for_ready,
|
||||
timeout,
|
||||
metadata,
|
||||
_registered_method=True)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Missing associated documentation comment in .proto file.</p></div>
|
||||
<h3>Static methods</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.remote_plugin_pb2_grpc.RemotePluginService.get_plugin_source"><code class="name flex">
|
||||
<span>def <span class="ident">get_plugin_source</span></span>(<span>request,<br>target,<br>options=(),<br>channel_credentials=None,<br>call_credentials=None,<br>insecure=False,<br>compression=None,<br>wait_for_ready=None,<br>timeout=None,<br>metadata=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@staticmethod
|
||||
def get_plugin_source(request,
|
||||
target,
|
||||
options=(),
|
||||
channel_credentials=None,
|
||||
call_credentials=None,
|
||||
insecure=False,
|
||||
compression=None,
|
||||
wait_for_ready=None,
|
||||
timeout=None,
|
||||
metadata=None):
|
||||
return grpc.experimental.unary_unary(
|
||||
request,
|
||||
target,
|
||||
'/connpy_remote.RemotePluginService/get_plugin_source',
|
||||
remote__plugin__pb2.IdRequest.SerializeToString,
|
||||
remote__plugin__pb2.StringResponse.FromString,
|
||||
options,
|
||||
channel_credentials,
|
||||
insecure,
|
||||
call_credentials,
|
||||
compression,
|
||||
wait_for_ready,
|
||||
timeout,
|
||||
metadata,
|
||||
_registered_method=True)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.remote_plugin_pb2_grpc.RemotePluginService.invoke_plugin"><code class="name flex">
|
||||
<span>def <span class="ident">invoke_plugin</span></span>(<span>request,<br>target,<br>options=(),<br>channel_credentials=None,<br>call_credentials=None,<br>insecure=False,<br>compression=None,<br>wait_for_ready=None,<br>timeout=None,<br>metadata=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@staticmethod
|
||||
def invoke_plugin(request,
|
||||
target,
|
||||
options=(),
|
||||
channel_credentials=None,
|
||||
call_credentials=None,
|
||||
insecure=False,
|
||||
compression=None,
|
||||
wait_for_ready=None,
|
||||
timeout=None,
|
||||
metadata=None):
|
||||
return grpc.experimental.unary_stream(
|
||||
request,
|
||||
target,
|
||||
'/connpy_remote.RemotePluginService/invoke_plugin',
|
||||
remote__plugin__pb2.PluginInvokeRequest.SerializeToString,
|
||||
remote__plugin__pb2.OutputChunk.FromString,
|
||||
options,
|
||||
channel_credentials,
|
||||
insecure,
|
||||
call_credentials,
|
||||
compression,
|
||||
wait_for_ready,
|
||||
timeout,
|
||||
metadata,
|
||||
_registered_method=True)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.remote_plugin_pb2_grpc.RemotePluginServiceServicer"><code class="flex name class">
|
||||
<span>class <span class="ident">RemotePluginServiceServicer</span></span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class RemotePluginServiceServicer(object):
|
||||
"""Missing associated documentation comment in .proto file."""
|
||||
|
||||
def get_plugin_source(self, request, context):
|
||||
"""Missing associated documentation comment in .proto file."""
|
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||
context.set_details('Method not implemented!')
|
||||
raise NotImplementedError('Method not implemented!')
|
||||
|
||||
def invoke_plugin(self, request, context):
|
||||
"""Missing associated documentation comment in .proto file."""
|
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||
context.set_details('Method not implemented!')
|
||||
raise NotImplementedError('Method not implemented!')</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Missing associated documentation comment in .proto file.</p></div>
|
||||
<h3>Subclasses</h3>
|
||||
<ul class="hlist">
|
||||
<li><a title="connpy.grpc.server.PluginServicer" href="server.html#connpy.grpc.server.PluginServicer">PluginServicer</a></li>
|
||||
</ul>
|
||||
<h3>Methods</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.remote_plugin_pb2_grpc.RemotePluginServiceServicer.get_plugin_source"><code class="name flex">
|
||||
<span>def <span class="ident">get_plugin_source</span></span>(<span>self, request, context)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def get_plugin_source(self, request, context):
|
||||
"""Missing associated documentation comment in .proto file."""
|
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||
context.set_details('Method not implemented!')
|
||||
raise NotImplementedError('Method not implemented!')</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Missing associated documentation comment in .proto file.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.remote_plugin_pb2_grpc.RemotePluginServiceServicer.invoke_plugin"><code class="name flex">
|
||||
<span>def <span class="ident">invoke_plugin</span></span>(<span>self, request, context)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def invoke_plugin(self, request, context):
|
||||
"""Missing associated documentation comment in .proto file."""
|
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||
context.set_details('Method not implemented!')
|
||||
raise NotImplementedError('Method not implemented!')</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Missing associated documentation comment in .proto file.</p></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.remote_plugin_pb2_grpc.RemotePluginServiceStub"><code class="flex name class">
|
||||
<span>class <span class="ident">RemotePluginServiceStub</span></span>
|
||||
<span>(</span><span>channel)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class RemotePluginServiceStub(object):
|
||||
"""Missing associated documentation comment in .proto file."""
|
||||
|
||||
def __init__(self, channel):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
channel: A grpc.Channel.
|
||||
"""
|
||||
self.get_plugin_source = channel.unary_unary(
|
||||
'/connpy_remote.RemotePluginService/get_plugin_source',
|
||||
request_serializer=remote__plugin__pb2.IdRequest.SerializeToString,
|
||||
response_deserializer=remote__plugin__pb2.StringResponse.FromString,
|
||||
_registered_method=True)
|
||||
self.invoke_plugin = channel.unary_stream(
|
||||
'/connpy_remote.RemotePluginService/invoke_plugin',
|
||||
request_serializer=remote__plugin__pb2.PluginInvokeRequest.SerializeToString,
|
||||
response_deserializer=remote__plugin__pb2.OutputChunk.FromString,
|
||||
_registered_method=True)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Missing associated documentation comment in .proto file.</p>
|
||||
<p>Constructor.</p>
|
||||
<h2 id="args">Args</h2>
|
||||
<dl>
|
||||
<dt><strong><code>channel</code></strong></dt>
|
||||
<dd>A grpc.Channel.</dd>
|
||||
</dl></div>
|
||||
</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.grpc" href="index.html">connpy.grpc</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-functions">Functions</a></h3>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.remote_plugin_pb2_grpc.add_RemotePluginServiceServicer_to_server" href="#connpy.grpc.remote_plugin_pb2_grpc.add_RemotePluginServiceServicer_to_server">add_RemotePluginServiceServicer_to_server</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.remote_plugin_pb2_grpc.RemotePluginService" href="#connpy.grpc.remote_plugin_pb2_grpc.RemotePluginService">RemotePluginService</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.remote_plugin_pb2_grpc.RemotePluginService.get_plugin_source" href="#connpy.grpc.remote_plugin_pb2_grpc.RemotePluginService.get_plugin_source">get_plugin_source</a></code></li>
|
||||
<li><code><a title="connpy.grpc.remote_plugin_pb2_grpc.RemotePluginService.invoke_plugin" href="#connpy.grpc.remote_plugin_pb2_grpc.RemotePluginService.invoke_plugin">invoke_plugin</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.remote_plugin_pb2_grpc.RemotePluginServiceServicer" href="#connpy.grpc.remote_plugin_pb2_grpc.RemotePluginServiceServicer">RemotePluginServiceServicer</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.remote_plugin_pb2_grpc.RemotePluginServiceServicer.get_plugin_source" href="#connpy.grpc.remote_plugin_pb2_grpc.RemotePluginServiceServicer.get_plugin_source">get_plugin_source</a></code></li>
|
||||
<li><code><a title="connpy.grpc.remote_plugin_pb2_grpc.RemotePluginServiceServicer.invoke_plugin" href="#connpy.grpc.remote_plugin_pb2_grpc.RemotePluginServiceServicer.invoke_plugin">invoke_plugin</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc.remote_plugin_pb2_grpc.RemotePluginServiceStub" href="#connpy.grpc.remote_plugin_pb2_grpc.RemotePluginServiceStub">RemotePluginServiceStub</a></code></h4>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,144 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.grpc.utils 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.grpc.utils</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-functions">Functions</h2>
|
||||
<dl>
|
||||
<dt id="connpy.grpc.utils.from_struct"><code class="name flex">
|
||||
<span>def <span class="ident">from_struct</span></span>(<span>struct)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def from_struct(struct):
|
||||
if not struct:
|
||||
return {}
|
||||
return json_format.MessageToDict(struct, preserving_proto_field_name=True)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.utils.from_value"><code class="name flex">
|
||||
<span>def <span class="ident">from_value</span></span>(<span>val)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def from_value(val):
|
||||
if not val.HasField("kind"):
|
||||
return None
|
||||
return json.loads(json_format.MessageToJson(val))</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.utils.to_struct"><code class="name flex">
|
||||
<span>def <span class="ident">to_struct</span></span>(<span>obj)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def to_struct(obj):
|
||||
if not obj:
|
||||
return Struct()
|
||||
s = Struct()
|
||||
json_format.ParseDict(obj, s)
|
||||
return s</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.grpc.utils.to_value"><code class="name flex">
|
||||
<span>def <span class="ident">to_value</span></span>(<span>obj)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def to_value(obj):
|
||||
if obj is None:
|
||||
v = Value()
|
||||
v.null_value = 0
|
||||
return v
|
||||
json_str = json.dumps(obj)
|
||||
v = Value()
|
||||
json_format.Parse(json_str, v)
|
||||
return v</code></pre>
|
||||
</details>
|
||||
<div class="desc"></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.grpc" href="index.html">connpy.grpc</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-functions">Functions</a></h3>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc.utils.from_struct" href="#connpy.grpc.utils.from_struct">from_struct</a></code></li>
|
||||
<li><code><a title="connpy.grpc.utils.from_value" href="#connpy.grpc.utils.from_value">from_value</a></code></li>
|
||||
<li><code><a title="connpy.grpc.utils.to_struct" href="#connpy.grpc.utils.to_struct">to_struct</a></code></li>
|
||||
<li><code><a title="connpy.grpc.utils.to_value" href="#connpy.grpc.utils.to_value">to_value</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.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
+648
-363
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,271 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.services.ai_service 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.services.ai_service</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
||||
<dl>
|
||||
<dt id="connpy.services.ai_service.AIService"><code class="flex name class">
|
||||
<span>class <span class="ident">AIService</span></span>
|
||||
<span>(</span><span>config=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class AIService(BaseService):
|
||||
"""Business logic for interacting with AI agents and LLM configurations."""
|
||||
|
||||
def ask(self, input_text, dryrun=False, chat_history=None, status=None, debug=False, session_id=None, console=None, chunk_callback=None, confirm_handler=None, trust=False, **overrides):
|
||||
"""Send a prompt to the AI agent."""
|
||||
from connpy.ai import ai
|
||||
agent = ai(self.config, console=console, confirm_handler=confirm_handler, trust=trust, **overrides)
|
||||
return agent.ask(input_text, dryrun, chat_history, status=status, debug=debug, session_id=session_id, chunk_callback=chunk_callback)
|
||||
|
||||
|
||||
def confirm(self, input_text, console=None):
|
||||
"""Ask for a safe confirmation of an action."""
|
||||
from connpy.ai import ai
|
||||
agent = ai(self.config, console=console)
|
||||
return agent.confirm(input_text)
|
||||
|
||||
|
||||
def list_sessions(self):
|
||||
"""Return a list of all saved AI sessions."""
|
||||
from connpy.ai import ai
|
||||
agent = ai(self.config)
|
||||
return agent._get_sessions()
|
||||
|
||||
def delete_session(self, session_id):
|
||||
"""Delete an AI session by ID."""
|
||||
import os
|
||||
sessions_dir = os.path.join(self.config.defaultdir, "ai_sessions")
|
||||
path = os.path.join(sessions_dir, f"{session_id}.json")
|
||||
if os.path.exists(path):
|
||||
os.remove(path)
|
||||
else:
|
||||
raise InvalidConfigurationError(f"Session '{session_id}' not found.")
|
||||
|
||||
def configure_provider(self, provider, model=None, api_key=None):
|
||||
"""Update AI provider settings in the configuration."""
|
||||
settings = self.config.config.get("ai", {})
|
||||
if model:
|
||||
settings[f"{provider}_model"] = model
|
||||
if api_key:
|
||||
settings[f"{provider}_api_key"] = api_key
|
||||
|
||||
self.config.config["ai"] = settings
|
||||
self.config._saveconfig(self.config.file)
|
||||
|
||||
def load_session_data(self, session_id):
|
||||
"""Load a session's raw data by ID."""
|
||||
from connpy.ai import ai
|
||||
agent = ai(self.config)
|
||||
return agent.load_session_data(session_id)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Business logic for interacting with AI agents and LLM configurations.</p>
|
||||
<p>Initialize the service.</p>
|
||||
<h2 id="args">Args</h2>
|
||||
<dl>
|
||||
<dt><strong><code>config</code></strong></dt>
|
||||
<dd>An instance of configfile (or None to instantiate a new one/use global context).</dd>
|
||||
</dl></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li><a title="connpy.services.base.BaseService" href="base.html#connpy.services.base.BaseService">BaseService</a></li>
|
||||
</ul>
|
||||
<h3>Methods</h3>
|
||||
<dl>
|
||||
<dt id="connpy.services.ai_service.AIService.ask"><code class="name flex">
|
||||
<span>def <span class="ident">ask</span></span>(<span>self,<br>input_text,<br>dryrun=False,<br>chat_history=None,<br>status=None,<br>debug=False,<br>session_id=None,<br>console=None,<br>chunk_callback=None,<br>confirm_handler=None,<br>trust=False,<br>**overrides)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def ask(self, input_text, dryrun=False, chat_history=None, status=None, debug=False, session_id=None, console=None, chunk_callback=None, confirm_handler=None, trust=False, **overrides):
|
||||
"""Send a prompt to the AI agent."""
|
||||
from connpy.ai import ai
|
||||
agent = ai(self.config, console=console, confirm_handler=confirm_handler, trust=trust, **overrides)
|
||||
return agent.ask(input_text, dryrun, chat_history, status=status, debug=debug, session_id=session_id, chunk_callback=chunk_callback)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Send a prompt to the AI agent.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.ai_service.AIService.configure_provider"><code class="name flex">
|
||||
<span>def <span class="ident">configure_provider</span></span>(<span>self, provider, model=None, api_key=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def configure_provider(self, provider, model=None, api_key=None):
|
||||
"""Update AI provider settings in the configuration."""
|
||||
settings = self.config.config.get("ai", {})
|
||||
if model:
|
||||
settings[f"{provider}_model"] = model
|
||||
if api_key:
|
||||
settings[f"{provider}_api_key"] = api_key
|
||||
|
||||
self.config.config["ai"] = settings
|
||||
self.config._saveconfig(self.config.file)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Update AI provider settings in the configuration.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.ai_service.AIService.confirm"><code class="name flex">
|
||||
<span>def <span class="ident">confirm</span></span>(<span>self, input_text, console=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def confirm(self, input_text, console=None):
|
||||
"""Ask for a safe confirmation of an action."""
|
||||
from connpy.ai import ai
|
||||
agent = ai(self.config, console=console)
|
||||
return agent.confirm(input_text)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Ask for a safe confirmation of an action.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.ai_service.AIService.delete_session"><code class="name flex">
|
||||
<span>def <span class="ident">delete_session</span></span>(<span>self, session_id)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def delete_session(self, session_id):
|
||||
"""Delete an AI session by ID."""
|
||||
import os
|
||||
sessions_dir = os.path.join(self.config.defaultdir, "ai_sessions")
|
||||
path = os.path.join(sessions_dir, f"{session_id}.json")
|
||||
if os.path.exists(path):
|
||||
os.remove(path)
|
||||
else:
|
||||
raise InvalidConfigurationError(f"Session '{session_id}' not found.")</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Delete an AI session by ID.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.ai_service.AIService.list_sessions"><code class="name flex">
|
||||
<span>def <span class="ident">list_sessions</span></span>(<span>self)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def list_sessions(self):
|
||||
"""Return a list of all saved AI sessions."""
|
||||
from connpy.ai import ai
|
||||
agent = ai(self.config)
|
||||
return agent._get_sessions()</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Return a list of all saved AI sessions.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.ai_service.AIService.load_session_data"><code class="name flex">
|
||||
<span>def <span class="ident">load_session_data</span></span>(<span>self, session_id)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def load_session_data(self, session_id):
|
||||
"""Load a session's raw data by ID."""
|
||||
from connpy.ai import ai
|
||||
agent = ai(self.config)
|
||||
return agent.load_session_data(session_id)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Load a session's raw data by ID.</p></div>
|
||||
</dd>
|
||||
</dl>
|
||||
<h3>Inherited members</h3>
|
||||
<ul class="hlist">
|
||||
<li><code><b><a title="connpy.services.base.BaseService" href="base.html#connpy.services.base.BaseService">BaseService</a></b></code>:
|
||||
<ul class="hlist">
|
||||
<li><code><a title="connpy.services.base.BaseService.set_reserved_names" href="base.html#connpy.services.base.BaseService.set_reserved_names">set_reserved_names</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</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.services" href="index.html">connpy.services</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<h4><code><a title="connpy.services.ai_service.AIService" href="#connpy.services.ai_service.AIService">AIService</a></code></h4>
|
||||
<ul class="two-column">
|
||||
<li><code><a title="connpy.services.ai_service.AIService.ask" href="#connpy.services.ai_service.AIService.ask">ask</a></code></li>
|
||||
<li><code><a title="connpy.services.ai_service.AIService.configure_provider" href="#connpy.services.ai_service.AIService.configure_provider">configure_provider</a></code></li>
|
||||
<li><code><a title="connpy.services.ai_service.AIService.confirm" href="#connpy.services.ai_service.AIService.confirm">confirm</a></code></li>
|
||||
<li><code><a title="connpy.services.ai_service.AIService.delete_session" href="#connpy.services.ai_service.AIService.delete_session">delete_session</a></code></li>
|
||||
<li><code><a title="connpy.services.ai_service.AIService.list_sessions" href="#connpy.services.ai_service.AIService.list_sessions">list_sessions</a></code></li>
|
||||
<li><code><a title="connpy.services.ai_service.AIService.load_session_data" href="#connpy.services.ai_service.AIService.load_session_data">load_session_data</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,158 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.services.base 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.services.base</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
||||
<dl>
|
||||
<dt id="connpy.services.base.BaseService"><code class="flex name class">
|
||||
<span>class <span class="ident">BaseService</span></span>
|
||||
<span>(</span><span>config=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class BaseService:
|
||||
"""Base class for all connpy services, providing common configuration access."""
|
||||
|
||||
def __init__(self, config=None):
|
||||
"""
|
||||
Initialize the service.
|
||||
|
||||
Args:
|
||||
config: An instance of configfile (or None to instantiate a new one/use global context).
|
||||
"""
|
||||
from connpy import configfile
|
||||
self.config = config or configfile()
|
||||
self.hooks = MethodHook
|
||||
self.reserved_names = []
|
||||
|
||||
def set_reserved_names(self, names):
|
||||
"""Inject a list of reserved names (e.g. from the CLI)."""
|
||||
self.reserved_names = names
|
||||
|
||||
def _validate_node_name(self, unique_id):
|
||||
"""Check if the node name in unique_id is reserved."""
|
||||
from .exceptions import ReservedNameError
|
||||
if not self.reserved_names:
|
||||
return
|
||||
|
||||
uniques = self.config._explode_unique(unique_id)
|
||||
if uniques and "id" in uniques:
|
||||
# We only validate the 'id' (the actual node name), folders are prefixed with @
|
||||
node_name = uniques["id"]
|
||||
if node_name in self.reserved_names:
|
||||
raise ReservedNameError(f"Node name '{node_name}' is a reserved command.")</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Base class for all connpy services, providing common configuration access.</p>
|
||||
<p>Initialize the service.</p>
|
||||
<h2 id="args">Args</h2>
|
||||
<dl>
|
||||
<dt><strong><code>config</code></strong></dt>
|
||||
<dd>An instance of configfile (or None to instantiate a new one/use global context).</dd>
|
||||
</dl></div>
|
||||
<h3>Subclasses</h3>
|
||||
<ul class="hlist">
|
||||
<li><a title="connpy.services.ai_service.AIService" href="ai_service.html#connpy.services.ai_service.AIService">AIService</a></li>
|
||||
<li><a title="connpy.services.config_service.ConfigService" href="config_service.html#connpy.services.config_service.ConfigService">ConfigService</a></li>
|
||||
<li><a title="connpy.services.context_service.ContextService" href="context_service.html#connpy.services.context_service.ContextService">ContextService</a></li>
|
||||
<li><a title="connpy.services.execution_service.ExecutionService" href="execution_service.html#connpy.services.execution_service.ExecutionService">ExecutionService</a></li>
|
||||
<li><a title="connpy.services.import_export_service.ImportExportService" href="import_export_service.html#connpy.services.import_export_service.ImportExportService">ImportExportService</a></li>
|
||||
<li><a title="connpy.services.node_service.NodeService" href="node_service.html#connpy.services.node_service.NodeService">NodeService</a></li>
|
||||
<li><a title="connpy.services.plugin_service.PluginService" href="plugin_service.html#connpy.services.plugin_service.PluginService">PluginService</a></li>
|
||||
<li><a title="connpy.services.profile_service.ProfileService" href="profile_service.html#connpy.services.profile_service.ProfileService">ProfileService</a></li>
|
||||
<li><a title="connpy.services.sync_service.SyncService" href="sync_service.html#connpy.services.sync_service.SyncService">SyncService</a></li>
|
||||
<li><a title="connpy.services.system_service.SystemService" href="system_service.html#connpy.services.system_service.SystemService">SystemService</a></li>
|
||||
</ul>
|
||||
<h3>Methods</h3>
|
||||
<dl>
|
||||
<dt id="connpy.services.base.BaseService.set_reserved_names"><code class="name flex">
|
||||
<span>def <span class="ident">set_reserved_names</span></span>(<span>self, names)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def set_reserved_names(self, names):
|
||||
"""Inject a list of reserved names (e.g. from the CLI)."""
|
||||
self.reserved_names = names</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Inject a list of reserved names (e.g. from the CLI).</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.services" href="index.html">connpy.services</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<h4><code><a title="connpy.services.base.BaseService" href="#connpy.services.base.BaseService">BaseService</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.services.base.BaseService.set_reserved_names" href="#connpy.services.base.BaseService.set_reserved_names">set_reserved_names</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,317 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.services.config_service 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.services.config_service</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
||||
<dl>
|
||||
<dt id="connpy.services.config_service.ConfigService"><code class="flex name class">
|
||||
<span>class <span class="ident">ConfigService</span></span>
|
||||
<span>(</span><span>config=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class ConfigService(BaseService):
|
||||
"""Business logic for general application settings and state configuration."""
|
||||
|
||||
def get_settings(self) -> Dict[str, Any]:
|
||||
"""Get the global configuration settings block."""
|
||||
settings = self.config.config.copy()
|
||||
settings["configfolder"] = self.config.defaultdir
|
||||
return settings
|
||||
|
||||
def get_default_dir(self) -> str:
|
||||
"""Get the default configuration directory."""
|
||||
return self.config.defaultdir
|
||||
|
||||
def set_config_folder(self, folder_path: str):
|
||||
"""Set the default location for config file by writing to ~/.config/conn/.folder"""
|
||||
if not os.path.isdir(folder_path):
|
||||
raise ConnpyError(f"readable_dir:{folder_path} is not a valid path")
|
||||
|
||||
pathfile = os.path.join(self.config.anchor_path, ".folder")
|
||||
folder = os.path.abspath(folder_path).rstrip('/')
|
||||
|
||||
try:
|
||||
with open(pathfile, "w") as f:
|
||||
f.write(str(folder))
|
||||
except Exception as e:
|
||||
raise ConnpyError(f"Failed to save config folder: {e}")
|
||||
|
||||
def update_setting(self, key, value):
|
||||
"""Update a setting in the configuration file."""
|
||||
self.config.config[key] = value
|
||||
self.config._saveconfig(self.config.file)
|
||||
|
||||
def encrypt_password(self, password):
|
||||
"""Encrypt a password using the application's configuration encryption key."""
|
||||
return self.config.encrypt(password)
|
||||
|
||||
def apply_theme_from_file(self, theme_input):
|
||||
"""Apply 'dark', 'light' theme or load a YAML theme file and save it to the configuration."""
|
||||
import yaml
|
||||
from ..printer import STYLES, LIGHT_THEME
|
||||
|
||||
if theme_input == "dark":
|
||||
valid_styles = {}
|
||||
self.update_setting("theme", valid_styles)
|
||||
return valid_styles
|
||||
elif theme_input == "light":
|
||||
valid_styles = LIGHT_THEME.copy()
|
||||
self.update_setting("theme", valid_styles)
|
||||
return valid_styles
|
||||
|
||||
if not os.path.exists(theme_input):
|
||||
raise InvalidConfigurationError(f"Theme file '{theme_input}' not found.")
|
||||
|
||||
try:
|
||||
with open(theme_input, 'r') as f:
|
||||
user_styles = yaml.safe_load(f)
|
||||
except Exception as e:
|
||||
raise InvalidConfigurationError(f"Failed to parse theme file: {e}")
|
||||
|
||||
if not isinstance(user_styles, dict):
|
||||
raise InvalidConfigurationError("Theme file must be a YAML dictionary.")
|
||||
|
||||
# Filter for valid styles only (prevent junk in config)
|
||||
valid_styles = {k: v for k, v in user_styles.items() if k in STYLES}
|
||||
|
||||
if not valid_styles:
|
||||
raise InvalidConfigurationError("No valid style keys found in theme file.")
|
||||
|
||||
# Persist and return merged styles
|
||||
self.update_setting("theme", valid_styles)
|
||||
return valid_styles</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Business logic for general application settings and state configuration.</p>
|
||||
<p>Initialize the service.</p>
|
||||
<h2 id="args">Args</h2>
|
||||
<dl>
|
||||
<dt><strong><code>config</code></strong></dt>
|
||||
<dd>An instance of configfile (or None to instantiate a new one/use global context).</dd>
|
||||
</dl></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li><a title="connpy.services.base.BaseService" href="base.html#connpy.services.base.BaseService">BaseService</a></li>
|
||||
</ul>
|
||||
<h3>Methods</h3>
|
||||
<dl>
|
||||
<dt id="connpy.services.config_service.ConfigService.apply_theme_from_file"><code class="name flex">
|
||||
<span>def <span class="ident">apply_theme_from_file</span></span>(<span>self, theme_input)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def apply_theme_from_file(self, theme_input):
|
||||
"""Apply 'dark', 'light' theme or load a YAML theme file and save it to the configuration."""
|
||||
import yaml
|
||||
from ..printer import STYLES, LIGHT_THEME
|
||||
|
||||
if theme_input == "dark":
|
||||
valid_styles = {}
|
||||
self.update_setting("theme", valid_styles)
|
||||
return valid_styles
|
||||
elif theme_input == "light":
|
||||
valid_styles = LIGHT_THEME.copy()
|
||||
self.update_setting("theme", valid_styles)
|
||||
return valid_styles
|
||||
|
||||
if not os.path.exists(theme_input):
|
||||
raise InvalidConfigurationError(f"Theme file '{theme_input}' not found.")
|
||||
|
||||
try:
|
||||
with open(theme_input, 'r') as f:
|
||||
user_styles = yaml.safe_load(f)
|
||||
except Exception as e:
|
||||
raise InvalidConfigurationError(f"Failed to parse theme file: {e}")
|
||||
|
||||
if not isinstance(user_styles, dict):
|
||||
raise InvalidConfigurationError("Theme file must be a YAML dictionary.")
|
||||
|
||||
# Filter for valid styles only (prevent junk in config)
|
||||
valid_styles = {k: v for k, v in user_styles.items() if k in STYLES}
|
||||
|
||||
if not valid_styles:
|
||||
raise InvalidConfigurationError("No valid style keys found in theme file.")
|
||||
|
||||
# Persist and return merged styles
|
||||
self.update_setting("theme", valid_styles)
|
||||
return valid_styles</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Apply 'dark', 'light' theme or load a YAML theme file and save it to the configuration.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.config_service.ConfigService.encrypt_password"><code class="name flex">
|
||||
<span>def <span class="ident">encrypt_password</span></span>(<span>self, password)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def encrypt_password(self, password):
|
||||
"""Encrypt a password using the application's configuration encryption key."""
|
||||
return self.config.encrypt(password)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Encrypt a password using the application's configuration encryption key.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.config_service.ConfigService.get_default_dir"><code class="name flex">
|
||||
<span>def <span class="ident">get_default_dir</span></span>(<span>self) ‑> str</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def get_default_dir(self) -> str:
|
||||
"""Get the default configuration directory."""
|
||||
return self.config.defaultdir</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Get the default configuration directory.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.config_service.ConfigService.get_settings"><code class="name flex">
|
||||
<span>def <span class="ident">get_settings</span></span>(<span>self) ‑> Dict[str, Any]</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def get_settings(self) -> Dict[str, Any]:
|
||||
"""Get the global configuration settings block."""
|
||||
settings = self.config.config.copy()
|
||||
settings["configfolder"] = self.config.defaultdir
|
||||
return settings</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Get the global configuration settings block.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.config_service.ConfigService.set_config_folder"><code class="name flex">
|
||||
<span>def <span class="ident">set_config_folder</span></span>(<span>self, folder_path: str)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def set_config_folder(self, folder_path: str):
|
||||
"""Set the default location for config file by writing to ~/.config/conn/.folder"""
|
||||
if not os.path.isdir(folder_path):
|
||||
raise ConnpyError(f"readable_dir:{folder_path} is not a valid path")
|
||||
|
||||
pathfile = os.path.join(self.config.anchor_path, ".folder")
|
||||
folder = os.path.abspath(folder_path).rstrip('/')
|
||||
|
||||
try:
|
||||
with open(pathfile, "w") as f:
|
||||
f.write(str(folder))
|
||||
except Exception as e:
|
||||
raise ConnpyError(f"Failed to save config folder: {e}")</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Set the default location for config file by writing to ~/.config/conn/.folder</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.config_service.ConfigService.update_setting"><code class="name flex">
|
||||
<span>def <span class="ident">update_setting</span></span>(<span>self, key, value)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def update_setting(self, key, value):
|
||||
"""Update a setting in the configuration file."""
|
||||
self.config.config[key] = value
|
||||
self.config._saveconfig(self.config.file)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Update a setting in the configuration file.</p></div>
|
||||
</dd>
|
||||
</dl>
|
||||
<h3>Inherited members</h3>
|
||||
<ul class="hlist">
|
||||
<li><code><b><a title="connpy.services.base.BaseService" href="base.html#connpy.services.base.BaseService">BaseService</a></b></code>:
|
||||
<ul class="hlist">
|
||||
<li><code><a title="connpy.services.base.BaseService.set_reserved_names" href="base.html#connpy.services.base.BaseService.set_reserved_names">set_reserved_names</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</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.services" href="index.html">connpy.services</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<h4><code><a title="connpy.services.config_service.ConfigService" href="#connpy.services.config_service.ConfigService">ConfigService</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.services.config_service.ConfigService.apply_theme_from_file" href="#connpy.services.config_service.ConfigService.apply_theme_from_file">apply_theme_from_file</a></code></li>
|
||||
<li><code><a title="connpy.services.config_service.ConfigService.encrypt_password" href="#connpy.services.config_service.ConfigService.encrypt_password">encrypt_password</a></code></li>
|
||||
<li><code><a title="connpy.services.config_service.ConfigService.get_default_dir" href="#connpy.services.config_service.ConfigService.get_default_dir">get_default_dir</a></code></li>
|
||||
<li><code><a title="connpy.services.config_service.ConfigService.get_settings" href="#connpy.services.config_service.ConfigService.get_settings">get_settings</a></code></li>
|
||||
<li><code><a title="connpy.services.config_service.ConfigService.set_config_folder" href="#connpy.services.config_service.ConfigService.set_config_folder">set_config_folder</a></code></li>
|
||||
<li><code><a title="connpy.services.config_service.ConfigService.update_setting" href="#connpy.services.config_service.ConfigService.update_setting">update_setting</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,376 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.services.context_service 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.services.context_service</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
||||
<dl>
|
||||
<dt id="connpy.services.context_service.ContextService"><code class="flex name class">
|
||||
<span>class <span class="ident">ContextService</span></span>
|
||||
<span>(</span><span>config=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class ContextService(BaseService):
|
||||
"""Business logic for managing and applying regex-based contexts locally."""
|
||||
|
||||
@property
|
||||
def contexts(self) -> Dict[str, List[str]]:
|
||||
return self.config.config.get("contexts", {"all": [".*"]})
|
||||
|
||||
@property
|
||||
def current_context(self) -> str:
|
||||
return self.config.config.get("current_context", "all")
|
||||
|
||||
def list_contexts(self) -> List[Dict[str, Any]]:
|
||||
result = []
|
||||
for name in self.contexts.keys():
|
||||
result.append({
|
||||
"name": name,
|
||||
"active": (name == self.current_context),
|
||||
"regexes": self.contexts[name]
|
||||
})
|
||||
return result
|
||||
|
||||
def add_context(self, name: str, regexes: List[str]):
|
||||
if not name.isalnum():
|
||||
raise ValueError("Context name must be alphanumeric")
|
||||
|
||||
ctxs = self.contexts
|
||||
if name in ctxs:
|
||||
raise ValueError(f"Context '{name}' already exists")
|
||||
|
||||
ctxs[name] = regexes
|
||||
self.config.config["contexts"] = ctxs
|
||||
self.config._saveconfig(self.config.file)
|
||||
|
||||
def update_context(self, name: str, regexes: List[str]):
|
||||
if name == "all":
|
||||
raise ValueError("Cannot modify default context 'all'")
|
||||
|
||||
ctxs = self.contexts
|
||||
if name not in ctxs:
|
||||
raise ValueError(f"Context '{name}' does not exist")
|
||||
|
||||
ctxs[name] = regexes
|
||||
self.config.config["contexts"] = ctxs
|
||||
self.config._saveconfig(self.config.file)
|
||||
|
||||
def delete_context(self, name: str):
|
||||
if name == "all":
|
||||
raise ValueError("Cannot delete default context 'all'")
|
||||
if name == self.current_context:
|
||||
raise ValueError(f"Cannot delete active context '{name}'")
|
||||
|
||||
ctxs = self.contexts
|
||||
if name not in ctxs:
|
||||
raise ValueError(f"Context '{name}' does not exist")
|
||||
|
||||
del ctxs[name]
|
||||
self.config.config["contexts"] = ctxs
|
||||
self.config._saveconfig(self.config.file)
|
||||
|
||||
def set_active_context(self, name: str):
|
||||
if name not in self.contexts:
|
||||
raise ValueError(f"Context '{name}' does not exist")
|
||||
|
||||
self.config.config["current_context"] = name
|
||||
self.config._saveconfig(self.config.file)
|
||||
|
||||
def get_active_regexes(self) -> List[re.Pattern]:
|
||||
patterns = self.contexts.get(self.current_context, [".*"])
|
||||
return [re.compile(p) for p in patterns]
|
||||
|
||||
def _match_any(self, node_name: str, patterns: List[re.Pattern]) -> bool:
|
||||
return any(p.match(node_name) for p in patterns)
|
||||
|
||||
# Hook handlers for filtering
|
||||
def filter_node_list(self, *args, **kwargs):
|
||||
patterns = self.get_active_regexes()
|
||||
return [node for node in kwargs["result"] if self._match_any(node, patterns)]
|
||||
|
||||
def filter_node_dict(self, *args, **kwargs):
|
||||
patterns = self.get_active_regexes()
|
||||
return {k: v for k, v in kwargs["result"].items() if self._match_any(k, patterns)}</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Business logic for managing and applying regex-based contexts locally.</p>
|
||||
<p>Initialize the service.</p>
|
||||
<h2 id="args">Args</h2>
|
||||
<dl>
|
||||
<dt><strong><code>config</code></strong></dt>
|
||||
<dd>An instance of configfile (or None to instantiate a new one/use global context).</dd>
|
||||
</dl></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li><a title="connpy.services.base.BaseService" href="base.html#connpy.services.base.BaseService">BaseService</a></li>
|
||||
</ul>
|
||||
<h3>Instance variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.services.context_service.ContextService.contexts"><code class="name">prop <span class="ident">contexts</span> : Dict[str, List[str]]</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@property
|
||||
def contexts(self) -> Dict[str, List[str]]:
|
||||
return self.config.config.get("contexts", {"all": [".*"]})</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.context_service.ContextService.current_context"><code class="name">prop <span class="ident">current_context</span> : str</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@property
|
||||
def current_context(self) -> str:
|
||||
return self.config.config.get("current_context", "all")</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
<h3>Methods</h3>
|
||||
<dl>
|
||||
<dt id="connpy.services.context_service.ContextService.add_context"><code class="name flex">
|
||||
<span>def <span class="ident">add_context</span></span>(<span>self, name: str, regexes: List[str])</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def add_context(self, name: str, regexes: List[str]):
|
||||
if not name.isalnum():
|
||||
raise ValueError("Context name must be alphanumeric")
|
||||
|
||||
ctxs = self.contexts
|
||||
if name in ctxs:
|
||||
raise ValueError(f"Context '{name}' already exists")
|
||||
|
||||
ctxs[name] = regexes
|
||||
self.config.config["contexts"] = ctxs
|
||||
self.config._saveconfig(self.config.file)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.context_service.ContextService.delete_context"><code class="name flex">
|
||||
<span>def <span class="ident">delete_context</span></span>(<span>self, name: str)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def delete_context(self, name: str):
|
||||
if name == "all":
|
||||
raise ValueError("Cannot delete default context 'all'")
|
||||
if name == self.current_context:
|
||||
raise ValueError(f"Cannot delete active context '{name}'")
|
||||
|
||||
ctxs = self.contexts
|
||||
if name not in ctxs:
|
||||
raise ValueError(f"Context '{name}' does not exist")
|
||||
|
||||
del ctxs[name]
|
||||
self.config.config["contexts"] = ctxs
|
||||
self.config._saveconfig(self.config.file)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.context_service.ContextService.filter_node_dict"><code class="name flex">
|
||||
<span>def <span class="ident">filter_node_dict</span></span>(<span>self, *args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def filter_node_dict(self, *args, **kwargs):
|
||||
patterns = self.get_active_regexes()
|
||||
return {k: v for k, v in kwargs["result"].items() if self._match_any(k, patterns)}</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.context_service.ContextService.filter_node_list"><code class="name flex">
|
||||
<span>def <span class="ident">filter_node_list</span></span>(<span>self, *args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def filter_node_list(self, *args, **kwargs):
|
||||
patterns = self.get_active_regexes()
|
||||
return [node for node in kwargs["result"] if self._match_any(node, patterns)]</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.context_service.ContextService.get_active_regexes"><code class="name flex">
|
||||
<span>def <span class="ident">get_active_regexes</span></span>(<span>self) ‑> List[re.Pattern]</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def get_active_regexes(self) -> List[re.Pattern]:
|
||||
patterns = self.contexts.get(self.current_context, [".*"])
|
||||
return [re.compile(p) for p in patterns]</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.context_service.ContextService.list_contexts"><code class="name flex">
|
||||
<span>def <span class="ident">list_contexts</span></span>(<span>self) ‑> List[Dict[str, Any]]</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def list_contexts(self) -> List[Dict[str, Any]]:
|
||||
result = []
|
||||
for name in self.contexts.keys():
|
||||
result.append({
|
||||
"name": name,
|
||||
"active": (name == self.current_context),
|
||||
"regexes": self.contexts[name]
|
||||
})
|
||||
return result</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.context_service.ContextService.set_active_context"><code class="name flex">
|
||||
<span>def <span class="ident">set_active_context</span></span>(<span>self, name: str)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def set_active_context(self, name: str):
|
||||
if name not in self.contexts:
|
||||
raise ValueError(f"Context '{name}' does not exist")
|
||||
|
||||
self.config.config["current_context"] = name
|
||||
self.config._saveconfig(self.config.file)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.context_service.ContextService.update_context"><code class="name flex">
|
||||
<span>def <span class="ident">update_context</span></span>(<span>self, name: str, regexes: List[str])</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def update_context(self, name: str, regexes: List[str]):
|
||||
if name == "all":
|
||||
raise ValueError("Cannot modify default context 'all'")
|
||||
|
||||
ctxs = self.contexts
|
||||
if name not in ctxs:
|
||||
raise ValueError(f"Context '{name}' does not exist")
|
||||
|
||||
ctxs[name] = regexes
|
||||
self.config.config["contexts"] = ctxs
|
||||
self.config._saveconfig(self.config.file)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
<h3>Inherited members</h3>
|
||||
<ul class="hlist">
|
||||
<li><code><b><a title="connpy.services.base.BaseService" href="base.html#connpy.services.base.BaseService">BaseService</a></b></code>:
|
||||
<ul class="hlist">
|
||||
<li><code><a title="connpy.services.base.BaseService.set_reserved_names" href="base.html#connpy.services.base.BaseService.set_reserved_names">set_reserved_names</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</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.services" href="index.html">connpy.services</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<h4><code><a title="connpy.services.context_service.ContextService" href="#connpy.services.context_service.ContextService">ContextService</a></code></h4>
|
||||
<ul class="two-column">
|
||||
<li><code><a title="connpy.services.context_service.ContextService.add_context" href="#connpy.services.context_service.ContextService.add_context">add_context</a></code></li>
|
||||
<li><code><a title="connpy.services.context_service.ContextService.contexts" href="#connpy.services.context_service.ContextService.contexts">contexts</a></code></li>
|
||||
<li><code><a title="connpy.services.context_service.ContextService.current_context" href="#connpy.services.context_service.ContextService.current_context">current_context</a></code></li>
|
||||
<li><code><a title="connpy.services.context_service.ContextService.delete_context" href="#connpy.services.context_service.ContextService.delete_context">delete_context</a></code></li>
|
||||
<li><code><a title="connpy.services.context_service.ContextService.filter_node_dict" href="#connpy.services.context_service.ContextService.filter_node_dict">filter_node_dict</a></code></li>
|
||||
<li><code><a title="connpy.services.context_service.ContextService.filter_node_list" href="#connpy.services.context_service.ContextService.filter_node_list">filter_node_list</a></code></li>
|
||||
<li><code><a title="connpy.services.context_service.ContextService.get_active_regexes" href="#connpy.services.context_service.ContextService.get_active_regexes">get_active_regexes</a></code></li>
|
||||
<li><code><a title="connpy.services.context_service.ContextService.list_contexts" href="#connpy.services.context_service.ContextService.list_contexts">list_contexts</a></code></li>
|
||||
<li><code><a title="connpy.services.context_service.ContextService.set_active_context" href="#connpy.services.context_service.ContextService.set_active_context">set_active_context</a></code></li>
|
||||
<li><code><a title="connpy.services.context_service.ContextService.update_context" href="#connpy.services.context_service.ContextService.update_context">update_context</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,274 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.services.exceptions 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.services.exceptions</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
||||
<dl>
|
||||
<dt id="connpy.services.exceptions.ConnpyError"><code class="flex name class">
|
||||
<span>class <span class="ident">ConnpyError</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class ConnpyError(Exception):
|
||||
"""Base exception for all connpy services."""
|
||||
pass</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Base exception for all connpy services.</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>builtins.Exception</li>
|
||||
<li>builtins.BaseException</li>
|
||||
</ul>
|
||||
<h3>Subclasses</h3>
|
||||
<ul class="hlist">
|
||||
<li><a title="connpy.services.exceptions.ExecutionError" href="#connpy.services.exceptions.ExecutionError">ExecutionError</a></li>
|
||||
<li><a title="connpy.services.exceptions.InvalidConfigurationError" href="#connpy.services.exceptions.InvalidConfigurationError">InvalidConfigurationError</a></li>
|
||||
<li><a title="connpy.services.exceptions.NodeAlreadyExistsError" href="#connpy.services.exceptions.NodeAlreadyExistsError">NodeAlreadyExistsError</a></li>
|
||||
<li><a title="connpy.services.exceptions.NodeNotFoundError" href="#connpy.services.exceptions.NodeNotFoundError">NodeNotFoundError</a></li>
|
||||
<li><a title="connpy.services.exceptions.ProfileAlreadyExistsError" href="#connpy.services.exceptions.ProfileAlreadyExistsError">ProfileAlreadyExistsError</a></li>
|
||||
<li><a title="connpy.services.exceptions.ProfileNotFoundError" href="#connpy.services.exceptions.ProfileNotFoundError">ProfileNotFoundError</a></li>
|
||||
<li><a title="connpy.services.exceptions.ReservedNameError" href="#connpy.services.exceptions.ReservedNameError">ReservedNameError</a></li>
|
||||
</ul>
|
||||
</dd>
|
||||
<dt id="connpy.services.exceptions.ExecutionError"><code class="flex name class">
|
||||
<span>class <span class="ident">ExecutionError</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class ExecutionError(ConnpyError):
|
||||
"""Raised when an execution fails or returns error."""
|
||||
pass</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Raised when an execution fails or returns error.</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li><a title="connpy.services.exceptions.ConnpyError" href="#connpy.services.exceptions.ConnpyError">ConnpyError</a></li>
|
||||
<li>builtins.Exception</li>
|
||||
<li>builtins.BaseException</li>
|
||||
</ul>
|
||||
</dd>
|
||||
<dt id="connpy.services.exceptions.InvalidConfigurationError"><code class="flex name class">
|
||||
<span>class <span class="ident">InvalidConfigurationError</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class InvalidConfigurationError(ConnpyError):
|
||||
"""Raised when data or configuration input is invalid."""
|
||||
pass</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Raised when data or configuration input is invalid.</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li><a title="connpy.services.exceptions.ConnpyError" href="#connpy.services.exceptions.ConnpyError">ConnpyError</a></li>
|
||||
<li>builtins.Exception</li>
|
||||
<li>builtins.BaseException</li>
|
||||
</ul>
|
||||
</dd>
|
||||
<dt id="connpy.services.exceptions.NodeAlreadyExistsError"><code class="flex name class">
|
||||
<span>class <span class="ident">NodeAlreadyExistsError</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class NodeAlreadyExistsError(ConnpyError):
|
||||
"""Raised when a node or folder already exists."""
|
||||
pass</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Raised when a node or folder already exists.</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li><a title="connpy.services.exceptions.ConnpyError" href="#connpy.services.exceptions.ConnpyError">ConnpyError</a></li>
|
||||
<li>builtins.Exception</li>
|
||||
<li>builtins.BaseException</li>
|
||||
</ul>
|
||||
</dd>
|
||||
<dt id="connpy.services.exceptions.NodeNotFoundError"><code class="flex name class">
|
||||
<span>class <span class="ident">NodeNotFoundError</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class NodeNotFoundError(ConnpyError):
|
||||
"""Raised when a connection or folder is not found."""
|
||||
pass</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Raised when a connection or folder is not found.</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li><a title="connpy.services.exceptions.ConnpyError" href="#connpy.services.exceptions.ConnpyError">ConnpyError</a></li>
|
||||
<li>builtins.Exception</li>
|
||||
<li>builtins.BaseException</li>
|
||||
</ul>
|
||||
</dd>
|
||||
<dt id="connpy.services.exceptions.ProfileAlreadyExistsError"><code class="flex name class">
|
||||
<span>class <span class="ident">ProfileAlreadyExistsError</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class ProfileAlreadyExistsError(ConnpyError):
|
||||
"""Raised when a profile with the same name already exists."""
|
||||
pass</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Raised when a profile with the same name already exists.</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li><a title="connpy.services.exceptions.ConnpyError" href="#connpy.services.exceptions.ConnpyError">ConnpyError</a></li>
|
||||
<li>builtins.Exception</li>
|
||||
<li>builtins.BaseException</li>
|
||||
</ul>
|
||||
</dd>
|
||||
<dt id="connpy.services.exceptions.ProfileNotFoundError"><code class="flex name class">
|
||||
<span>class <span class="ident">ProfileNotFoundError</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class ProfileNotFoundError(ConnpyError):
|
||||
"""Raised when a profile is not found."""
|
||||
pass</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Raised when a profile is not found.</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li><a title="connpy.services.exceptions.ConnpyError" href="#connpy.services.exceptions.ConnpyError">ConnpyError</a></li>
|
||||
<li>builtins.Exception</li>
|
||||
<li>builtins.BaseException</li>
|
||||
</ul>
|
||||
</dd>
|
||||
<dt id="connpy.services.exceptions.ReservedNameError"><code class="flex name class">
|
||||
<span>class <span class="ident">ReservedNameError</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class ReservedNameError(ConnpyError):
|
||||
"""Raised when a node name conflicts with a reserved command."""
|
||||
pass</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Raised when a node name conflicts with a reserved command.</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li><a title="connpy.services.exceptions.ConnpyError" href="#connpy.services.exceptions.ConnpyError">ConnpyError</a></li>
|
||||
<li>builtins.Exception</li>
|
||||
<li>builtins.BaseException</li>
|
||||
</ul>
|
||||
</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.services" href="index.html">connpy.services</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<h4><code><a title="connpy.services.exceptions.ConnpyError" href="#connpy.services.exceptions.ConnpyError">ConnpyError</a></code></h4>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.services.exceptions.ExecutionError" href="#connpy.services.exceptions.ExecutionError">ExecutionError</a></code></h4>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.services.exceptions.InvalidConfigurationError" href="#connpy.services.exceptions.InvalidConfigurationError">InvalidConfigurationError</a></code></h4>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.services.exceptions.NodeAlreadyExistsError" href="#connpy.services.exceptions.NodeAlreadyExistsError">NodeAlreadyExistsError</a></code></h4>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.services.exceptions.NodeNotFoundError" href="#connpy.services.exceptions.NodeNotFoundError">NodeNotFoundError</a></code></h4>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.services.exceptions.ProfileAlreadyExistsError" href="#connpy.services.exceptions.ProfileAlreadyExistsError">ProfileAlreadyExistsError</a></code></h4>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.services.exceptions.ProfileNotFoundError" href="#connpy.services.exceptions.ProfileNotFoundError">ProfileNotFoundError</a></code></h4>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.services.exceptions.ReservedNameError" href="#connpy.services.exceptions.ReservedNameError">ReservedNameError</a></code></h4>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,401 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.services.execution_service 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.services.execution_service</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
||||
<dl>
|
||||
<dt id="connpy.services.execution_service.ExecutionService"><code class="flex name class">
|
||||
<span>class <span class="ident">ExecutionService</span></span>
|
||||
<span>(</span><span>config=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class ExecutionService(BaseService):
|
||||
"""Business logic for executing commands on nodes and running automation scripts."""
|
||||
|
||||
def run_commands(
|
||||
self,
|
||||
nodes_filter: str,
|
||||
commands: List[str],
|
||||
variables: Optional[Dict[str, Any]] = None,
|
||||
parallel: int = 10,
|
||||
timeout: int = 10,
|
||||
folder: Optional[str] = None,
|
||||
prompt: Optional[str] = None,
|
||||
on_node_complete: Optional[Callable] = None,
|
||||
logger: Optional[Callable] = None
|
||||
) -> Dict[str, str]:
|
||||
|
||||
"""Execute commands on a set of nodes."""
|
||||
try:
|
||||
matched_names = self.config._getallnodes(nodes_filter)
|
||||
if not matched_names:
|
||||
raise ConnpyError(f"No nodes found matching filter: {nodes_filter}")
|
||||
|
||||
node_data = self.config.getitems(matched_names, extract=True)
|
||||
executor = Nodes(node_data, config=self.config)
|
||||
self.last_executor = executor
|
||||
|
||||
results = executor.run(
|
||||
commands=commands,
|
||||
vars=variables,
|
||||
parallel=parallel,
|
||||
timeout=timeout,
|
||||
folder=folder,
|
||||
prompt=prompt,
|
||||
on_complete=on_node_complete,
|
||||
logger=logger
|
||||
)
|
||||
|
||||
return results
|
||||
except Exception as e:
|
||||
raise ConnpyError(f"Execution failed: {e}")
|
||||
|
||||
def test_commands(
|
||||
self,
|
||||
nodes_filter: str,
|
||||
commands: List[str],
|
||||
expected: List[str],
|
||||
variables: Optional[Dict[str, Any]] = None,
|
||||
parallel: int = 10,
|
||||
timeout: int = 10,
|
||||
prompt: Optional[str] = None,
|
||||
on_node_complete: Optional[Callable] = None,
|
||||
logger: Optional[Callable] = None
|
||||
) -> Dict[str, Dict[str, bool]]:
|
||||
|
||||
"""Run commands and verify expected output on a set of nodes."""
|
||||
try:
|
||||
matched_names = self.config._getallnodes(nodes_filter)
|
||||
if not matched_names:
|
||||
raise ConnpyError(f"No nodes found matching filter: {nodes_filter}")
|
||||
|
||||
node_data = self.config.getitems(matched_names, extract=True)
|
||||
executor = Nodes(node_data, config=self.config)
|
||||
self.last_executor = executor
|
||||
|
||||
results = executor.test(
|
||||
commands=commands,
|
||||
expected=expected,
|
||||
vars=variables,
|
||||
parallel=parallel,
|
||||
timeout=timeout,
|
||||
prompt=prompt,
|
||||
on_complete=on_node_complete,
|
||||
logger=logger
|
||||
)
|
||||
return results
|
||||
except Exception as e:
|
||||
raise ConnpyError(f"Testing failed: {e}")
|
||||
|
||||
def run_cli_script(self, nodes_filter: str, script_path: str, parallel: int = 10) -> Dict[str, str]:
|
||||
"""Run a plain-text script containing one command per line."""
|
||||
if not os.path.exists(script_path):
|
||||
raise ConnpyError(f"Script file not found: {script_path}")
|
||||
|
||||
try:
|
||||
with open(script_path, "r") as f:
|
||||
commands = [line.strip() for line in f if line.strip()]
|
||||
except Exception as e:
|
||||
raise ConnpyError(f"Failed to read script {script_path}: {e}")
|
||||
|
||||
return self.run_commands(nodes_filter, commands, parallel=parallel)
|
||||
|
||||
def run_yaml_playbook(self, playbook_path: str, parallel: int = 10) -> Dict[str, Any]:
|
||||
"""Run a structured Connpy YAML automation playbook."""
|
||||
if not os.path.exists(playbook_path):
|
||||
raise ConnpyError(f"Playbook file not found: {playbook_path}")
|
||||
|
||||
try:
|
||||
with open(playbook_path, "r") as f:
|
||||
playbook = yaml.load(f, Loader=yaml.FullLoader)
|
||||
except Exception as e:
|
||||
raise ConnpyError(f"Failed to load playbook {playbook_path}: {e}")
|
||||
|
||||
# Basic validation
|
||||
if not isinstance(playbook, dict) or "nodes" not in playbook or "commands" not in playbook:
|
||||
raise ConnpyError("Invalid playbook format: missing 'nodes' or 'commands' keys.")
|
||||
|
||||
action = playbook.get("action", "run")
|
||||
if action == "run":
|
||||
return self.run_commands(
|
||||
nodes_filter=playbook["nodes"],
|
||||
commands=playbook["commands"],
|
||||
parallel=parallel,
|
||||
timeout=playbook.get("timeout", 10)
|
||||
)
|
||||
elif action == "test":
|
||||
return self.test_commands(
|
||||
nodes_filter=playbook["nodes"],
|
||||
commands=playbook["commands"],
|
||||
expected=playbook.get("expected", []),
|
||||
parallel=parallel,
|
||||
timeout=playbook.get("timeout", 10)
|
||||
)
|
||||
else:
|
||||
raise ConnpyError(f"Unsupported playbook action: {action}")</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Business logic for executing commands on nodes and running automation scripts.</p>
|
||||
<p>Initialize the service.</p>
|
||||
<h2 id="args">Args</h2>
|
||||
<dl>
|
||||
<dt><strong><code>config</code></strong></dt>
|
||||
<dd>An instance of configfile (or None to instantiate a new one/use global context).</dd>
|
||||
</dl></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li><a title="connpy.services.base.BaseService" href="base.html#connpy.services.base.BaseService">BaseService</a></li>
|
||||
</ul>
|
||||
<h3>Methods</h3>
|
||||
<dl>
|
||||
<dt id="connpy.services.execution_service.ExecutionService.run_cli_script"><code class="name flex">
|
||||
<span>def <span class="ident">run_cli_script</span></span>(<span>self, nodes_filter: str, script_path: str, parallel: int = 10) ‑> Dict[str, str]</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def run_cli_script(self, nodes_filter: str, script_path: str, parallel: int = 10) -> Dict[str, str]:
|
||||
"""Run a plain-text script containing one command per line."""
|
||||
if not os.path.exists(script_path):
|
||||
raise ConnpyError(f"Script file not found: {script_path}")
|
||||
|
||||
try:
|
||||
with open(script_path, "r") as f:
|
||||
commands = [line.strip() for line in f if line.strip()]
|
||||
except Exception as e:
|
||||
raise ConnpyError(f"Failed to read script {script_path}: {e}")
|
||||
|
||||
return self.run_commands(nodes_filter, commands, parallel=parallel)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Run a plain-text script containing one command per line.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.execution_service.ExecutionService.run_commands"><code class="name flex">
|
||||
<span>def <span class="ident">run_commands</span></span>(<span>self,<br>nodes_filter: str,<br>commands: List[str],<br>variables: Dict[str, Any] | None = None,<br>parallel: int = 10,<br>timeout: int = 10,<br>folder: str | None = None,<br>prompt: str | None = None,<br>on_node_complete: Callable | None = None,<br>logger: Callable | None = None) ‑> Dict[str, str]</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def run_commands(
|
||||
self,
|
||||
nodes_filter: str,
|
||||
commands: List[str],
|
||||
variables: Optional[Dict[str, Any]] = None,
|
||||
parallel: int = 10,
|
||||
timeout: int = 10,
|
||||
folder: Optional[str] = None,
|
||||
prompt: Optional[str] = None,
|
||||
on_node_complete: Optional[Callable] = None,
|
||||
logger: Optional[Callable] = None
|
||||
) -> Dict[str, str]:
|
||||
|
||||
"""Execute commands on a set of nodes."""
|
||||
try:
|
||||
matched_names = self.config._getallnodes(nodes_filter)
|
||||
if not matched_names:
|
||||
raise ConnpyError(f"No nodes found matching filter: {nodes_filter}")
|
||||
|
||||
node_data = self.config.getitems(matched_names, extract=True)
|
||||
executor = Nodes(node_data, config=self.config)
|
||||
self.last_executor = executor
|
||||
|
||||
results = executor.run(
|
||||
commands=commands,
|
||||
vars=variables,
|
||||
parallel=parallel,
|
||||
timeout=timeout,
|
||||
folder=folder,
|
||||
prompt=prompt,
|
||||
on_complete=on_node_complete,
|
||||
logger=logger
|
||||
)
|
||||
|
||||
return results
|
||||
except Exception as e:
|
||||
raise ConnpyError(f"Execution failed: {e}")</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Execute commands on a set of nodes.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.execution_service.ExecutionService.run_yaml_playbook"><code class="name flex">
|
||||
<span>def <span class="ident">run_yaml_playbook</span></span>(<span>self, playbook_path: str, parallel: int = 10) ‑> Dict[str, Any]</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def run_yaml_playbook(self, playbook_path: str, parallel: int = 10) -> Dict[str, Any]:
|
||||
"""Run a structured Connpy YAML automation playbook."""
|
||||
if not os.path.exists(playbook_path):
|
||||
raise ConnpyError(f"Playbook file not found: {playbook_path}")
|
||||
|
||||
try:
|
||||
with open(playbook_path, "r") as f:
|
||||
playbook = yaml.load(f, Loader=yaml.FullLoader)
|
||||
except Exception as e:
|
||||
raise ConnpyError(f"Failed to load playbook {playbook_path}: {e}")
|
||||
|
||||
# Basic validation
|
||||
if not isinstance(playbook, dict) or "nodes" not in playbook or "commands" not in playbook:
|
||||
raise ConnpyError("Invalid playbook format: missing 'nodes' or 'commands' keys.")
|
||||
|
||||
action = playbook.get("action", "run")
|
||||
if action == "run":
|
||||
return self.run_commands(
|
||||
nodes_filter=playbook["nodes"],
|
||||
commands=playbook["commands"],
|
||||
parallel=parallel,
|
||||
timeout=playbook.get("timeout", 10)
|
||||
)
|
||||
elif action == "test":
|
||||
return self.test_commands(
|
||||
nodes_filter=playbook["nodes"],
|
||||
commands=playbook["commands"],
|
||||
expected=playbook.get("expected", []),
|
||||
parallel=parallel,
|
||||
timeout=playbook.get("timeout", 10)
|
||||
)
|
||||
else:
|
||||
raise ConnpyError(f"Unsupported playbook action: {action}")</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Run a structured Connpy YAML automation playbook.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.execution_service.ExecutionService.test_commands"><code class="name flex">
|
||||
<span>def <span class="ident">test_commands</span></span>(<span>self,<br>nodes_filter: str,<br>commands: List[str],<br>expected: List[str],<br>variables: Dict[str, Any] | None = None,<br>parallel: int = 10,<br>timeout: int = 10,<br>prompt: str | None = None,<br>on_node_complete: Callable | None = None,<br>logger: Callable | None = None) ‑> Dict[str, Dict[str, bool]]</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def test_commands(
|
||||
self,
|
||||
nodes_filter: str,
|
||||
commands: List[str],
|
||||
expected: List[str],
|
||||
variables: Optional[Dict[str, Any]] = None,
|
||||
parallel: int = 10,
|
||||
timeout: int = 10,
|
||||
prompt: Optional[str] = None,
|
||||
on_node_complete: Optional[Callable] = None,
|
||||
logger: Optional[Callable] = None
|
||||
) -> Dict[str, Dict[str, bool]]:
|
||||
|
||||
"""Run commands and verify expected output on a set of nodes."""
|
||||
try:
|
||||
matched_names = self.config._getallnodes(nodes_filter)
|
||||
if not matched_names:
|
||||
raise ConnpyError(f"No nodes found matching filter: {nodes_filter}")
|
||||
|
||||
node_data = self.config.getitems(matched_names, extract=True)
|
||||
executor = Nodes(node_data, config=self.config)
|
||||
self.last_executor = executor
|
||||
|
||||
results = executor.test(
|
||||
commands=commands,
|
||||
expected=expected,
|
||||
vars=variables,
|
||||
parallel=parallel,
|
||||
timeout=timeout,
|
||||
prompt=prompt,
|
||||
on_complete=on_node_complete,
|
||||
logger=logger
|
||||
)
|
||||
return results
|
||||
except Exception as e:
|
||||
raise ConnpyError(f"Testing failed: {e}")</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Run commands and verify expected output on a set of nodes.</p></div>
|
||||
</dd>
|
||||
</dl>
|
||||
<h3>Inherited members</h3>
|
||||
<ul class="hlist">
|
||||
<li><code><b><a title="connpy.services.base.BaseService" href="base.html#connpy.services.base.BaseService">BaseService</a></b></code>:
|
||||
<ul class="hlist">
|
||||
<li><code><a title="connpy.services.base.BaseService.set_reserved_names" href="base.html#connpy.services.base.BaseService.set_reserved_names">set_reserved_names</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</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.services" href="index.html">connpy.services</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<h4><code><a title="connpy.services.execution_service.ExecutionService" href="#connpy.services.execution_service.ExecutionService">ExecutionService</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.services.execution_service.ExecutionService.run_cli_script" href="#connpy.services.execution_service.ExecutionService.run_cli_script">run_cli_script</a></code></li>
|
||||
<li><code><a title="connpy.services.execution_service.ExecutionService.run_commands" href="#connpy.services.execution_service.ExecutionService.run_commands">run_commands</a></code></li>
|
||||
<li><code><a title="connpy.services.execution_service.ExecutionService.run_yaml_playbook" href="#connpy.services.execution_service.ExecutionService.run_yaml_playbook">run_yaml_playbook</a></code></li>
|
||||
<li><code><a title="connpy.services.execution_service.ExecutionService.test_commands" href="#connpy.services.execution_service.ExecutionService.test_commands">test_commands</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,285 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.services.import_export_service 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.services.import_export_service</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
||||
<dl>
|
||||
<dt id="connpy.services.import_export_service.ImportExportService"><code class="flex name class">
|
||||
<span>class <span class="ident">ImportExportService</span></span>
|
||||
<span>(</span><span>config=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class ImportExportService(BaseService):
|
||||
"""Business logic for YAML/JSON inventory import and export."""
|
||||
|
||||
def export_to_file(self, file_path, folders=None):
|
||||
"""Export nodes/folders to a YAML file."""
|
||||
if os.path.exists(file_path):
|
||||
raise InvalidConfigurationError(f"File '{file_path}' already exists.")
|
||||
|
||||
data = self.export_to_dict(folders)
|
||||
try:
|
||||
with open(file_path, "w") as f:
|
||||
yaml.dump(data, f, Dumper=NoAliasDumper, default_flow_style=False)
|
||||
except OSError as e:
|
||||
raise InvalidConfigurationError(f"Failed to export to '{file_path}': {e}")
|
||||
|
||||
def export_to_dict(self, folders=None):
|
||||
"""Export nodes/folders to a dictionary."""
|
||||
if not folders:
|
||||
return self.config._getallnodesfull(extract=False)
|
||||
else:
|
||||
# Validate folders exist
|
||||
for f in folders:
|
||||
if f != "@" and f not in self.config._getallfolders():
|
||||
raise NodeNotFoundError(f"Folder '{f}' not found.")
|
||||
return self.config._getallnodesfull(folders, extract=False)
|
||||
|
||||
def import_from_file(self, file_path):
|
||||
"""Import nodes/folders from a YAML file."""
|
||||
if not os.path.exists(file_path):
|
||||
raise InvalidConfigurationError(f"File '{file_path}' does not exist.")
|
||||
|
||||
try:
|
||||
with open(file_path, "r") as f:
|
||||
data = yaml.load(f, Loader=yaml.FullLoader)
|
||||
self.import_from_dict(data)
|
||||
except Exception as e:
|
||||
raise InvalidConfigurationError(f"Failed to read/parse import file: {e}")
|
||||
|
||||
def import_from_dict(self, data):
|
||||
"""Import nodes/folders from a dictionary."""
|
||||
if not isinstance(data, dict):
|
||||
raise InvalidConfigurationError("Invalid import data format: expected a dictionary of nodes.")
|
||||
|
||||
# Process imports
|
||||
for k, v in data.items():
|
||||
uniques = self.config._explode_unique(k)
|
||||
|
||||
# Ensure folders exist
|
||||
if "folder" in uniques:
|
||||
folder_name = f"@{uniques['folder']}"
|
||||
if folder_name not in self.config._getallfolders():
|
||||
folder_uniques = self.config._explode_unique(folder_name)
|
||||
self.config._folder_add(**folder_uniques)
|
||||
|
||||
if "subfolder" in uniques:
|
||||
sub_name = f"@{uniques['subfolder']}@{uniques['folder']}"
|
||||
if sub_name not in self.config._getallfolders():
|
||||
sub_uniques = self.config._explode_unique(sub_name)
|
||||
self.config._folder_add(**sub_uniques)
|
||||
|
||||
# Add node/connection
|
||||
v.update(uniques)
|
||||
self._validate_node_name(k)
|
||||
self.config._connections_add(**v)
|
||||
|
||||
self.config._saveconfig(self.config.file)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Business logic for YAML/JSON inventory import and export.</p>
|
||||
<p>Initialize the service.</p>
|
||||
<h2 id="args">Args</h2>
|
||||
<dl>
|
||||
<dt><strong><code>config</code></strong></dt>
|
||||
<dd>An instance of configfile (or None to instantiate a new one/use global context).</dd>
|
||||
</dl></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li><a title="connpy.services.base.BaseService" href="base.html#connpy.services.base.BaseService">BaseService</a></li>
|
||||
</ul>
|
||||
<h3>Methods</h3>
|
||||
<dl>
|
||||
<dt id="connpy.services.import_export_service.ImportExportService.export_to_dict"><code class="name flex">
|
||||
<span>def <span class="ident">export_to_dict</span></span>(<span>self, folders=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def export_to_dict(self, folders=None):
|
||||
"""Export nodes/folders to a dictionary."""
|
||||
if not folders:
|
||||
return self.config._getallnodesfull(extract=False)
|
||||
else:
|
||||
# Validate folders exist
|
||||
for f in folders:
|
||||
if f != "@" and f not in self.config._getallfolders():
|
||||
raise NodeNotFoundError(f"Folder '{f}' not found.")
|
||||
return self.config._getallnodesfull(folders, extract=False)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Export nodes/folders to a dictionary.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.import_export_service.ImportExportService.export_to_file"><code class="name flex">
|
||||
<span>def <span class="ident">export_to_file</span></span>(<span>self, file_path, folders=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def export_to_file(self, file_path, folders=None):
|
||||
"""Export nodes/folders to a YAML file."""
|
||||
if os.path.exists(file_path):
|
||||
raise InvalidConfigurationError(f"File '{file_path}' already exists.")
|
||||
|
||||
data = self.export_to_dict(folders)
|
||||
try:
|
||||
with open(file_path, "w") as f:
|
||||
yaml.dump(data, f, Dumper=NoAliasDumper, default_flow_style=False)
|
||||
except OSError as e:
|
||||
raise InvalidConfigurationError(f"Failed to export to '{file_path}': {e}")</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Export nodes/folders to a YAML file.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.import_export_service.ImportExportService.import_from_dict"><code class="name flex">
|
||||
<span>def <span class="ident">import_from_dict</span></span>(<span>self, data)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def import_from_dict(self, data):
|
||||
"""Import nodes/folders from a dictionary."""
|
||||
if not isinstance(data, dict):
|
||||
raise InvalidConfigurationError("Invalid import data format: expected a dictionary of nodes.")
|
||||
|
||||
# Process imports
|
||||
for k, v in data.items():
|
||||
uniques = self.config._explode_unique(k)
|
||||
|
||||
# Ensure folders exist
|
||||
if "folder" in uniques:
|
||||
folder_name = f"@{uniques['folder']}"
|
||||
if folder_name not in self.config._getallfolders():
|
||||
folder_uniques = self.config._explode_unique(folder_name)
|
||||
self.config._folder_add(**folder_uniques)
|
||||
|
||||
if "subfolder" in uniques:
|
||||
sub_name = f"@{uniques['subfolder']}@{uniques['folder']}"
|
||||
if sub_name not in self.config._getallfolders():
|
||||
sub_uniques = self.config._explode_unique(sub_name)
|
||||
self.config._folder_add(**sub_uniques)
|
||||
|
||||
# Add node/connection
|
||||
v.update(uniques)
|
||||
self._validate_node_name(k)
|
||||
self.config._connections_add(**v)
|
||||
|
||||
self.config._saveconfig(self.config.file)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Import nodes/folders from a dictionary.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.import_export_service.ImportExportService.import_from_file"><code class="name flex">
|
||||
<span>def <span class="ident">import_from_file</span></span>(<span>self, file_path)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def import_from_file(self, file_path):
|
||||
"""Import nodes/folders from a YAML file."""
|
||||
if not os.path.exists(file_path):
|
||||
raise InvalidConfigurationError(f"File '{file_path}' does not exist.")
|
||||
|
||||
try:
|
||||
with open(file_path, "r") as f:
|
||||
data = yaml.load(f, Loader=yaml.FullLoader)
|
||||
self.import_from_dict(data)
|
||||
except Exception as e:
|
||||
raise InvalidConfigurationError(f"Failed to read/parse import file: {e}")</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Import nodes/folders from a YAML file.</p></div>
|
||||
</dd>
|
||||
</dl>
|
||||
<h3>Inherited members</h3>
|
||||
<ul class="hlist">
|
||||
<li><code><b><a title="connpy.services.base.BaseService" href="base.html#connpy.services.base.BaseService">BaseService</a></b></code>:
|
||||
<ul class="hlist">
|
||||
<li><code><a title="connpy.services.base.BaseService.set_reserved_names" href="base.html#connpy.services.base.BaseService.set_reserved_names">set_reserved_names</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</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.services" href="index.html">connpy.services</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<h4><code><a title="connpy.services.import_export_service.ImportExportService" href="#connpy.services.import_export_service.ImportExportService">ImportExportService</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.services.import_export_service.ImportExportService.export_to_dict" href="#connpy.services.import_export_service.ImportExportService.export_to_dict">export_to_dict</a></code></li>
|
||||
<li><code><a title="connpy.services.import_export_service.ImportExportService.export_to_file" href="#connpy.services.import_export_service.ImportExportService.export_to_file">export_to_file</a></code></li>
|
||||
<li><code><a title="connpy.services.import_export_service.ImportExportService.import_from_dict" href="#connpy.services.import_export_service.ImportExportService.import_from_dict">import_from_dict</a></code></li>
|
||||
<li><code><a title="connpy.services.import_export_service.ImportExportService.import_from_file" href="#connpy.services.import_export_service.ImportExportService.import_from_file">import_from_file</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,745 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.services.node_service 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.services.node_service</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
||||
<dl>
|
||||
<dt id="connpy.services.node_service.NodeService"><code class="flex name class">
|
||||
<span>class <span class="ident">NodeService</span></span>
|
||||
<span>(</span><span>config=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class NodeService(BaseService):
|
||||
def __init__(self, config=None):
|
||||
super().__init__(config)
|
||||
|
||||
|
||||
def list_nodes(self, filter_str=None, format_str=None):
|
||||
"""Return a listed filtered by regex match and formatted if needed."""
|
||||
nodes = self.config._getallnodes()
|
||||
case_sensitive = self.config.config.get("case", False)
|
||||
|
||||
if filter_str:
|
||||
flags = re.IGNORECASE if not case_sensitive else 0
|
||||
nodes = [n for n in nodes if re.search(filter_str, n, flags)]
|
||||
|
||||
if not format_str:
|
||||
return nodes
|
||||
|
||||
from .profile_service import ProfileService
|
||||
profile_service = ProfileService(self.config)
|
||||
|
||||
formatted_nodes = []
|
||||
for n_id in nodes:
|
||||
# Use ProfileService to resolve profiles for dynamic formatting
|
||||
details = self.config.getitem(n_id, extract=False)
|
||||
if details:
|
||||
details = profile_service.resolve_node_data(details)
|
||||
|
||||
name = n_id.split("@")[0]
|
||||
location = n_id.partition("@")[2] or "root"
|
||||
|
||||
# Prepare context for .format() with all details
|
||||
context = details.copy()
|
||||
context.update({
|
||||
"name": name,
|
||||
"NAME": name.upper(),
|
||||
"location": location,
|
||||
"LOCATION": location.upper(),
|
||||
})
|
||||
|
||||
# Add exploded uniques (id, folder, subfolder)
|
||||
uniques = self.config._explode_unique(n_id)
|
||||
if uniques:
|
||||
context.update(uniques)
|
||||
|
||||
# Add uppercase versions of all keys for convenience
|
||||
for k, v in list(context.items()):
|
||||
if isinstance(v, str):
|
||||
context[k.upper()] = v.upper()
|
||||
|
||||
try:
|
||||
formatted_nodes.append(format_str.format(**context))
|
||||
except (KeyError, IndexError, ValueError):
|
||||
# Fallback to original string if format fails
|
||||
formatted_nodes.append(n_id)
|
||||
return formatted_nodes
|
||||
|
||||
def list_folders(self, filter_str=None):
|
||||
"""Return all unique folders, optionally filtered by regex."""
|
||||
folders = self.config._getallfolders()
|
||||
case_sensitive = self.config.config.get("case", False)
|
||||
|
||||
if filter_str:
|
||||
flags = re.IGNORECASE if not case_sensitive else 0
|
||||
folders = [f for f in folders if re.search(filter_str, f, flags)]
|
||||
return folders
|
||||
|
||||
def get_node_details(self, unique_id):
|
||||
"""Return full configuration dictionary for a specific node."""
|
||||
details = self.config.getitem(unique_id)
|
||||
if not details:
|
||||
raise NodeNotFoundError(f"Node '{unique_id}' not found.")
|
||||
return details
|
||||
|
||||
def explode_unique(self, unique_id):
|
||||
"""Explode a unique ID into a dictionary of its parts."""
|
||||
return self.config._explode_unique(unique_id)
|
||||
|
||||
def generate_cache(self, nodes=None, folders=None, profiles=None):
|
||||
"""Generate and update the internal nodes cache."""
|
||||
self.config._generate_nodes_cache(nodes=nodes, folders=folders, profiles=profiles)
|
||||
|
||||
|
||||
def add_node(self, unique_id, data, is_folder=False):
|
||||
"""Logic for adding a new node or folder to configuration."""
|
||||
if not is_folder:
|
||||
self._validate_node_name(unique_id)
|
||||
|
||||
all_nodes = self.config._getallnodes()
|
||||
all_folders = self.config._getallfolders()
|
||||
|
||||
if is_folder:
|
||||
if unique_id in all_folders:
|
||||
raise NodeAlreadyExistsError(f"Folder '{unique_id}' already exists.")
|
||||
uniques = self.config._explode_unique(unique_id)
|
||||
if not uniques:
|
||||
raise InvalidConfigurationError(f"Invalid folder name '{unique_id}'.")
|
||||
|
||||
# Check if parent folder exists when creating a subfolder
|
||||
if "subfolder" in uniques:
|
||||
parent_folder = f"@{uniques['folder']}"
|
||||
if parent_folder not in all_folders:
|
||||
raise NodeNotFoundError(f"Folder '{parent_folder}' not found.")
|
||||
|
||||
self.config._folder_add(**uniques)
|
||||
self.config._saveconfig(self.config.file)
|
||||
else:
|
||||
if unique_id in all_nodes:
|
||||
raise NodeAlreadyExistsError(f"Node '{unique_id}' already exists.")
|
||||
|
||||
# Check if parent folder exists when creating a node in a folder
|
||||
node_folder = unique_id.partition("@")[2]
|
||||
if node_folder:
|
||||
parent_folder = f"@{node_folder}"
|
||||
if parent_folder not in all_folders:
|
||||
raise NodeNotFoundError(f"Folder '{parent_folder}' not found.")
|
||||
|
||||
# Ensure 'id' is in data for config._connections_add
|
||||
if "id" not in data:
|
||||
uniques = self.config._explode_unique(unique_id)
|
||||
if uniques and "id" in uniques:
|
||||
data["id"] = uniques["id"]
|
||||
|
||||
self.config._connections_add(**data)
|
||||
self.config._saveconfig(self.config.file)
|
||||
|
||||
def update_node(self, unique_id, data):
|
||||
"""Explicitly update an existing node."""
|
||||
all_nodes = self.config._getallnodes()
|
||||
if unique_id not in all_nodes:
|
||||
raise NodeNotFoundError(f"Node '{unique_id}' not found.")
|
||||
|
||||
# Ensure 'id' is in data for config._connections_add
|
||||
if "id" not in data:
|
||||
uniques = self.config._explode_unique(unique_id)
|
||||
if uniques:
|
||||
data["id"] = uniques["id"]
|
||||
|
||||
# config._connections_add actually handles updates if ID exists correctly
|
||||
self.config._connections_add(**data)
|
||||
self.config._saveconfig(self.config.file)
|
||||
|
||||
def delete_node(self, unique_id, is_folder=False):
|
||||
"""Logic for deleting a node or folder."""
|
||||
if is_folder:
|
||||
uniques = self.config._explode_unique(unique_id)
|
||||
if not uniques:
|
||||
raise NodeNotFoundError(f"Folder '{unique_id}' not found or invalid.")
|
||||
self.config._folder_del(**uniques)
|
||||
else:
|
||||
uniques = self.config._explode_unique(unique_id)
|
||||
if not uniques:
|
||||
raise NodeNotFoundError(f"Node '{unique_id}' not found or invalid.")
|
||||
self.config._connections_del(**uniques)
|
||||
|
||||
self.config._saveconfig(self.config.file)
|
||||
|
||||
def connect_node(self, unique_id, sftp=False, debug=False, logger=None):
|
||||
"""Interact with a node directly."""
|
||||
from connpy.core import node
|
||||
from .profile_service import ProfileService
|
||||
|
||||
node_data = self.config.getitem(unique_id, extract=False)
|
||||
if not node_data:
|
||||
raise NodeNotFoundError(f"Node '{unique_id}' not found.")
|
||||
|
||||
# Resolve profiles
|
||||
profile_service = ProfileService(self.config)
|
||||
resolved_data = profile_service.resolve_node_data(node_data)
|
||||
|
||||
n = node(unique_id, **resolved_data, config=self.config)
|
||||
if sftp:
|
||||
n.protocol = "sftp"
|
||||
|
||||
n.interact(debug=debug, logger=logger)
|
||||
|
||||
def move_node(self, src_id, dst_id, copy=False):
|
||||
"""Move or copy a node."""
|
||||
self._validate_node_name(dst_id)
|
||||
|
||||
node_data = self.config.getitem(src_id)
|
||||
if not node_data:
|
||||
raise NodeNotFoundError(f"Source node '{src_id}' not found.")
|
||||
|
||||
if dst_id in self.config._getallnodes():
|
||||
raise NodeAlreadyExistsError(f"Destination node '{dst_id}' already exists.")
|
||||
|
||||
new_uniques = self.config._explode_unique(dst_id)
|
||||
if not new_uniques:
|
||||
raise InvalidConfigurationError(f"Invalid destination format '{dst_id}'.")
|
||||
|
||||
new_node_data = node_data.copy()
|
||||
new_node_data.update(new_uniques)
|
||||
|
||||
self.config._connections_add(**new_node_data)
|
||||
|
||||
if not copy:
|
||||
src_uniques = self.config._explode_unique(src_id)
|
||||
self.config._connections_del(**src_uniques)
|
||||
|
||||
self.config._saveconfig(self.config.file)
|
||||
|
||||
def bulk_add(self, ids, hosts, common_data):
|
||||
"""Add multiple nodes with shared common configuration."""
|
||||
count = 0
|
||||
all_nodes = self.config._getallnodes()
|
||||
|
||||
for i, uid in enumerate(ids):
|
||||
if uid in all_nodes:
|
||||
continue
|
||||
|
||||
try:
|
||||
self._validate_node_name(uid)
|
||||
except ReservedNameError:
|
||||
# For bulk, we might want to just skip or log.
|
||||
# CLI caller will handle if it wants to be strict.
|
||||
continue
|
||||
|
||||
host = hosts[i] if i < len(hosts) else hosts[0]
|
||||
uniques = self.config._explode_unique(uid)
|
||||
if not uniques:
|
||||
continue
|
||||
|
||||
node_data = common_data.copy()
|
||||
node_data.pop("ids", None)
|
||||
node_data.pop("location", None)
|
||||
node_data.update(uniques)
|
||||
node_data["host"] = host
|
||||
node_data["type"] = "connection"
|
||||
|
||||
self.config._connections_add(**node_data)
|
||||
count += 1
|
||||
|
||||
if count > 0:
|
||||
self.config._saveconfig(self.config.file)
|
||||
return count
|
||||
|
||||
def full_replace(self, connections, profiles):
|
||||
"""Replace all connections and profiles with new data."""
|
||||
self.config.connections = connections
|
||||
self.config.profiles = profiles
|
||||
self.config._saveconfig(self.config.file)
|
||||
|
||||
def get_inventory(self):
|
||||
"""Return a full snapshot of connections and profiles."""
|
||||
return {
|
||||
"connections": self.config.connections,
|
||||
"profiles": self.config.profiles
|
||||
}</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Base class for all connpy services, providing common configuration access.</p>
|
||||
<p>Initialize the service.</p>
|
||||
<h2 id="args">Args</h2>
|
||||
<dl>
|
||||
<dt><strong><code>config</code></strong></dt>
|
||||
<dd>An instance of configfile (or None to instantiate a new one/use global context).</dd>
|
||||
</dl></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li><a title="connpy.services.base.BaseService" href="base.html#connpy.services.base.BaseService">BaseService</a></li>
|
||||
</ul>
|
||||
<h3>Methods</h3>
|
||||
<dl>
|
||||
<dt id="connpy.services.node_service.NodeService.add_node"><code class="name flex">
|
||||
<span>def <span class="ident">add_node</span></span>(<span>self, unique_id, data, is_folder=False)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def add_node(self, unique_id, data, is_folder=False):
|
||||
"""Logic for adding a new node or folder to configuration."""
|
||||
if not is_folder:
|
||||
self._validate_node_name(unique_id)
|
||||
|
||||
all_nodes = self.config._getallnodes()
|
||||
all_folders = self.config._getallfolders()
|
||||
|
||||
if is_folder:
|
||||
if unique_id in all_folders:
|
||||
raise NodeAlreadyExistsError(f"Folder '{unique_id}' already exists.")
|
||||
uniques = self.config._explode_unique(unique_id)
|
||||
if not uniques:
|
||||
raise InvalidConfigurationError(f"Invalid folder name '{unique_id}'.")
|
||||
|
||||
# Check if parent folder exists when creating a subfolder
|
||||
if "subfolder" in uniques:
|
||||
parent_folder = f"@{uniques['folder']}"
|
||||
if parent_folder not in all_folders:
|
||||
raise NodeNotFoundError(f"Folder '{parent_folder}' not found.")
|
||||
|
||||
self.config._folder_add(**uniques)
|
||||
self.config._saveconfig(self.config.file)
|
||||
else:
|
||||
if unique_id in all_nodes:
|
||||
raise NodeAlreadyExistsError(f"Node '{unique_id}' already exists.")
|
||||
|
||||
# Check if parent folder exists when creating a node in a folder
|
||||
node_folder = unique_id.partition("@")[2]
|
||||
if node_folder:
|
||||
parent_folder = f"@{node_folder}"
|
||||
if parent_folder not in all_folders:
|
||||
raise NodeNotFoundError(f"Folder '{parent_folder}' not found.")
|
||||
|
||||
# Ensure 'id' is in data for config._connections_add
|
||||
if "id" not in data:
|
||||
uniques = self.config._explode_unique(unique_id)
|
||||
if uniques and "id" in uniques:
|
||||
data["id"] = uniques["id"]
|
||||
|
||||
self.config._connections_add(**data)
|
||||
self.config._saveconfig(self.config.file)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Logic for adding a new node or folder to configuration.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.node_service.NodeService.bulk_add"><code class="name flex">
|
||||
<span>def <span class="ident">bulk_add</span></span>(<span>self, ids, hosts, common_data)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def bulk_add(self, ids, hosts, common_data):
|
||||
"""Add multiple nodes with shared common configuration."""
|
||||
count = 0
|
||||
all_nodes = self.config._getallnodes()
|
||||
|
||||
for i, uid in enumerate(ids):
|
||||
if uid in all_nodes:
|
||||
continue
|
||||
|
||||
try:
|
||||
self._validate_node_name(uid)
|
||||
except ReservedNameError:
|
||||
# For bulk, we might want to just skip or log.
|
||||
# CLI caller will handle if it wants to be strict.
|
||||
continue
|
||||
|
||||
host = hosts[i] if i < len(hosts) else hosts[0]
|
||||
uniques = self.config._explode_unique(uid)
|
||||
if not uniques:
|
||||
continue
|
||||
|
||||
node_data = common_data.copy()
|
||||
node_data.pop("ids", None)
|
||||
node_data.pop("location", None)
|
||||
node_data.update(uniques)
|
||||
node_data["host"] = host
|
||||
node_data["type"] = "connection"
|
||||
|
||||
self.config._connections_add(**node_data)
|
||||
count += 1
|
||||
|
||||
if count > 0:
|
||||
self.config._saveconfig(self.config.file)
|
||||
return count</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Add multiple nodes with shared common configuration.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.node_service.NodeService.connect_node"><code class="name flex">
|
||||
<span>def <span class="ident">connect_node</span></span>(<span>self, unique_id, sftp=False, debug=False, logger=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def connect_node(self, unique_id, sftp=False, debug=False, logger=None):
|
||||
"""Interact with a node directly."""
|
||||
from connpy.core import node
|
||||
from .profile_service import ProfileService
|
||||
|
||||
node_data = self.config.getitem(unique_id, extract=False)
|
||||
if not node_data:
|
||||
raise NodeNotFoundError(f"Node '{unique_id}' not found.")
|
||||
|
||||
# Resolve profiles
|
||||
profile_service = ProfileService(self.config)
|
||||
resolved_data = profile_service.resolve_node_data(node_data)
|
||||
|
||||
n = node(unique_id, **resolved_data, config=self.config)
|
||||
if sftp:
|
||||
n.protocol = "sftp"
|
||||
|
||||
n.interact(debug=debug, logger=logger)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Interact with a node directly.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.node_service.NodeService.delete_node"><code class="name flex">
|
||||
<span>def <span class="ident">delete_node</span></span>(<span>self, unique_id, is_folder=False)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def delete_node(self, unique_id, is_folder=False):
|
||||
"""Logic for deleting a node or folder."""
|
||||
if is_folder:
|
||||
uniques = self.config._explode_unique(unique_id)
|
||||
if not uniques:
|
||||
raise NodeNotFoundError(f"Folder '{unique_id}' not found or invalid.")
|
||||
self.config._folder_del(**uniques)
|
||||
else:
|
||||
uniques = self.config._explode_unique(unique_id)
|
||||
if not uniques:
|
||||
raise NodeNotFoundError(f"Node '{unique_id}' not found or invalid.")
|
||||
self.config._connections_del(**uniques)
|
||||
|
||||
self.config._saveconfig(self.config.file)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Logic for deleting a node or folder.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.node_service.NodeService.explode_unique"><code class="name flex">
|
||||
<span>def <span class="ident">explode_unique</span></span>(<span>self, unique_id)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def explode_unique(self, unique_id):
|
||||
"""Explode a unique ID into a dictionary of its parts."""
|
||||
return self.config._explode_unique(unique_id)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Explode a unique ID into a dictionary of its parts.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.node_service.NodeService.full_replace"><code class="name flex">
|
||||
<span>def <span class="ident">full_replace</span></span>(<span>self, connections, profiles)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def full_replace(self, connections, profiles):
|
||||
"""Replace all connections and profiles with new data."""
|
||||
self.config.connections = connections
|
||||
self.config.profiles = profiles
|
||||
self.config._saveconfig(self.config.file)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Replace all connections and profiles with new data.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.node_service.NodeService.generate_cache"><code class="name flex">
|
||||
<span>def <span class="ident">generate_cache</span></span>(<span>self, nodes=None, folders=None, profiles=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def generate_cache(self, nodes=None, folders=None, profiles=None):
|
||||
"""Generate and update the internal nodes cache."""
|
||||
self.config._generate_nodes_cache(nodes=nodes, folders=folders, profiles=profiles)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Generate and update the internal nodes cache.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.node_service.NodeService.get_inventory"><code class="name flex">
|
||||
<span>def <span class="ident">get_inventory</span></span>(<span>self)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def get_inventory(self):
|
||||
"""Return a full snapshot of connections and profiles."""
|
||||
return {
|
||||
"connections": self.config.connections,
|
||||
"profiles": self.config.profiles
|
||||
}</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Return a full snapshot of connections and profiles.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.node_service.NodeService.get_node_details"><code class="name flex">
|
||||
<span>def <span class="ident">get_node_details</span></span>(<span>self, unique_id)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def get_node_details(self, unique_id):
|
||||
"""Return full configuration dictionary for a specific node."""
|
||||
details = self.config.getitem(unique_id)
|
||||
if not details:
|
||||
raise NodeNotFoundError(f"Node '{unique_id}' not found.")
|
||||
return details</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Return full configuration dictionary for a specific node.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.node_service.NodeService.list_folders"><code class="name flex">
|
||||
<span>def <span class="ident">list_folders</span></span>(<span>self, filter_str=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def list_folders(self, filter_str=None):
|
||||
"""Return all unique folders, optionally filtered by regex."""
|
||||
folders = self.config._getallfolders()
|
||||
case_sensitive = self.config.config.get("case", False)
|
||||
|
||||
if filter_str:
|
||||
flags = re.IGNORECASE if not case_sensitive else 0
|
||||
folders = [f for f in folders if re.search(filter_str, f, flags)]
|
||||
return folders</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Return all unique folders, optionally filtered by regex.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.node_service.NodeService.list_nodes"><code class="name flex">
|
||||
<span>def <span class="ident">list_nodes</span></span>(<span>self, filter_str=None, format_str=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def list_nodes(self, filter_str=None, format_str=None):
|
||||
"""Return a listed filtered by regex match and formatted if needed."""
|
||||
nodes = self.config._getallnodes()
|
||||
case_sensitive = self.config.config.get("case", False)
|
||||
|
||||
if filter_str:
|
||||
flags = re.IGNORECASE if not case_sensitive else 0
|
||||
nodes = [n for n in nodes if re.search(filter_str, n, flags)]
|
||||
|
||||
if not format_str:
|
||||
return nodes
|
||||
|
||||
from .profile_service import ProfileService
|
||||
profile_service = ProfileService(self.config)
|
||||
|
||||
formatted_nodes = []
|
||||
for n_id in nodes:
|
||||
# Use ProfileService to resolve profiles for dynamic formatting
|
||||
details = self.config.getitem(n_id, extract=False)
|
||||
if details:
|
||||
details = profile_service.resolve_node_data(details)
|
||||
|
||||
name = n_id.split("@")[0]
|
||||
location = n_id.partition("@")[2] or "root"
|
||||
|
||||
# Prepare context for .format() with all details
|
||||
context = details.copy()
|
||||
context.update({
|
||||
"name": name,
|
||||
"NAME": name.upper(),
|
||||
"location": location,
|
||||
"LOCATION": location.upper(),
|
||||
})
|
||||
|
||||
# Add exploded uniques (id, folder, subfolder)
|
||||
uniques = self.config._explode_unique(n_id)
|
||||
if uniques:
|
||||
context.update(uniques)
|
||||
|
||||
# Add uppercase versions of all keys for convenience
|
||||
for k, v in list(context.items()):
|
||||
if isinstance(v, str):
|
||||
context[k.upper()] = v.upper()
|
||||
|
||||
try:
|
||||
formatted_nodes.append(format_str.format(**context))
|
||||
except (KeyError, IndexError, ValueError):
|
||||
# Fallback to original string if format fails
|
||||
formatted_nodes.append(n_id)
|
||||
return formatted_nodes</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Return a listed filtered by regex match and formatted if needed.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.node_service.NodeService.move_node"><code class="name flex">
|
||||
<span>def <span class="ident">move_node</span></span>(<span>self, src_id, dst_id, copy=False)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def move_node(self, src_id, dst_id, copy=False):
|
||||
"""Move or copy a node."""
|
||||
self._validate_node_name(dst_id)
|
||||
|
||||
node_data = self.config.getitem(src_id)
|
||||
if not node_data:
|
||||
raise NodeNotFoundError(f"Source node '{src_id}' not found.")
|
||||
|
||||
if dst_id in self.config._getallnodes():
|
||||
raise NodeAlreadyExistsError(f"Destination node '{dst_id}' already exists.")
|
||||
|
||||
new_uniques = self.config._explode_unique(dst_id)
|
||||
if not new_uniques:
|
||||
raise InvalidConfigurationError(f"Invalid destination format '{dst_id}'.")
|
||||
|
||||
new_node_data = node_data.copy()
|
||||
new_node_data.update(new_uniques)
|
||||
|
||||
self.config._connections_add(**new_node_data)
|
||||
|
||||
if not copy:
|
||||
src_uniques = self.config._explode_unique(src_id)
|
||||
self.config._connections_del(**src_uniques)
|
||||
|
||||
self.config._saveconfig(self.config.file)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Move or copy a node.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.node_service.NodeService.update_node"><code class="name flex">
|
||||
<span>def <span class="ident">update_node</span></span>(<span>self, unique_id, data)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def update_node(self, unique_id, data):
|
||||
"""Explicitly update an existing node."""
|
||||
all_nodes = self.config._getallnodes()
|
||||
if unique_id not in all_nodes:
|
||||
raise NodeNotFoundError(f"Node '{unique_id}' not found.")
|
||||
|
||||
# Ensure 'id' is in data for config._connections_add
|
||||
if "id" not in data:
|
||||
uniques = self.config._explode_unique(unique_id)
|
||||
if uniques:
|
||||
data["id"] = uniques["id"]
|
||||
|
||||
# config._connections_add actually handles updates if ID exists correctly
|
||||
self.config._connections_add(**data)
|
||||
self.config._saveconfig(self.config.file)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Explicitly update an existing node.</p></div>
|
||||
</dd>
|
||||
</dl>
|
||||
<h3>Inherited members</h3>
|
||||
<ul class="hlist">
|
||||
<li><code><b><a title="connpy.services.base.BaseService" href="base.html#connpy.services.base.BaseService">BaseService</a></b></code>:
|
||||
<ul class="hlist">
|
||||
<li><code><a title="connpy.services.base.BaseService.set_reserved_names" href="base.html#connpy.services.base.BaseService.set_reserved_names">set_reserved_names</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</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.services" href="index.html">connpy.services</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<h4><code><a title="connpy.services.node_service.NodeService" href="#connpy.services.node_service.NodeService">NodeService</a></code></h4>
|
||||
<ul class="two-column">
|
||||
<li><code><a title="connpy.services.node_service.NodeService.add_node" href="#connpy.services.node_service.NodeService.add_node">add_node</a></code></li>
|
||||
<li><code><a title="connpy.services.node_service.NodeService.bulk_add" href="#connpy.services.node_service.NodeService.bulk_add">bulk_add</a></code></li>
|
||||
<li><code><a title="connpy.services.node_service.NodeService.connect_node" href="#connpy.services.node_service.NodeService.connect_node">connect_node</a></code></li>
|
||||
<li><code><a title="connpy.services.node_service.NodeService.delete_node" href="#connpy.services.node_service.NodeService.delete_node">delete_node</a></code></li>
|
||||
<li><code><a title="connpy.services.node_service.NodeService.explode_unique" href="#connpy.services.node_service.NodeService.explode_unique">explode_unique</a></code></li>
|
||||
<li><code><a title="connpy.services.node_service.NodeService.full_replace" href="#connpy.services.node_service.NodeService.full_replace">full_replace</a></code></li>
|
||||
<li><code><a title="connpy.services.node_service.NodeService.generate_cache" href="#connpy.services.node_service.NodeService.generate_cache">generate_cache</a></code></li>
|
||||
<li><code><a title="connpy.services.node_service.NodeService.get_inventory" href="#connpy.services.node_service.NodeService.get_inventory">get_inventory</a></code></li>
|
||||
<li><code><a title="connpy.services.node_service.NodeService.get_node_details" href="#connpy.services.node_service.NodeService.get_node_details">get_node_details</a></code></li>
|
||||
<li><code><a title="connpy.services.node_service.NodeService.list_folders" href="#connpy.services.node_service.NodeService.list_folders">list_folders</a></code></li>
|
||||
<li><code><a title="connpy.services.node_service.NodeService.list_nodes" href="#connpy.services.node_service.NodeService.list_nodes">list_nodes</a></code></li>
|
||||
<li><code><a title="connpy.services.node_service.NodeService.move_node" href="#connpy.services.node_service.NodeService.move_node">move_node</a></code></li>
|
||||
<li><code><a title="connpy.services.node_service.NodeService.update_node" href="#connpy.services.node_service.NodeService.update_node">update_node</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,663 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.services.plugin_service 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.services.plugin_service</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
||||
<dl>
|
||||
<dt id="connpy.services.plugin_service.PluginService"><code class="flex name class">
|
||||
<span>class <span class="ident">PluginService</span></span>
|
||||
<span>(</span><span>config=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class PluginService(BaseService):
|
||||
"""Business logic for enabling, disabling, and listing plugins."""
|
||||
|
||||
def list_plugins(self):
|
||||
"""List all core and user-defined plugins with their status and hash."""
|
||||
import os
|
||||
import hashlib
|
||||
|
||||
# Check for user plugins directory
|
||||
plugin_dir = os.path.join(self.config.defaultdir, "plugins")
|
||||
# Check for core plugins directory
|
||||
core_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "core_plugins")
|
||||
|
||||
all_plugin_info = {}
|
||||
|
||||
def get_hash(path):
|
||||
try:
|
||||
with open(path, "rb") as f:
|
||||
return hashlib.md5(f.read()).hexdigest()
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
# User plugins
|
||||
if os.path.exists(plugin_dir):
|
||||
for f in os.listdir(plugin_dir):
|
||||
if f.endswith(".py"):
|
||||
name = f[:-3]
|
||||
path = os.path.join(plugin_dir, f)
|
||||
all_plugin_info[name] = {"enabled": True, "hash": get_hash(path)}
|
||||
elif f.endswith(".py.bkp"):
|
||||
name = f[:-7]
|
||||
all_plugin_info[name] = {"enabled": False}
|
||||
|
||||
return all_plugin_info
|
||||
|
||||
def add_plugin(self, name, source_file, update=False):
|
||||
"""Add or update a plugin from a local file."""
|
||||
import os
|
||||
import shutil
|
||||
from connpy.plugins import Plugins
|
||||
|
||||
if not name.isalpha() or not name.islower() or len(name) > 15:
|
||||
raise InvalidConfigurationError("Plugin name should be lowercase letters up to 15 characters.")
|
||||
|
||||
p_manager = Plugins()
|
||||
# Check for bad script
|
||||
error = p_manager.verify_script(source_file)
|
||||
if error:
|
||||
raise InvalidConfigurationError(f"Invalid plugin script: {error}")
|
||||
|
||||
self._save_plugin_file(name, source_file, update, is_path=True)
|
||||
|
||||
def add_plugin_from_bytes(self, name, content, update=False):
|
||||
"""Add or update a plugin from bytes (gRPC)."""
|
||||
import tempfile
|
||||
import os
|
||||
|
||||
if not name.isalpha() or not name.islower() or len(name) > 15:
|
||||
raise InvalidConfigurationError("Plugin name should be lowercase letters up to 15 characters.")
|
||||
|
||||
# Write to temp file to verify script
|
||||
with tempfile.NamedTemporaryFile(suffix=".py", delete=False) as tmp:
|
||||
tmp.write(content)
|
||||
tmp_path = tmp.name
|
||||
|
||||
try:
|
||||
from connpy.plugins import Plugins
|
||||
p_manager = Plugins()
|
||||
error = p_manager.verify_script(tmp_path)
|
||||
if error:
|
||||
raise InvalidConfigurationError(f"Invalid plugin script: {error}")
|
||||
|
||||
self._save_plugin_file(name, tmp_path, update, is_path=True)
|
||||
finally:
|
||||
if os.path.exists(tmp_path):
|
||||
os.remove(tmp_path)
|
||||
|
||||
def _save_plugin_file(self, name, source, update=False, is_path=True):
|
||||
import os
|
||||
import shutil
|
||||
|
||||
plugin_dir = os.path.join(self.config.defaultdir, "plugins")
|
||||
os.makedirs(plugin_dir, exist_ok=True)
|
||||
|
||||
target_file = os.path.join(plugin_dir, f"{name}.py")
|
||||
backup_file = f"{target_file}.bkp"
|
||||
|
||||
if not update and (os.path.exists(target_file) or os.path.exists(backup_file)):
|
||||
raise InvalidConfigurationError(f"Plugin '{name}' already exists.")
|
||||
|
||||
try:
|
||||
if is_path:
|
||||
shutil.copy2(source, target_file)
|
||||
else:
|
||||
with open(target_file, "wb") as f:
|
||||
f.write(source)
|
||||
except OSError as e:
|
||||
raise InvalidConfigurationError(f"Failed to save plugin file: {e}")
|
||||
|
||||
def delete_plugin(self, name):
|
||||
"""Remove a plugin file permanently."""
|
||||
import os
|
||||
plugin_file = os.path.join(self.config.defaultdir, "plugins", f"{name}.py")
|
||||
disabled_file = f"{plugin_file}.bkp"
|
||||
|
||||
deleted = False
|
||||
for f in [plugin_file, disabled_file]:
|
||||
if os.path.exists(f):
|
||||
try:
|
||||
os.remove(f)
|
||||
deleted = True
|
||||
except OSError as e:
|
||||
raise InvalidConfigurationError(f"Failed to delete plugin file '{f}': {e}")
|
||||
|
||||
if not deleted:
|
||||
raise InvalidConfigurationError(f"Plugin '{name}' not found.")
|
||||
|
||||
def enable_plugin(self, name):
|
||||
"""Activate a plugin by renaming its backup file."""
|
||||
import os
|
||||
plugin_file = os.path.join(self.config.defaultdir, "plugins", f"{name}.py")
|
||||
disabled_file = f"{plugin_file}.bkp"
|
||||
|
||||
if os.path.exists(plugin_file):
|
||||
return False # Already enabled
|
||||
|
||||
if not os.path.exists(disabled_file):
|
||||
raise InvalidConfigurationError(f"Plugin '{name}' not found.")
|
||||
|
||||
try:
|
||||
os.rename(disabled_file, plugin_file)
|
||||
return True
|
||||
except OSError as e:
|
||||
raise InvalidConfigurationError(f"Failed to enable plugin '{name}': {e}")
|
||||
|
||||
def disable_plugin(self, name):
|
||||
"""Deactivate a plugin by renaming it to a backup file."""
|
||||
import os
|
||||
plugin_file = os.path.join(self.config.defaultdir, "plugins", f"{name}.py")
|
||||
disabled_file = f"{plugin_file}.bkp"
|
||||
|
||||
if os.path.exists(disabled_file):
|
||||
return False # Already disabled
|
||||
|
||||
if not os.path.exists(plugin_file):
|
||||
raise InvalidConfigurationError(f"Plugin '{name}' not found or is a core plugin.")
|
||||
|
||||
try:
|
||||
os.rename(plugin_file, disabled_file)
|
||||
return True
|
||||
except OSError as e:
|
||||
raise InvalidConfigurationError(f"Failed to disable plugin '{name}': {e}")
|
||||
|
||||
def get_plugin_source(self, name):
|
||||
import os
|
||||
from ..services.exceptions import InvalidConfigurationError
|
||||
|
||||
plugin_file = os.path.join(self.config.defaultdir, "plugins", f"{name}.py")
|
||||
core_path = os.path.dirname(os.path.realpath(__file__)) + f"/../core_plugins/{name}.py"
|
||||
|
||||
if os.path.exists(plugin_file):
|
||||
target = plugin_file
|
||||
elif os.path.exists(core_path):
|
||||
target = core_path
|
||||
else:
|
||||
raise InvalidConfigurationError(f"Plugin '{name}' not found")
|
||||
|
||||
with open(target, "r") as f:
|
||||
return f.read()
|
||||
|
||||
def invoke_plugin(self, name, args_dict):
|
||||
import sys, io
|
||||
from argparse import Namespace
|
||||
from ..services.exceptions import InvalidConfigurationError
|
||||
from connpy.plugins import Plugins
|
||||
class MockApp:
|
||||
def __init__(self, config):
|
||||
from ..core import node, nodes
|
||||
from ..ai import ai
|
||||
from ..services.provider import ServiceProvider
|
||||
|
||||
self.config = config
|
||||
self.node = node
|
||||
self.nodes = nodes
|
||||
self.ai = ai
|
||||
|
||||
self.services = ServiceProvider(config, mode="local")
|
||||
try:
|
||||
self.nodes_list = self.services.nodes.list_nodes()
|
||||
self.folders = self.services.nodes.list_folders()
|
||||
self.profiles = self.services.profiles.list_profiles()
|
||||
except Exception:
|
||||
self.nodes_list = {}
|
||||
self.folders = {}
|
||||
self.profiles = {}
|
||||
|
||||
args = Namespace(**args_dict)
|
||||
|
||||
p_manager = Plugins()
|
||||
import os
|
||||
plugin_file = os.path.join(self.config.defaultdir, "plugins", f"{name}.py")
|
||||
core_path = os.path.dirname(os.path.realpath(__file__)) + f"/../core_plugins/{name}.py"
|
||||
|
||||
if os.path.exists(plugin_file):
|
||||
target = plugin_file
|
||||
elif os.path.exists(core_path):
|
||||
target = core_path
|
||||
else:
|
||||
raise InvalidConfigurationError(f"Plugin '{name}' not found")
|
||||
|
||||
module = p_manager._import_from_path(target)
|
||||
parser = module.Parser().parser if hasattr(module, "Parser") else None
|
||||
|
||||
if "__func_name__" in args_dict and hasattr(module, args_dict["__func_name__"]):
|
||||
args.func = getattr(module, args_dict["__func_name__"])
|
||||
|
||||
app = MockApp(self.config)
|
||||
|
||||
from .. import printer
|
||||
from rich.console import Console
|
||||
|
||||
buf = io.StringIO()
|
||||
old_console = printer.console
|
||||
old_err_console = printer.err_console
|
||||
|
||||
printer.console = Console(file=buf, theme=printer.connpy_theme, force_terminal=True)
|
||||
printer.err_console = Console(file=buf, theme=printer.connpy_theme, force_terminal=True)
|
||||
|
||||
old_stdout = sys.stdout
|
||||
sys.stdout = buf
|
||||
|
||||
try:
|
||||
if hasattr(module, "Entrypoint"):
|
||||
module.Entrypoint(args, parser, app)
|
||||
except Exception as e:
|
||||
import traceback
|
||||
printer.err_console.print(traceback.format_exc())
|
||||
finally:
|
||||
sys.stdout = old_stdout
|
||||
printer.console = old_console
|
||||
printer.err_console = old_err_console
|
||||
|
||||
for line in buf.getvalue().splitlines(keepends=True):
|
||||
yield line</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Business logic for enabling, disabling, and listing plugins.</p>
|
||||
<p>Initialize the service.</p>
|
||||
<h2 id="args">Args</h2>
|
||||
<dl>
|
||||
<dt><strong><code>config</code></strong></dt>
|
||||
<dd>An instance of configfile (or None to instantiate a new one/use global context).</dd>
|
||||
</dl></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li><a title="connpy.services.base.BaseService" href="base.html#connpy.services.base.BaseService">BaseService</a></li>
|
||||
</ul>
|
||||
<h3>Methods</h3>
|
||||
<dl>
|
||||
<dt id="connpy.services.plugin_service.PluginService.add_plugin"><code class="name flex">
|
||||
<span>def <span class="ident">add_plugin</span></span>(<span>self, name, source_file, update=False)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def add_plugin(self, name, source_file, update=False):
|
||||
"""Add or update a plugin from a local file."""
|
||||
import os
|
||||
import shutil
|
||||
from connpy.plugins import Plugins
|
||||
|
||||
if not name.isalpha() or not name.islower() or len(name) > 15:
|
||||
raise InvalidConfigurationError("Plugin name should be lowercase letters up to 15 characters.")
|
||||
|
||||
p_manager = Plugins()
|
||||
# Check for bad script
|
||||
error = p_manager.verify_script(source_file)
|
||||
if error:
|
||||
raise InvalidConfigurationError(f"Invalid plugin script: {error}")
|
||||
|
||||
self._save_plugin_file(name, source_file, update, is_path=True)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Add or update a plugin from a local file.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.plugin_service.PluginService.add_plugin_from_bytes"><code class="name flex">
|
||||
<span>def <span class="ident">add_plugin_from_bytes</span></span>(<span>self, name, content, update=False)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def add_plugin_from_bytes(self, name, content, update=False):
|
||||
"""Add or update a plugin from bytes (gRPC)."""
|
||||
import tempfile
|
||||
import os
|
||||
|
||||
if not name.isalpha() or not name.islower() or len(name) > 15:
|
||||
raise InvalidConfigurationError("Plugin name should be lowercase letters up to 15 characters.")
|
||||
|
||||
# Write to temp file to verify script
|
||||
with tempfile.NamedTemporaryFile(suffix=".py", delete=False) as tmp:
|
||||
tmp.write(content)
|
||||
tmp_path = tmp.name
|
||||
|
||||
try:
|
||||
from connpy.plugins import Plugins
|
||||
p_manager = Plugins()
|
||||
error = p_manager.verify_script(tmp_path)
|
||||
if error:
|
||||
raise InvalidConfigurationError(f"Invalid plugin script: {error}")
|
||||
|
||||
self._save_plugin_file(name, tmp_path, update, is_path=True)
|
||||
finally:
|
||||
if os.path.exists(tmp_path):
|
||||
os.remove(tmp_path)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Add or update a plugin from bytes (gRPC).</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.plugin_service.PluginService.delete_plugin"><code class="name flex">
|
||||
<span>def <span class="ident">delete_plugin</span></span>(<span>self, name)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def delete_plugin(self, name):
|
||||
"""Remove a plugin file permanently."""
|
||||
import os
|
||||
plugin_file = os.path.join(self.config.defaultdir, "plugins", f"{name}.py")
|
||||
disabled_file = f"{plugin_file}.bkp"
|
||||
|
||||
deleted = False
|
||||
for f in [plugin_file, disabled_file]:
|
||||
if os.path.exists(f):
|
||||
try:
|
||||
os.remove(f)
|
||||
deleted = True
|
||||
except OSError as e:
|
||||
raise InvalidConfigurationError(f"Failed to delete plugin file '{f}': {e}")
|
||||
|
||||
if not deleted:
|
||||
raise InvalidConfigurationError(f"Plugin '{name}' not found.")</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Remove a plugin file permanently.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.plugin_service.PluginService.disable_plugin"><code class="name flex">
|
||||
<span>def <span class="ident">disable_plugin</span></span>(<span>self, name)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def disable_plugin(self, name):
|
||||
"""Deactivate a plugin by renaming it to a backup file."""
|
||||
import os
|
||||
plugin_file = os.path.join(self.config.defaultdir, "plugins", f"{name}.py")
|
||||
disabled_file = f"{plugin_file}.bkp"
|
||||
|
||||
if os.path.exists(disabled_file):
|
||||
return False # Already disabled
|
||||
|
||||
if not os.path.exists(plugin_file):
|
||||
raise InvalidConfigurationError(f"Plugin '{name}' not found or is a core plugin.")
|
||||
|
||||
try:
|
||||
os.rename(plugin_file, disabled_file)
|
||||
return True
|
||||
except OSError as e:
|
||||
raise InvalidConfigurationError(f"Failed to disable plugin '{name}': {e}")</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Deactivate a plugin by renaming it to a backup file.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.plugin_service.PluginService.enable_plugin"><code class="name flex">
|
||||
<span>def <span class="ident">enable_plugin</span></span>(<span>self, name)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def enable_plugin(self, name):
|
||||
"""Activate a plugin by renaming its backup file."""
|
||||
import os
|
||||
plugin_file = os.path.join(self.config.defaultdir, "plugins", f"{name}.py")
|
||||
disabled_file = f"{plugin_file}.bkp"
|
||||
|
||||
if os.path.exists(plugin_file):
|
||||
return False # Already enabled
|
||||
|
||||
if not os.path.exists(disabled_file):
|
||||
raise InvalidConfigurationError(f"Plugin '{name}' not found.")
|
||||
|
||||
try:
|
||||
os.rename(disabled_file, plugin_file)
|
||||
return True
|
||||
except OSError as e:
|
||||
raise InvalidConfigurationError(f"Failed to enable plugin '{name}': {e}")</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Activate a plugin by renaming its backup file.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.plugin_service.PluginService.get_plugin_source"><code class="name flex">
|
||||
<span>def <span class="ident">get_plugin_source</span></span>(<span>self, name)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def get_plugin_source(self, name):
|
||||
import os
|
||||
from ..services.exceptions import InvalidConfigurationError
|
||||
|
||||
plugin_file = os.path.join(self.config.defaultdir, "plugins", f"{name}.py")
|
||||
core_path = os.path.dirname(os.path.realpath(__file__)) + f"/../core_plugins/{name}.py"
|
||||
|
||||
if os.path.exists(plugin_file):
|
||||
target = plugin_file
|
||||
elif os.path.exists(core_path):
|
||||
target = core_path
|
||||
else:
|
||||
raise InvalidConfigurationError(f"Plugin '{name}' not found")
|
||||
|
||||
with open(target, "r") as f:
|
||||
return f.read()</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.plugin_service.PluginService.invoke_plugin"><code class="name flex">
|
||||
<span>def <span class="ident">invoke_plugin</span></span>(<span>self, name, args_dict)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def invoke_plugin(self, name, args_dict):
|
||||
import sys, io
|
||||
from argparse import Namespace
|
||||
from ..services.exceptions import InvalidConfigurationError
|
||||
from connpy.plugins import Plugins
|
||||
class MockApp:
|
||||
def __init__(self, config):
|
||||
from ..core import node, nodes
|
||||
from ..ai import ai
|
||||
from ..services.provider import ServiceProvider
|
||||
|
||||
self.config = config
|
||||
self.node = node
|
||||
self.nodes = nodes
|
||||
self.ai = ai
|
||||
|
||||
self.services = ServiceProvider(config, mode="local")
|
||||
try:
|
||||
self.nodes_list = self.services.nodes.list_nodes()
|
||||
self.folders = self.services.nodes.list_folders()
|
||||
self.profiles = self.services.profiles.list_profiles()
|
||||
except Exception:
|
||||
self.nodes_list = {}
|
||||
self.folders = {}
|
||||
self.profiles = {}
|
||||
|
||||
args = Namespace(**args_dict)
|
||||
|
||||
p_manager = Plugins()
|
||||
import os
|
||||
plugin_file = os.path.join(self.config.defaultdir, "plugins", f"{name}.py")
|
||||
core_path = os.path.dirname(os.path.realpath(__file__)) + f"/../core_plugins/{name}.py"
|
||||
|
||||
if os.path.exists(plugin_file):
|
||||
target = plugin_file
|
||||
elif os.path.exists(core_path):
|
||||
target = core_path
|
||||
else:
|
||||
raise InvalidConfigurationError(f"Plugin '{name}' not found")
|
||||
|
||||
module = p_manager._import_from_path(target)
|
||||
parser = module.Parser().parser if hasattr(module, "Parser") else None
|
||||
|
||||
if "__func_name__" in args_dict and hasattr(module, args_dict["__func_name__"]):
|
||||
args.func = getattr(module, args_dict["__func_name__"])
|
||||
|
||||
app = MockApp(self.config)
|
||||
|
||||
from .. import printer
|
||||
from rich.console import Console
|
||||
|
||||
buf = io.StringIO()
|
||||
old_console = printer.console
|
||||
old_err_console = printer.err_console
|
||||
|
||||
printer.console = Console(file=buf, theme=printer.connpy_theme, force_terminal=True)
|
||||
printer.err_console = Console(file=buf, theme=printer.connpy_theme, force_terminal=True)
|
||||
|
||||
old_stdout = sys.stdout
|
||||
sys.stdout = buf
|
||||
|
||||
try:
|
||||
if hasattr(module, "Entrypoint"):
|
||||
module.Entrypoint(args, parser, app)
|
||||
except Exception as e:
|
||||
import traceback
|
||||
printer.err_console.print(traceback.format_exc())
|
||||
finally:
|
||||
sys.stdout = old_stdout
|
||||
printer.console = old_console
|
||||
printer.err_console = old_err_console
|
||||
|
||||
for line in buf.getvalue().splitlines(keepends=True):
|
||||
yield line</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.plugin_service.PluginService.list_plugins"><code class="name flex">
|
||||
<span>def <span class="ident">list_plugins</span></span>(<span>self)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def list_plugins(self):
|
||||
"""List all core and user-defined plugins with their status and hash."""
|
||||
import os
|
||||
import hashlib
|
||||
|
||||
# Check for user plugins directory
|
||||
plugin_dir = os.path.join(self.config.defaultdir, "plugins")
|
||||
# Check for core plugins directory
|
||||
core_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "core_plugins")
|
||||
|
||||
all_plugin_info = {}
|
||||
|
||||
def get_hash(path):
|
||||
try:
|
||||
with open(path, "rb") as f:
|
||||
return hashlib.md5(f.read()).hexdigest()
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
# User plugins
|
||||
if os.path.exists(plugin_dir):
|
||||
for f in os.listdir(plugin_dir):
|
||||
if f.endswith(".py"):
|
||||
name = f[:-3]
|
||||
path = os.path.join(plugin_dir, f)
|
||||
all_plugin_info[name] = {"enabled": True, "hash": get_hash(path)}
|
||||
elif f.endswith(".py.bkp"):
|
||||
name = f[:-7]
|
||||
all_plugin_info[name] = {"enabled": False}
|
||||
|
||||
return all_plugin_info</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>List all core and user-defined plugins with their status and hash.</p></div>
|
||||
</dd>
|
||||
</dl>
|
||||
<h3>Inherited members</h3>
|
||||
<ul class="hlist">
|
||||
<li><code><b><a title="connpy.services.base.BaseService" href="base.html#connpy.services.base.BaseService">BaseService</a></b></code>:
|
||||
<ul class="hlist">
|
||||
<li><code><a title="connpy.services.base.BaseService.set_reserved_names" href="base.html#connpy.services.base.BaseService.set_reserved_names">set_reserved_names</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</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.services" href="index.html">connpy.services</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<h4><code><a title="connpy.services.plugin_service.PluginService" href="#connpy.services.plugin_service.PluginService">PluginService</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.services.plugin_service.PluginService.add_plugin" href="#connpy.services.plugin_service.PluginService.add_plugin">add_plugin</a></code></li>
|
||||
<li><code><a title="connpy.services.plugin_service.PluginService.add_plugin_from_bytes" href="#connpy.services.plugin_service.PluginService.add_plugin_from_bytes">add_plugin_from_bytes</a></code></li>
|
||||
<li><code><a title="connpy.services.plugin_service.PluginService.delete_plugin" href="#connpy.services.plugin_service.PluginService.delete_plugin">delete_plugin</a></code></li>
|
||||
<li><code><a title="connpy.services.plugin_service.PluginService.disable_plugin" href="#connpy.services.plugin_service.PluginService.disable_plugin">disable_plugin</a></code></li>
|
||||
<li><code><a title="connpy.services.plugin_service.PluginService.enable_plugin" href="#connpy.services.plugin_service.PluginService.enable_plugin">enable_plugin</a></code></li>
|
||||
<li><code><a title="connpy.services.plugin_service.PluginService.get_plugin_source" href="#connpy.services.plugin_service.PluginService.get_plugin_source">get_plugin_source</a></code></li>
|
||||
<li><code><a title="connpy.services.plugin_service.PluginService.invoke_plugin" href="#connpy.services.plugin_service.PluginService.invoke_plugin">invoke_plugin</a></code></li>
|
||||
<li><code><a title="connpy.services.plugin_service.PluginService.list_plugins" href="#connpy.services.plugin_service.PluginService.list_plugins">list_plugins</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,435 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.services.profile_service 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.services.profile_service</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
||||
<dl>
|
||||
<dt id="connpy.services.profile_service.ProfileService"><code class="flex name class">
|
||||
<span>class <span class="ident">ProfileService</span></span>
|
||||
<span>(</span><span>config=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class ProfileService(BaseService):
|
||||
"""Business logic for node profiles management."""
|
||||
|
||||
def list_profiles(self, filter_str=None):
|
||||
"""List all profile names, optionally filtered."""
|
||||
profiles = list(self.config.profiles.keys())
|
||||
case_sensitive = self.config.config.get("case", False)
|
||||
|
||||
if filter_str:
|
||||
if not case_sensitive:
|
||||
f_str = filter_str.lower()
|
||||
return [p for p in profiles if f_str in p.lower()]
|
||||
else:
|
||||
return [p for p in profiles if filter_str in p]
|
||||
return profiles
|
||||
|
||||
def get_profile(self, name, resolve=True):
|
||||
"""Get the profile dictionary, optionally resolved."""
|
||||
profile = self.config.profiles.get(name)
|
||||
if not profile:
|
||||
raise ProfileNotFoundError(f"Profile '{name}' not found.")
|
||||
|
||||
if resolve:
|
||||
return self.resolve_node_data(profile)
|
||||
return profile
|
||||
|
||||
def add_profile(self, name, data):
|
||||
"""Add a new profile."""
|
||||
if name in self.config.profiles:
|
||||
raise ProfileAlreadyExistsError(f"Profile '{name}' already exists.")
|
||||
|
||||
# Filter data to match _profiles_add signature and ensure id is passed
|
||||
allowed_keys = {"host", "options", "logs", "password", "port", "protocol", "user", "tags", "jumphost"}
|
||||
filtered_data = {k: v for k, v in data.items() if k in allowed_keys}
|
||||
|
||||
self.config._profiles_add(id=name, **filtered_data)
|
||||
self.config._saveconfig(self.config.file)
|
||||
|
||||
def resolve_node_data(self, node_data):
|
||||
"""Resolve profile references (@profile) in node data and handle inheritance."""
|
||||
resolved = node_data.copy()
|
||||
|
||||
# 1. Identify all referenced profiles to support inheritance
|
||||
referenced_profiles = []
|
||||
for value in resolved.values():
|
||||
if isinstance(value, str) and value.startswith("@"):
|
||||
referenced_profiles.append(value[1:])
|
||||
elif isinstance(value, list):
|
||||
for item in value:
|
||||
if isinstance(item, str) and item.startswith("@"):
|
||||
referenced_profiles.append(item[1:])
|
||||
|
||||
# 2. Resolve explicit references
|
||||
for key, value in resolved.items():
|
||||
if isinstance(value, str) and value.startswith("@"):
|
||||
profile_name = value[1:]
|
||||
try:
|
||||
profile = self.get_profile(profile_name, resolve=True)
|
||||
resolved[key] = profile.get(key, "")
|
||||
except ProfileNotFoundError:
|
||||
resolved[key] = ""
|
||||
elif isinstance(value, list):
|
||||
resolved_list = []
|
||||
for item in value:
|
||||
if isinstance(item, str) and item.startswith("@"):
|
||||
profile_name = item[1:]
|
||||
try:
|
||||
profile = self.get_profile(profile_name, resolve=True)
|
||||
if "password" in profile:
|
||||
resolved_list.append(profile["password"])
|
||||
except ProfileNotFoundError:
|
||||
pass
|
||||
else:
|
||||
resolved_list.append(item)
|
||||
resolved[key] = resolved_list
|
||||
|
||||
# 3. Inheritance: Fill empty keys from the first referenced profile
|
||||
if referenced_profiles:
|
||||
base_profile_name = referenced_profiles[0]
|
||||
try:
|
||||
base_profile = self.get_profile(base_profile_name, resolve=True)
|
||||
for key, value in base_profile.items():
|
||||
# Fill if key is missing or empty
|
||||
if key not in resolved or resolved[key] == "" or resolved[key] == [] or resolved[key] is None:
|
||||
resolved[key] = value
|
||||
except ProfileNotFoundError:
|
||||
pass
|
||||
|
||||
# 4. Handle default protocol
|
||||
if resolved.get("protocol") == "" or resolved.get("protocol") is None:
|
||||
try:
|
||||
default_profile = self.get_profile("default", resolve=True)
|
||||
resolved["protocol"] = default_profile.get("protocol", "ssh")
|
||||
except ProfileNotFoundError:
|
||||
resolved["protocol"] = "ssh"
|
||||
|
||||
return resolved
|
||||
|
||||
def delete_profile(self, name):
|
||||
"""Delete an existing profile, with safety checks."""
|
||||
if name not in self.config.profiles:
|
||||
raise ProfileNotFoundError(f"Profile '{name}' not found.")
|
||||
|
||||
if name == "default":
|
||||
raise InvalidConfigurationError("Cannot delete the 'default' profile.")
|
||||
|
||||
used_by = self.config._profileused(name)
|
||||
if used_by:
|
||||
# We return the list of nodes using it so the UI can inform the user
|
||||
raise InvalidConfigurationError(f"Profile '{name}' is used by nodes: {', '.join(used_by)}")
|
||||
|
||||
self.config._profiles_del(id=name)
|
||||
self.config._saveconfig(self.config.file)
|
||||
|
||||
def update_profile(self, name, data):
|
||||
"""Update an existing profile."""
|
||||
if name not in self.config.profiles:
|
||||
raise ProfileNotFoundError(f"Profile '{name}' not found.")
|
||||
|
||||
# Merge with existing data
|
||||
existing = self.get_profile(name, resolve=False)
|
||||
updated_data = existing.copy()
|
||||
updated_data.update(data)
|
||||
|
||||
# Filter data to match _profiles_add signature
|
||||
allowed_keys = {"host", "options", "logs", "password", "port", "protocol", "user", "tags", "jumphost"}
|
||||
filtered_data = {k: v for k, v in updated_data.items() if k in allowed_keys}
|
||||
|
||||
self.config._profiles_add(id=name, **filtered_data)
|
||||
self.config._saveconfig(self.config.file)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Business logic for node profiles management.</p>
|
||||
<p>Initialize the service.</p>
|
||||
<h2 id="args">Args</h2>
|
||||
<dl>
|
||||
<dt><strong><code>config</code></strong></dt>
|
||||
<dd>An instance of configfile (or None to instantiate a new one/use global context).</dd>
|
||||
</dl></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li><a title="connpy.services.base.BaseService" href="base.html#connpy.services.base.BaseService">BaseService</a></li>
|
||||
</ul>
|
||||
<h3>Methods</h3>
|
||||
<dl>
|
||||
<dt id="connpy.services.profile_service.ProfileService.add_profile"><code class="name flex">
|
||||
<span>def <span class="ident">add_profile</span></span>(<span>self, name, data)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def add_profile(self, name, data):
|
||||
"""Add a new profile."""
|
||||
if name in self.config.profiles:
|
||||
raise ProfileAlreadyExistsError(f"Profile '{name}' already exists.")
|
||||
|
||||
# Filter data to match _profiles_add signature and ensure id is passed
|
||||
allowed_keys = {"host", "options", "logs", "password", "port", "protocol", "user", "tags", "jumphost"}
|
||||
filtered_data = {k: v for k, v in data.items() if k in allowed_keys}
|
||||
|
||||
self.config._profiles_add(id=name, **filtered_data)
|
||||
self.config._saveconfig(self.config.file)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Add a new profile.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.profile_service.ProfileService.delete_profile"><code class="name flex">
|
||||
<span>def <span class="ident">delete_profile</span></span>(<span>self, name)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def delete_profile(self, name):
|
||||
"""Delete an existing profile, with safety checks."""
|
||||
if name not in self.config.profiles:
|
||||
raise ProfileNotFoundError(f"Profile '{name}' not found.")
|
||||
|
||||
if name == "default":
|
||||
raise InvalidConfigurationError("Cannot delete the 'default' profile.")
|
||||
|
||||
used_by = self.config._profileused(name)
|
||||
if used_by:
|
||||
# We return the list of nodes using it so the UI can inform the user
|
||||
raise InvalidConfigurationError(f"Profile '{name}' is used by nodes: {', '.join(used_by)}")
|
||||
|
||||
self.config._profiles_del(id=name)
|
||||
self.config._saveconfig(self.config.file)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Delete an existing profile, with safety checks.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.profile_service.ProfileService.get_profile"><code class="name flex">
|
||||
<span>def <span class="ident">get_profile</span></span>(<span>self, name, resolve=True)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def get_profile(self, name, resolve=True):
|
||||
"""Get the profile dictionary, optionally resolved."""
|
||||
profile = self.config.profiles.get(name)
|
||||
if not profile:
|
||||
raise ProfileNotFoundError(f"Profile '{name}' not found.")
|
||||
|
||||
if resolve:
|
||||
return self.resolve_node_data(profile)
|
||||
return profile</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Get the profile dictionary, optionally resolved.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.profile_service.ProfileService.list_profiles"><code class="name flex">
|
||||
<span>def <span class="ident">list_profiles</span></span>(<span>self, filter_str=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def list_profiles(self, filter_str=None):
|
||||
"""List all profile names, optionally filtered."""
|
||||
profiles = list(self.config.profiles.keys())
|
||||
case_sensitive = self.config.config.get("case", False)
|
||||
|
||||
if filter_str:
|
||||
if not case_sensitive:
|
||||
f_str = filter_str.lower()
|
||||
return [p for p in profiles if f_str in p.lower()]
|
||||
else:
|
||||
return [p for p in profiles if filter_str in p]
|
||||
return profiles</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>List all profile names, optionally filtered.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.profile_service.ProfileService.resolve_node_data"><code class="name flex">
|
||||
<span>def <span class="ident">resolve_node_data</span></span>(<span>self, node_data)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def resolve_node_data(self, node_data):
|
||||
"""Resolve profile references (@profile) in node data and handle inheritance."""
|
||||
resolved = node_data.copy()
|
||||
|
||||
# 1. Identify all referenced profiles to support inheritance
|
||||
referenced_profiles = []
|
||||
for value in resolved.values():
|
||||
if isinstance(value, str) and value.startswith("@"):
|
||||
referenced_profiles.append(value[1:])
|
||||
elif isinstance(value, list):
|
||||
for item in value:
|
||||
if isinstance(item, str) and item.startswith("@"):
|
||||
referenced_profiles.append(item[1:])
|
||||
|
||||
# 2. Resolve explicit references
|
||||
for key, value in resolved.items():
|
||||
if isinstance(value, str) and value.startswith("@"):
|
||||
profile_name = value[1:]
|
||||
try:
|
||||
profile = self.get_profile(profile_name, resolve=True)
|
||||
resolved[key] = profile.get(key, "")
|
||||
except ProfileNotFoundError:
|
||||
resolved[key] = ""
|
||||
elif isinstance(value, list):
|
||||
resolved_list = []
|
||||
for item in value:
|
||||
if isinstance(item, str) and item.startswith("@"):
|
||||
profile_name = item[1:]
|
||||
try:
|
||||
profile = self.get_profile(profile_name, resolve=True)
|
||||
if "password" in profile:
|
||||
resolved_list.append(profile["password"])
|
||||
except ProfileNotFoundError:
|
||||
pass
|
||||
else:
|
||||
resolved_list.append(item)
|
||||
resolved[key] = resolved_list
|
||||
|
||||
# 3. Inheritance: Fill empty keys from the first referenced profile
|
||||
if referenced_profiles:
|
||||
base_profile_name = referenced_profiles[0]
|
||||
try:
|
||||
base_profile = self.get_profile(base_profile_name, resolve=True)
|
||||
for key, value in base_profile.items():
|
||||
# Fill if key is missing or empty
|
||||
if key not in resolved or resolved[key] == "" or resolved[key] == [] or resolved[key] is None:
|
||||
resolved[key] = value
|
||||
except ProfileNotFoundError:
|
||||
pass
|
||||
|
||||
# 4. Handle default protocol
|
||||
if resolved.get("protocol") == "" or resolved.get("protocol") is None:
|
||||
try:
|
||||
default_profile = self.get_profile("default", resolve=True)
|
||||
resolved["protocol"] = default_profile.get("protocol", "ssh")
|
||||
except ProfileNotFoundError:
|
||||
resolved["protocol"] = "ssh"
|
||||
|
||||
return resolved</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Resolve profile references (@profile) in node data and handle inheritance.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.profile_service.ProfileService.update_profile"><code class="name flex">
|
||||
<span>def <span class="ident">update_profile</span></span>(<span>self, name, data)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def update_profile(self, name, data):
|
||||
"""Update an existing profile."""
|
||||
if name not in self.config.profiles:
|
||||
raise ProfileNotFoundError(f"Profile '{name}' not found.")
|
||||
|
||||
# Merge with existing data
|
||||
existing = self.get_profile(name, resolve=False)
|
||||
updated_data = existing.copy()
|
||||
updated_data.update(data)
|
||||
|
||||
# Filter data to match _profiles_add signature
|
||||
allowed_keys = {"host", "options", "logs", "password", "port", "protocol", "user", "tags", "jumphost"}
|
||||
filtered_data = {k: v for k, v in updated_data.items() if k in allowed_keys}
|
||||
|
||||
self.config._profiles_add(id=name, **filtered_data)
|
||||
self.config._saveconfig(self.config.file)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Update an existing profile.</p></div>
|
||||
</dd>
|
||||
</dl>
|
||||
<h3>Inherited members</h3>
|
||||
<ul class="hlist">
|
||||
<li><code><b><a title="connpy.services.base.BaseService" href="base.html#connpy.services.base.BaseService">BaseService</a></b></code>:
|
||||
<ul class="hlist">
|
||||
<li><code><a title="connpy.services.base.BaseService.set_reserved_names" href="base.html#connpy.services.base.BaseService.set_reserved_names">set_reserved_names</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</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.services" href="index.html">connpy.services</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<h4><code><a title="connpy.services.profile_service.ProfileService" href="#connpy.services.profile_service.ProfileService">ProfileService</a></code></h4>
|
||||
<ul class="two-column">
|
||||
<li><code><a title="connpy.services.profile_service.ProfileService.add_profile" href="#connpy.services.profile_service.ProfileService.add_profile">add_profile</a></code></li>
|
||||
<li><code><a title="connpy.services.profile_service.ProfileService.delete_profile" href="#connpy.services.profile_service.ProfileService.delete_profile">delete_profile</a></code></li>
|
||||
<li><code><a title="connpy.services.profile_service.ProfileService.get_profile" href="#connpy.services.profile_service.ProfileService.get_profile">get_profile</a></code></li>
|
||||
<li><code><a title="connpy.services.profile_service.ProfileService.list_profiles" href="#connpy.services.profile_service.ProfileService.list_profiles">list_profiles</a></code></li>
|
||||
<li><code><a title="connpy.services.profile_service.ProfileService.resolve_node_data" href="#connpy.services.profile_service.ProfileService.resolve_node_data">resolve_node_data</a></code></li>
|
||||
<li><code><a title="connpy.services.profile_service.ProfileService.update_profile" href="#connpy.services.profile_service.ProfileService.update_profile">update_profile</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,170 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.services.provider 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.services.provider</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
||||
<dl>
|
||||
<dt id="connpy.services.provider.RemoteStub"><code class="flex name class">
|
||||
<span>class <span class="ident">RemoteStub</span></span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class RemoteStub:
|
||||
def __getattr__(self, name):
|
||||
raise NotImplementedError(
|
||||
"Remote mode (gRPC) is not yet available. "
|
||||
"Use local mode or wait for the gRPC implementation."
|
||||
)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.provider.ServiceProvider"><code class="flex name class">
|
||||
<span>class <span class="ident">ServiceProvider</span></span>
|
||||
<span>(</span><span>config, mode='local', remote_host=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class ServiceProvider:
|
||||
"""Dynamic service backend. Transparently provides local or remote services."""
|
||||
|
||||
def __init__(self, config, mode="local", remote_host=None):
|
||||
self.mode = mode
|
||||
self.config = config
|
||||
self.remote_host = remote_host
|
||||
|
||||
if mode == "local":
|
||||
self._init_local()
|
||||
elif mode == "remote":
|
||||
self._init_remote()
|
||||
else:
|
||||
raise ValueError(f"Unknown service mode: {mode}")
|
||||
|
||||
def _init_local(self):
|
||||
from .node_service import NodeService
|
||||
from .profile_service import ProfileService
|
||||
from .config_service import ConfigService
|
||||
from .plugin_service import PluginService
|
||||
from .ai_service import AIService
|
||||
from .system_service import SystemService
|
||||
from .execution_service import ExecutionService
|
||||
from .import_export_service import ImportExportService
|
||||
from .context_service import ContextService
|
||||
from .sync_service import SyncService
|
||||
|
||||
self.nodes = NodeService(self.config)
|
||||
self.profiles = ProfileService(self.config)
|
||||
self.config_svc = ConfigService(self.config)
|
||||
self.plugins = PluginService(self.config)
|
||||
self.ai = AIService(self.config)
|
||||
self.system = SystemService(self.config)
|
||||
self.execution = ExecutionService(self.config)
|
||||
self.import_export = ImportExportService(self.config)
|
||||
self.context = ContextService(self.config)
|
||||
self.sync = SyncService(self.config)
|
||||
|
||||
def _init_remote(self):
|
||||
# Allow ConfigService to work locally so the user can revert the mode
|
||||
from .config_service import ConfigService
|
||||
from .context_service import ContextService
|
||||
from .sync_service import SyncService
|
||||
self.config_svc = ConfigService(self.config)
|
||||
self.context = ContextService(self.config)
|
||||
self.sync = SyncService(self.config)
|
||||
|
||||
if not self.remote_host:
|
||||
raise InvalidConfigurationError("Remote host must be specified in remote mode")
|
||||
|
||||
import grpc
|
||||
from ..grpc.stubs import NodeStub, ProfileStub, PluginStub, AIStub, ExecutionStub, ImportExportStub, SystemStub
|
||||
|
||||
channel = grpc.insecure_channel(self.remote_host)
|
||||
|
||||
self.nodes = NodeStub(channel, remote_host=self.remote_host, config=self.config)
|
||||
self.profiles = ProfileStub(channel, remote_host=self.remote_host, node_stub=self.nodes)
|
||||
self.plugins = PluginStub(channel, remote_host=self.remote_host)
|
||||
self.ai = AIStub(channel, remote_host=self.remote_host)
|
||||
self.system = SystemStub(channel, remote_host=self.remote_host)
|
||||
self.execution = ExecutionStub(channel, remote_host=self.remote_host)
|
||||
self.import_export = ImportExportStub(channel, remote_host=self.remote_host)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Dynamic service backend. Transparently provides local or remote services.</p></div>
|
||||
</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.services" href="index.html">connpy.services</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<h4><code><a title="connpy.services.provider.RemoteStub" href="#connpy.services.provider.RemoteStub">RemoteStub</a></code></h4>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.services.provider.ServiceProvider" href="#connpy.services.provider.ServiceProvider">ServiceProvider</a></code></h4>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,970 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.services.sync_service 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.services.sync_service</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
||||
<dl>
|
||||
<dt id="connpy.services.sync_service.SyncService"><code class="flex name class">
|
||||
<span>class <span class="ident">SyncService</span></span>
|
||||
<span>(</span><span>config)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class SyncService(BaseService):
|
||||
"""Business logic for Google Drive synchronization."""
|
||||
|
||||
def __init__(self, config):
|
||||
super().__init__(config)
|
||||
self.scopes = ['https://www.googleapis.com/auth/drive.appdata']
|
||||
self.token_file = os.path.join(self.config.defaultdir, "gtoken.json")
|
||||
|
||||
# Embedded OAuth config
|
||||
self.client_config = {
|
||||
"installed": {
|
||||
"client_id": "559598250648-cr189kfrga2il1a6d6nkaspq0a9pn5vv." + "apps.googleusercontent.com",
|
||||
"project_id": "celtic-surface-420323",
|
||||
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
||||
"token_uri": "https://oauth2.googleapis.com/token",
|
||||
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
||||
"client_secret": "GOCSPX-" + "VVfOSrJLPU90Pl0g7aAXM9GK2xPE",
|
||||
"redirect_uris": ["http://localhost"]
|
||||
}
|
||||
}
|
||||
|
||||
# Sync status from config
|
||||
self.sync_enabled = self.config.config.get("sync", False)
|
||||
self.sync_remote = self.config.config.get("sync_remote", False)
|
||||
|
||||
def login(self):
|
||||
"""Authenticate with Google Drive."""
|
||||
creds = None
|
||||
if os.path.exists(self.token_file):
|
||||
creds = Credentials.from_authorized_user_file(self.token_file, self.scopes)
|
||||
|
||||
try:
|
||||
if not creds or not creds.valid:
|
||||
if creds and creds.expired and creds.refresh_token:
|
||||
creds.refresh(Request())
|
||||
else:
|
||||
flow = InstalledAppFlow.from_client_config(self.client_config, self.scopes)
|
||||
creds = flow.run_local_server(port=0, access_type='offline')
|
||||
|
||||
with open(self.token_file, 'w') as token:
|
||||
token.write(creds.to_json())
|
||||
|
||||
printer.success("Logged in successfully.")
|
||||
return True
|
||||
|
||||
except RefreshError:
|
||||
if os.path.exists(self.token_file):
|
||||
os.remove(self.token_file)
|
||||
printer.warning("Existing token was invalid and has been removed. Please log in again.")
|
||||
return False
|
||||
except Exception as e:
|
||||
printer.error(f"Login failed: {e}")
|
||||
return False
|
||||
|
||||
def logout(self):
|
||||
"""Remove Google Drive credentials."""
|
||||
if os.path.exists(self.token_file):
|
||||
os.remove(self.token_file)
|
||||
printer.success("Logged out successfully.")
|
||||
else:
|
||||
printer.info("No credentials file found. Already logged out.")
|
||||
|
||||
def get_credentials(self):
|
||||
"""Get valid credentials, refreshing if necessary."""
|
||||
if os.path.exists(self.token_file):
|
||||
creds = Credentials.from_authorized_user_file(self.token_file, self.scopes)
|
||||
else:
|
||||
return None
|
||||
|
||||
if not creds or not creds.valid:
|
||||
if creds and creds.expired and creds.refresh_token:
|
||||
try:
|
||||
creds.refresh(Request())
|
||||
except RefreshError:
|
||||
return None
|
||||
else:
|
||||
return None
|
||||
return creds
|
||||
|
||||
def check_login_status(self):
|
||||
"""Check if logged in to Google Drive."""
|
||||
if os.path.exists(self.token_file):
|
||||
creds = Credentials.from_authorized_user_file(self.token_file)
|
||||
if creds and creds.expired and creds.refresh_token:
|
||||
try:
|
||||
creds.refresh(Request())
|
||||
except RefreshError:
|
||||
pass
|
||||
return True if creds.valid else "Invalid"
|
||||
return False
|
||||
|
||||
def list_backups(self):
|
||||
"""List files in Google Drive appDataFolder."""
|
||||
creds = self.get_credentials()
|
||||
if not creds:
|
||||
printer.error("Not logged in to Google Drive.")
|
||||
return []
|
||||
|
||||
try:
|
||||
service = build("drive", "v3", credentials=creds)
|
||||
response = service.files().list(
|
||||
spaces="appDataFolder",
|
||||
fields="files(id, name, appProperties)",
|
||||
pageSize=10,
|
||||
).execute()
|
||||
|
||||
files_info = []
|
||||
for file in response.get("files", []):
|
||||
files_info.append({
|
||||
"name": file.get("name"),
|
||||
"id": file.get("id"),
|
||||
"date": file.get("appProperties", {}).get("date"),
|
||||
"timestamp": file.get("appProperties", {}).get("timestamp")
|
||||
})
|
||||
return files_info
|
||||
except HttpError as error:
|
||||
printer.error(f"Google Drive API error: {error}")
|
||||
return []
|
||||
|
||||
def compress_and_upload(self, remote_data=None):
|
||||
"""Compress config and upload to Drive."""
|
||||
timestamp = int(time.time() * 1000)
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
zip_path = os.path.join(tmp_dir, f"connpy-backup-{timestamp}.zip")
|
||||
|
||||
with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
|
||||
# If we have remote data, we create a virtual config file
|
||||
if remote_data:
|
||||
config_tmp = os.path.join(tmp_dir, "config.yaml")
|
||||
with open(config_tmp, 'w') as f:
|
||||
yaml.dump(remote_data, f, default_flow_style=False)
|
||||
zipf.write(config_tmp, "config.yaml")
|
||||
else:
|
||||
# Legacy behavior: use local file
|
||||
zipf.write(self.config.file, os.path.basename(self.config.file))
|
||||
|
||||
# Always include the key if it exists
|
||||
if os.path.exists(self.config.key):
|
||||
zipf.write(self.config.key, ".osk")
|
||||
|
||||
# Manage retention (max 10 backups)
|
||||
backups = self.list_backups()
|
||||
if len(backups) >= 10:
|
||||
oldest = min(backups, key=lambda x: x['timestamp'] or '0')
|
||||
self.delete_backup(oldest['id'])
|
||||
|
||||
# Upload
|
||||
return self.upload_file(zip_path, timestamp)
|
||||
|
||||
def upload_file(self, file_path, timestamp):
|
||||
"""Internal method to upload to Drive."""
|
||||
creds = self.get_credentials()
|
||||
if not creds: return False
|
||||
|
||||
service = build('drive', 'v3', credentials=creds)
|
||||
date_str = datetime.fromtimestamp(timestamp/1000).strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
file_metadata = {
|
||||
'name': os.path.basename(file_path),
|
||||
'parents': ["appDataFolder"],
|
||||
'appProperties': {
|
||||
'timestamp': str(timestamp),
|
||||
'date': date_str
|
||||
}
|
||||
}
|
||||
media = MediaFileUpload(file_path)
|
||||
try:
|
||||
service.files().create(body=file_metadata, media_body=media, fields='id').execute()
|
||||
printer.success("Backup uploaded to Google Drive.")
|
||||
return True
|
||||
except Exception as e:
|
||||
printer.error(f"Upload failed: {e}")
|
||||
return False
|
||||
|
||||
def delete_backup(self, file_id):
|
||||
"""Delete a backup from Drive."""
|
||||
creds = self.get_credentials()
|
||||
if not creds: return False
|
||||
try:
|
||||
service = build("drive", "v3", credentials=creds)
|
||||
service.files().delete(fileId=file_id).execute()
|
||||
return True
|
||||
except Exception as e:
|
||||
printer.error(f"Delete failed: {e}")
|
||||
return False
|
||||
|
||||
def restore_backup(self, file_id=None, restore_config=True, restore_nodes=True, app_instance=None):
|
||||
"""Download and analyze a backup for restoration."""
|
||||
backups = self.list_backups()
|
||||
if not backups:
|
||||
printer.error("No backups found.")
|
||||
return None
|
||||
|
||||
if file_id:
|
||||
selected = next((f for f in backups if f['id'] == file_id), None)
|
||||
if not selected:
|
||||
printer.error(f"Backup {file_id} not found.")
|
||||
return None
|
||||
else:
|
||||
selected = max(backups, key=lambda x: x['timestamp'] or '0')
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
zip_path = os.path.join(tmp_dir, 'restore.zip')
|
||||
if self.download_file(selected['id'], zip_path):
|
||||
return self.perform_restore(zip_path, restore_config, restore_nodes, app_instance)
|
||||
return False
|
||||
|
||||
def download_file(self, file_id, dest):
|
||||
"""Internal method to download from Drive."""
|
||||
creds = self.get_credentials()
|
||||
if not creds: return False
|
||||
try:
|
||||
service = build('drive', 'v3', credentials=creds)
|
||||
request = service.files().get_media(fileId=file_id)
|
||||
with io.FileIO(dest, mode='wb') as fh:
|
||||
downloader = MediaIoBaseDownload(fh, request)
|
||||
done = False
|
||||
while not done:
|
||||
_, done = downloader.next_chunk()
|
||||
return True
|
||||
except Exception as e:
|
||||
printer.error(f"Download failed: {e}")
|
||||
return False
|
||||
|
||||
def perform_restore(self, zip_path, restore_config=True, restore_nodes=True, app_instance=None):
|
||||
"""Execute the actual restoration of files or remote nodes."""
|
||||
try:
|
||||
with zipfile.ZipFile(zip_path, 'r') as zipf:
|
||||
names = zipf.namelist()
|
||||
dest_dir = os.path.dirname(self.config.file)
|
||||
|
||||
# We need to read the config content from zip to decide what to do
|
||||
backup_data = {}
|
||||
config_filename = "config.yaml" if "config.yaml" in names else ("config.json" if "config.json" in names else None)
|
||||
|
||||
if config_filename:
|
||||
with zipf.open(config_filename) as f:
|
||||
backup_data = yaml.safe_load(f)
|
||||
|
||||
# 1. Restore Key (.osk) - Part of config identity
|
||||
if restore_config and ".osk" in names:
|
||||
zipf.extract(".osk", os.path.dirname(self.config.key))
|
||||
|
||||
# 2. Restore Config (Local Settings)
|
||||
if restore_config and backup_data:
|
||||
local_config = self.config.config.copy()
|
||||
|
||||
# Capture current connectivity settings to preserve them
|
||||
current_mode = local_config.get("service_mode", "local")
|
||||
current_remote = local_config.get("remote_host")
|
||||
|
||||
if "config" in backup_data:
|
||||
local_config.update(backup_data["config"])
|
||||
|
||||
# Restore connectivity settings - we don't want a restore to
|
||||
# accidentally switch us between local and remote and break connectivity
|
||||
local_config["service_mode"] = current_mode
|
||||
if current_remote:
|
||||
local_config["remote_host"] = current_remote
|
||||
|
||||
self.config.config = local_config
|
||||
self.config._saveconfig(self.config.file)
|
||||
|
||||
# 3. Restore Nodes and Profiles
|
||||
if restore_nodes and backup_data:
|
||||
connections = backup_data.get("connections", {})
|
||||
profiles = backup_data.get("profiles", {})
|
||||
|
||||
if app_instance and app_instance.services.mode == "remote":
|
||||
# Push to Remote via gRPC
|
||||
app_instance.services.nodes.full_replace(connections, profiles)
|
||||
else:
|
||||
# Restore to Local config file
|
||||
self.config.connections = connections
|
||||
self.config.profiles = profiles
|
||||
self.config._saveconfig(self.config.file)
|
||||
|
||||
# Clear caches
|
||||
for f in [self.config.cachefile, self.config.fzf_cachefile]:
|
||||
if os.path.exists(f): os.remove(f)
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
printer.error(f"Restoration failed: {e}")
|
||||
return False
|
||||
|
||||
def analyze_backup_content(self, file_id=None):
|
||||
"""Analyze a backup without restoring to provide info for confirmation."""
|
||||
backups = self.list_backups()
|
||||
if not backups: return None
|
||||
selected = next((f for f in backups if f['id'] == file_id), None) if file_id else max(backups, key=lambda x: x['timestamp'] or '0')
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
zip_path = os.path.join(tmp_dir, 'analyze.zip')
|
||||
if self.download_file(selected['id'], zip_path):
|
||||
with zipfile.ZipFile(zip_path, 'r') as zipf:
|
||||
names = zipf.namelist()
|
||||
config_filename = "config.yaml" if "config.yaml" in names else ("config.json" if "config.json" in names else None)
|
||||
if config_filename:
|
||||
with zipf.open(config_filename) as f:
|
||||
data = yaml.safe_load(f)
|
||||
connections = data.get("connections", {})
|
||||
|
||||
# Accurate recursive count
|
||||
nodes_count = 0
|
||||
folders_count = 0
|
||||
|
||||
# Layer 1
|
||||
for k, v in connections.items():
|
||||
if isinstance(v, dict):
|
||||
if v.get("type") == "connection":
|
||||
nodes_count += 1
|
||||
elif v.get("type") == "folder":
|
||||
folders_count += 1
|
||||
# Layer 2
|
||||
for k2, v2 in v.items():
|
||||
if isinstance(v2, dict):
|
||||
if v2.get("type") == "connection":
|
||||
nodes_count += 1
|
||||
elif v2.get("type") == "subfolder":
|
||||
folders_count += 1
|
||||
# Layer 3
|
||||
for k3, v3 in v2.items():
|
||||
if isinstance(v3, dict) and v3.get("type") == "connection":
|
||||
nodes_count += 1
|
||||
|
||||
return {
|
||||
"nodes": nodes_count,
|
||||
"folders": folders_count,
|
||||
"profiles": len(data.get("profiles", {})),
|
||||
"has_config": "config" in data,
|
||||
"has_key": ".osk" in names
|
||||
}
|
||||
return None
|
||||
|
||||
def perform_sync(self, app_instance):
|
||||
"""Background sync logic."""
|
||||
# Always check current config state
|
||||
sync_enabled = self.config.config.get("sync", False)
|
||||
sync_remote = self.config.config.get("sync_remote", False)
|
||||
|
||||
if not sync_enabled: return
|
||||
|
||||
printer.info("Triggering auto-sync...")
|
||||
if self.check_login_status() != True:
|
||||
printer.warning("Auto-sync: Not logged in to Google Drive.")
|
||||
return
|
||||
|
||||
remote_data = None
|
||||
if sync_remote and app_instance.services.mode == "remote":
|
||||
try:
|
||||
inventory = app_instance.services.nodes.get_inventory()
|
||||
# Merge with local settings
|
||||
local_settings = app_instance.services.config_svc.get_settings()
|
||||
local_settings.pop("configfolder", None)
|
||||
|
||||
# Maintain proper config structure: {config: {}, connections: {}, profiles: {}}
|
||||
remote_data = {
|
||||
"config": local_settings,
|
||||
"connections": inventory.get("connections", {}),
|
||||
"profiles": inventory.get("profiles", {})
|
||||
}
|
||||
except Exception as e:
|
||||
printer.warning(f"Could not fetch remote inventory for sync: {e}")
|
||||
|
||||
# Run in thread to not block CLI
|
||||
threading.Thread(
|
||||
target=self.compress_and_upload,
|
||||
args=(remote_data,)
|
||||
).start()</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Business logic for Google Drive synchronization.</p>
|
||||
<p>Initialize the service.</p>
|
||||
<h2 id="args">Args</h2>
|
||||
<dl>
|
||||
<dt><strong><code>config</code></strong></dt>
|
||||
<dd>An instance of configfile (or None to instantiate a new one/use global context).</dd>
|
||||
</dl></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li><a title="connpy.services.base.BaseService" href="base.html#connpy.services.base.BaseService">BaseService</a></li>
|
||||
</ul>
|
||||
<h3>Methods</h3>
|
||||
<dl>
|
||||
<dt id="connpy.services.sync_service.SyncService.analyze_backup_content"><code class="name flex">
|
||||
<span>def <span class="ident">analyze_backup_content</span></span>(<span>self, file_id=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def analyze_backup_content(self, file_id=None):
|
||||
"""Analyze a backup without restoring to provide info for confirmation."""
|
||||
backups = self.list_backups()
|
||||
if not backups: return None
|
||||
selected = next((f for f in backups if f['id'] == file_id), None) if file_id else max(backups, key=lambda x: x['timestamp'] or '0')
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
zip_path = os.path.join(tmp_dir, 'analyze.zip')
|
||||
if self.download_file(selected['id'], zip_path):
|
||||
with zipfile.ZipFile(zip_path, 'r') as zipf:
|
||||
names = zipf.namelist()
|
||||
config_filename = "config.yaml" if "config.yaml" in names else ("config.json" if "config.json" in names else None)
|
||||
if config_filename:
|
||||
with zipf.open(config_filename) as f:
|
||||
data = yaml.safe_load(f)
|
||||
connections = data.get("connections", {})
|
||||
|
||||
# Accurate recursive count
|
||||
nodes_count = 0
|
||||
folders_count = 0
|
||||
|
||||
# Layer 1
|
||||
for k, v in connections.items():
|
||||
if isinstance(v, dict):
|
||||
if v.get("type") == "connection":
|
||||
nodes_count += 1
|
||||
elif v.get("type") == "folder":
|
||||
folders_count += 1
|
||||
# Layer 2
|
||||
for k2, v2 in v.items():
|
||||
if isinstance(v2, dict):
|
||||
if v2.get("type") == "connection":
|
||||
nodes_count += 1
|
||||
elif v2.get("type") == "subfolder":
|
||||
folders_count += 1
|
||||
# Layer 3
|
||||
for k3, v3 in v2.items():
|
||||
if isinstance(v3, dict) and v3.get("type") == "connection":
|
||||
nodes_count += 1
|
||||
|
||||
return {
|
||||
"nodes": nodes_count,
|
||||
"folders": folders_count,
|
||||
"profiles": len(data.get("profiles", {})),
|
||||
"has_config": "config" in data,
|
||||
"has_key": ".osk" in names
|
||||
}
|
||||
return None</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Analyze a backup without restoring to provide info for confirmation.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.sync_service.SyncService.check_login_status"><code class="name flex">
|
||||
<span>def <span class="ident">check_login_status</span></span>(<span>self)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def check_login_status(self):
|
||||
"""Check if logged in to Google Drive."""
|
||||
if os.path.exists(self.token_file):
|
||||
creds = Credentials.from_authorized_user_file(self.token_file)
|
||||
if creds and creds.expired and creds.refresh_token:
|
||||
try:
|
||||
creds.refresh(Request())
|
||||
except RefreshError:
|
||||
pass
|
||||
return True if creds.valid else "Invalid"
|
||||
return False</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Check if logged in to Google Drive.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.sync_service.SyncService.compress_and_upload"><code class="name flex">
|
||||
<span>def <span class="ident">compress_and_upload</span></span>(<span>self, remote_data=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def compress_and_upload(self, remote_data=None):
|
||||
"""Compress config and upload to Drive."""
|
||||
timestamp = int(time.time() * 1000)
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
zip_path = os.path.join(tmp_dir, f"connpy-backup-{timestamp}.zip")
|
||||
|
||||
with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
|
||||
# If we have remote data, we create a virtual config file
|
||||
if remote_data:
|
||||
config_tmp = os.path.join(tmp_dir, "config.yaml")
|
||||
with open(config_tmp, 'w') as f:
|
||||
yaml.dump(remote_data, f, default_flow_style=False)
|
||||
zipf.write(config_tmp, "config.yaml")
|
||||
else:
|
||||
# Legacy behavior: use local file
|
||||
zipf.write(self.config.file, os.path.basename(self.config.file))
|
||||
|
||||
# Always include the key if it exists
|
||||
if os.path.exists(self.config.key):
|
||||
zipf.write(self.config.key, ".osk")
|
||||
|
||||
# Manage retention (max 10 backups)
|
||||
backups = self.list_backups()
|
||||
if len(backups) >= 10:
|
||||
oldest = min(backups, key=lambda x: x['timestamp'] or '0')
|
||||
self.delete_backup(oldest['id'])
|
||||
|
||||
# Upload
|
||||
return self.upload_file(zip_path, timestamp)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Compress config and upload to Drive.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.sync_service.SyncService.delete_backup"><code class="name flex">
|
||||
<span>def <span class="ident">delete_backup</span></span>(<span>self, file_id)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def delete_backup(self, file_id):
|
||||
"""Delete a backup from Drive."""
|
||||
creds = self.get_credentials()
|
||||
if not creds: return False
|
||||
try:
|
||||
service = build("drive", "v3", credentials=creds)
|
||||
service.files().delete(fileId=file_id).execute()
|
||||
return True
|
||||
except Exception as e:
|
||||
printer.error(f"Delete failed: {e}")
|
||||
return False</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Delete a backup from Drive.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.sync_service.SyncService.download_file"><code class="name flex">
|
||||
<span>def <span class="ident">download_file</span></span>(<span>self, file_id, dest)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def download_file(self, file_id, dest):
|
||||
"""Internal method to download from Drive."""
|
||||
creds = self.get_credentials()
|
||||
if not creds: return False
|
||||
try:
|
||||
service = build('drive', 'v3', credentials=creds)
|
||||
request = service.files().get_media(fileId=file_id)
|
||||
with io.FileIO(dest, mode='wb') as fh:
|
||||
downloader = MediaIoBaseDownload(fh, request)
|
||||
done = False
|
||||
while not done:
|
||||
_, done = downloader.next_chunk()
|
||||
return True
|
||||
except Exception as e:
|
||||
printer.error(f"Download failed: {e}")
|
||||
return False</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Internal method to download from Drive.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.sync_service.SyncService.get_credentials"><code class="name flex">
|
||||
<span>def <span class="ident">get_credentials</span></span>(<span>self)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def get_credentials(self):
|
||||
"""Get valid credentials, refreshing if necessary."""
|
||||
if os.path.exists(self.token_file):
|
||||
creds = Credentials.from_authorized_user_file(self.token_file, self.scopes)
|
||||
else:
|
||||
return None
|
||||
|
||||
if not creds or not creds.valid:
|
||||
if creds and creds.expired and creds.refresh_token:
|
||||
try:
|
||||
creds.refresh(Request())
|
||||
except RefreshError:
|
||||
return None
|
||||
else:
|
||||
return None
|
||||
return creds</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Get valid credentials, refreshing if necessary.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.sync_service.SyncService.list_backups"><code class="name flex">
|
||||
<span>def <span class="ident">list_backups</span></span>(<span>self)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def list_backups(self):
|
||||
"""List files in Google Drive appDataFolder."""
|
||||
creds = self.get_credentials()
|
||||
if not creds:
|
||||
printer.error("Not logged in to Google Drive.")
|
||||
return []
|
||||
|
||||
try:
|
||||
service = build("drive", "v3", credentials=creds)
|
||||
response = service.files().list(
|
||||
spaces="appDataFolder",
|
||||
fields="files(id, name, appProperties)",
|
||||
pageSize=10,
|
||||
).execute()
|
||||
|
||||
files_info = []
|
||||
for file in response.get("files", []):
|
||||
files_info.append({
|
||||
"name": file.get("name"),
|
||||
"id": file.get("id"),
|
||||
"date": file.get("appProperties", {}).get("date"),
|
||||
"timestamp": file.get("appProperties", {}).get("timestamp")
|
||||
})
|
||||
return files_info
|
||||
except HttpError as error:
|
||||
printer.error(f"Google Drive API error: {error}")
|
||||
return []</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>List files in Google Drive appDataFolder.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.sync_service.SyncService.login"><code class="name flex">
|
||||
<span>def <span class="ident">login</span></span>(<span>self)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def login(self):
|
||||
"""Authenticate with Google Drive."""
|
||||
creds = None
|
||||
if os.path.exists(self.token_file):
|
||||
creds = Credentials.from_authorized_user_file(self.token_file, self.scopes)
|
||||
|
||||
try:
|
||||
if not creds or not creds.valid:
|
||||
if creds and creds.expired and creds.refresh_token:
|
||||
creds.refresh(Request())
|
||||
else:
|
||||
flow = InstalledAppFlow.from_client_config(self.client_config, self.scopes)
|
||||
creds = flow.run_local_server(port=0, access_type='offline')
|
||||
|
||||
with open(self.token_file, 'w') as token:
|
||||
token.write(creds.to_json())
|
||||
|
||||
printer.success("Logged in successfully.")
|
||||
return True
|
||||
|
||||
except RefreshError:
|
||||
if os.path.exists(self.token_file):
|
||||
os.remove(self.token_file)
|
||||
printer.warning("Existing token was invalid and has been removed. Please log in again.")
|
||||
return False
|
||||
except Exception as e:
|
||||
printer.error(f"Login failed: {e}")
|
||||
return False</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Authenticate with Google Drive.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.sync_service.SyncService.logout"><code class="name flex">
|
||||
<span>def <span class="ident">logout</span></span>(<span>self)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def logout(self):
|
||||
"""Remove Google Drive credentials."""
|
||||
if os.path.exists(self.token_file):
|
||||
os.remove(self.token_file)
|
||||
printer.success("Logged out successfully.")
|
||||
else:
|
||||
printer.info("No credentials file found. Already logged out.")</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Remove Google Drive credentials.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.sync_service.SyncService.perform_restore"><code class="name flex">
|
||||
<span>def <span class="ident">perform_restore</span></span>(<span>self, zip_path, restore_config=True, restore_nodes=True, app_instance=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def perform_restore(self, zip_path, restore_config=True, restore_nodes=True, app_instance=None):
|
||||
"""Execute the actual restoration of files or remote nodes."""
|
||||
try:
|
||||
with zipfile.ZipFile(zip_path, 'r') as zipf:
|
||||
names = zipf.namelist()
|
||||
dest_dir = os.path.dirname(self.config.file)
|
||||
|
||||
# We need to read the config content from zip to decide what to do
|
||||
backup_data = {}
|
||||
config_filename = "config.yaml" if "config.yaml" in names else ("config.json" if "config.json" in names else None)
|
||||
|
||||
if config_filename:
|
||||
with zipf.open(config_filename) as f:
|
||||
backup_data = yaml.safe_load(f)
|
||||
|
||||
# 1. Restore Key (.osk) - Part of config identity
|
||||
if restore_config and ".osk" in names:
|
||||
zipf.extract(".osk", os.path.dirname(self.config.key))
|
||||
|
||||
# 2. Restore Config (Local Settings)
|
||||
if restore_config and backup_data:
|
||||
local_config = self.config.config.copy()
|
||||
|
||||
# Capture current connectivity settings to preserve them
|
||||
current_mode = local_config.get("service_mode", "local")
|
||||
current_remote = local_config.get("remote_host")
|
||||
|
||||
if "config" in backup_data:
|
||||
local_config.update(backup_data["config"])
|
||||
|
||||
# Restore connectivity settings - we don't want a restore to
|
||||
# accidentally switch us between local and remote and break connectivity
|
||||
local_config["service_mode"] = current_mode
|
||||
if current_remote:
|
||||
local_config["remote_host"] = current_remote
|
||||
|
||||
self.config.config = local_config
|
||||
self.config._saveconfig(self.config.file)
|
||||
|
||||
# 3. Restore Nodes and Profiles
|
||||
if restore_nodes and backup_data:
|
||||
connections = backup_data.get("connections", {})
|
||||
profiles = backup_data.get("profiles", {})
|
||||
|
||||
if app_instance and app_instance.services.mode == "remote":
|
||||
# Push to Remote via gRPC
|
||||
app_instance.services.nodes.full_replace(connections, profiles)
|
||||
else:
|
||||
# Restore to Local config file
|
||||
self.config.connections = connections
|
||||
self.config.profiles = profiles
|
||||
self.config._saveconfig(self.config.file)
|
||||
|
||||
# Clear caches
|
||||
for f in [self.config.cachefile, self.config.fzf_cachefile]:
|
||||
if os.path.exists(f): os.remove(f)
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
printer.error(f"Restoration failed: {e}")
|
||||
return False</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Execute the actual restoration of files or remote nodes.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.sync_service.SyncService.perform_sync"><code class="name flex">
|
||||
<span>def <span class="ident">perform_sync</span></span>(<span>self, app_instance)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def perform_sync(self, app_instance):
|
||||
"""Background sync logic."""
|
||||
# Always check current config state
|
||||
sync_enabled = self.config.config.get("sync", False)
|
||||
sync_remote = self.config.config.get("sync_remote", False)
|
||||
|
||||
if not sync_enabled: return
|
||||
|
||||
printer.info("Triggering auto-sync...")
|
||||
if self.check_login_status() != True:
|
||||
printer.warning("Auto-sync: Not logged in to Google Drive.")
|
||||
return
|
||||
|
||||
remote_data = None
|
||||
if sync_remote and app_instance.services.mode == "remote":
|
||||
try:
|
||||
inventory = app_instance.services.nodes.get_inventory()
|
||||
# Merge with local settings
|
||||
local_settings = app_instance.services.config_svc.get_settings()
|
||||
local_settings.pop("configfolder", None)
|
||||
|
||||
# Maintain proper config structure: {config: {}, connections: {}, profiles: {}}
|
||||
remote_data = {
|
||||
"config": local_settings,
|
||||
"connections": inventory.get("connections", {}),
|
||||
"profiles": inventory.get("profiles", {})
|
||||
}
|
||||
except Exception as e:
|
||||
printer.warning(f"Could not fetch remote inventory for sync: {e}")
|
||||
|
||||
# Run in thread to not block CLI
|
||||
threading.Thread(
|
||||
target=self.compress_and_upload,
|
||||
args=(remote_data,)
|
||||
).start()</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Background sync logic.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.sync_service.SyncService.restore_backup"><code class="name flex">
|
||||
<span>def <span class="ident">restore_backup</span></span>(<span>self, file_id=None, restore_config=True, restore_nodes=True, app_instance=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def restore_backup(self, file_id=None, restore_config=True, restore_nodes=True, app_instance=None):
|
||||
"""Download and analyze a backup for restoration."""
|
||||
backups = self.list_backups()
|
||||
if not backups:
|
||||
printer.error("No backups found.")
|
||||
return None
|
||||
|
||||
if file_id:
|
||||
selected = next((f for f in backups if f['id'] == file_id), None)
|
||||
if not selected:
|
||||
printer.error(f"Backup {file_id} not found.")
|
||||
return None
|
||||
else:
|
||||
selected = max(backups, key=lambda x: x['timestamp'] or '0')
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
zip_path = os.path.join(tmp_dir, 'restore.zip')
|
||||
if self.download_file(selected['id'], zip_path):
|
||||
return self.perform_restore(zip_path, restore_config, restore_nodes, app_instance)
|
||||
return False</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Download and analyze a backup for restoration.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.sync_service.SyncService.upload_file"><code class="name flex">
|
||||
<span>def <span class="ident">upload_file</span></span>(<span>self, file_path, timestamp)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def upload_file(self, file_path, timestamp):
|
||||
"""Internal method to upload to Drive."""
|
||||
creds = self.get_credentials()
|
||||
if not creds: return False
|
||||
|
||||
service = build('drive', 'v3', credentials=creds)
|
||||
date_str = datetime.fromtimestamp(timestamp/1000).strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
file_metadata = {
|
||||
'name': os.path.basename(file_path),
|
||||
'parents': ["appDataFolder"],
|
||||
'appProperties': {
|
||||
'timestamp': str(timestamp),
|
||||
'date': date_str
|
||||
}
|
||||
}
|
||||
media = MediaFileUpload(file_path)
|
||||
try:
|
||||
service.files().create(body=file_metadata, media_body=media, fields='id').execute()
|
||||
printer.success("Backup uploaded to Google Drive.")
|
||||
return True
|
||||
except Exception as e:
|
||||
printer.error(f"Upload failed: {e}")
|
||||
return False</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Internal method to upload to Drive.</p></div>
|
||||
</dd>
|
||||
</dl>
|
||||
<h3>Inherited members</h3>
|
||||
<ul class="hlist">
|
||||
<li><code><b><a title="connpy.services.base.BaseService" href="base.html#connpy.services.base.BaseService">BaseService</a></b></code>:
|
||||
<ul class="hlist">
|
||||
<li><code><a title="connpy.services.base.BaseService.set_reserved_names" href="base.html#connpy.services.base.BaseService.set_reserved_names">set_reserved_names</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</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.services" href="index.html">connpy.services</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<h4><code><a title="connpy.services.sync_service.SyncService" href="#connpy.services.sync_service.SyncService">SyncService</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.services.sync_service.SyncService.analyze_backup_content" href="#connpy.services.sync_service.SyncService.analyze_backup_content">analyze_backup_content</a></code></li>
|
||||
<li><code><a title="connpy.services.sync_service.SyncService.check_login_status" href="#connpy.services.sync_service.SyncService.check_login_status">check_login_status</a></code></li>
|
||||
<li><code><a title="connpy.services.sync_service.SyncService.compress_and_upload" href="#connpy.services.sync_service.SyncService.compress_and_upload">compress_and_upload</a></code></li>
|
||||
<li><code><a title="connpy.services.sync_service.SyncService.delete_backup" href="#connpy.services.sync_service.SyncService.delete_backup">delete_backup</a></code></li>
|
||||
<li><code><a title="connpy.services.sync_service.SyncService.download_file" href="#connpy.services.sync_service.SyncService.download_file">download_file</a></code></li>
|
||||
<li><code><a title="connpy.services.sync_service.SyncService.get_credentials" href="#connpy.services.sync_service.SyncService.get_credentials">get_credentials</a></code></li>
|
||||
<li><code><a title="connpy.services.sync_service.SyncService.list_backups" href="#connpy.services.sync_service.SyncService.list_backups">list_backups</a></code></li>
|
||||
<li><code><a title="connpy.services.sync_service.SyncService.login" href="#connpy.services.sync_service.SyncService.login">login</a></code></li>
|
||||
<li><code><a title="connpy.services.sync_service.SyncService.logout" href="#connpy.services.sync_service.SyncService.logout">logout</a></code></li>
|
||||
<li><code><a title="connpy.services.sync_service.SyncService.perform_restore" href="#connpy.services.sync_service.SyncService.perform_restore">perform_restore</a></code></li>
|
||||
<li><code><a title="connpy.services.sync_service.SyncService.perform_sync" href="#connpy.services.sync_service.SyncService.perform_sync">perform_sync</a></code></li>
|
||||
<li><code><a title="connpy.services.sync_service.SyncService.restore_backup" href="#connpy.services.sync_service.SyncService.restore_backup">restore_backup</a></code></li>
|
||||
<li><code><a title="connpy.services.sync_service.SyncService.upload_file" href="#connpy.services.sync_service.SyncService.upload_file">upload_file</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,333 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.services.system_service 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.services.system_service</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
||||
<dl>
|
||||
<dt id="connpy.services.system_service.SystemService"><code class="flex name class">
|
||||
<span>class <span class="ident">SystemService</span></span>
|
||||
<span>(</span><span>config=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class SystemService(BaseService):
|
||||
"""Business logic for application lifecycle (API, processes)."""
|
||||
|
||||
def start_api(self, port=None):
|
||||
"""Start the Connpy REST API."""
|
||||
print(f"DEBUG SystemService: port type={type(port)} value={port}")
|
||||
from connpy.api import start_api
|
||||
try:
|
||||
start_api(port, config=self.config)
|
||||
except Exception as e:
|
||||
raise ConnpyError(f"Failed to start API: {e}")
|
||||
|
||||
def debug_api(self, port=None):
|
||||
"""Start the Connpy REST API in debug mode."""
|
||||
from connpy.api import debug_api
|
||||
try:
|
||||
debug_api(port, config=self.config)
|
||||
except Exception as e:
|
||||
raise ConnpyError(f"Failed to start API in debug mode: {e}")
|
||||
|
||||
|
||||
def stop_api(self):
|
||||
"""Stop the Connpy REST API."""
|
||||
try:
|
||||
import os
|
||||
import signal
|
||||
|
||||
pids = ["/run/connpy.pid", "/tmp/connpy.pid"]
|
||||
stopped = False
|
||||
for pid_file in pids:
|
||||
if os.path.exists(pid_file):
|
||||
try:
|
||||
with open(pid_file, "r") as f:
|
||||
# Read only the first line (PID)
|
||||
line = f.readline().strip()
|
||||
if not line:
|
||||
continue
|
||||
pid = int(line)
|
||||
os.kill(pid, signal.SIGTERM)
|
||||
# Remove the PID file after successful kill
|
||||
os.remove(pid_file)
|
||||
stopped = True
|
||||
except (ValueError, OSError, ProcessLookupError):
|
||||
# If process is already dead, just remove the stale PID file
|
||||
try:
|
||||
os.remove(pid_file)
|
||||
except OSError:
|
||||
pass
|
||||
continue
|
||||
return stopped
|
||||
except Exception as e:
|
||||
raise ConnpyError(f"Failed to stop API: {e}")
|
||||
|
||||
def restart_api(self, port=None):
|
||||
"""Restart the Connpy REST API, maintaining the current port if none provided."""
|
||||
if port is None:
|
||||
status = self.get_api_status()
|
||||
if status["running"] and status.get("port"):
|
||||
port = status["port"]
|
||||
|
||||
self.stop_api()
|
||||
import time
|
||||
time.sleep(1)
|
||||
self.start_api(port)
|
||||
|
||||
def get_api_status(self):
|
||||
"""Check if the API is currently running."""
|
||||
import os
|
||||
pids = ["/run/connpy.pid", "/tmp/connpy.pid"]
|
||||
for pid_file in pids:
|
||||
if os.path.exists(pid_file):
|
||||
try:
|
||||
with open(pid_file, "r") as f:
|
||||
pid_line = f.readline().strip()
|
||||
port_line = f.readline().strip()
|
||||
if not pid_line:
|
||||
continue
|
||||
pid = int(pid_line)
|
||||
port = int(port_line) if port_line else None
|
||||
# Signal 0 checks for process existence without killing it
|
||||
os.kill(pid, 0)
|
||||
return {"running": True, "pid": pid, "port": port, "pid_file": pid_file}
|
||||
except (ValueError, OSError, ProcessLookupError):
|
||||
continue
|
||||
return {"running": False}</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Business logic for application lifecycle (API, processes).</p>
|
||||
<p>Initialize the service.</p>
|
||||
<h2 id="args">Args</h2>
|
||||
<dl>
|
||||
<dt><strong><code>config</code></strong></dt>
|
||||
<dd>An instance of configfile (or None to instantiate a new one/use global context).</dd>
|
||||
</dl></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li><a title="connpy.services.base.BaseService" href="base.html#connpy.services.base.BaseService">BaseService</a></li>
|
||||
</ul>
|
||||
<h3>Methods</h3>
|
||||
<dl>
|
||||
<dt id="connpy.services.system_service.SystemService.debug_api"><code class="name flex">
|
||||
<span>def <span class="ident">debug_api</span></span>(<span>self, port=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def debug_api(self, port=None):
|
||||
"""Start the Connpy REST API in debug mode."""
|
||||
from connpy.api import debug_api
|
||||
try:
|
||||
debug_api(port, config=self.config)
|
||||
except Exception as e:
|
||||
raise ConnpyError(f"Failed to start API in debug mode: {e}")</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Start the Connpy REST API in debug mode.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.system_service.SystemService.get_api_status"><code class="name flex">
|
||||
<span>def <span class="ident">get_api_status</span></span>(<span>self)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def get_api_status(self):
|
||||
"""Check if the API is currently running."""
|
||||
import os
|
||||
pids = ["/run/connpy.pid", "/tmp/connpy.pid"]
|
||||
for pid_file in pids:
|
||||
if os.path.exists(pid_file):
|
||||
try:
|
||||
with open(pid_file, "r") as f:
|
||||
pid_line = f.readline().strip()
|
||||
port_line = f.readline().strip()
|
||||
if not pid_line:
|
||||
continue
|
||||
pid = int(pid_line)
|
||||
port = int(port_line) if port_line else None
|
||||
# Signal 0 checks for process existence without killing it
|
||||
os.kill(pid, 0)
|
||||
return {"running": True, "pid": pid, "port": port, "pid_file": pid_file}
|
||||
except (ValueError, OSError, ProcessLookupError):
|
||||
continue
|
||||
return {"running": False}</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Check if the API is currently running.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.system_service.SystemService.restart_api"><code class="name flex">
|
||||
<span>def <span class="ident">restart_api</span></span>(<span>self, port=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def restart_api(self, port=None):
|
||||
"""Restart the Connpy REST API, maintaining the current port if none provided."""
|
||||
if port is None:
|
||||
status = self.get_api_status()
|
||||
if status["running"] and status.get("port"):
|
||||
port = status["port"]
|
||||
|
||||
self.stop_api()
|
||||
import time
|
||||
time.sleep(1)
|
||||
self.start_api(port)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Restart the Connpy REST API, maintaining the current port if none provided.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.system_service.SystemService.start_api"><code class="name flex">
|
||||
<span>def <span class="ident">start_api</span></span>(<span>self, port=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def start_api(self, port=None):
|
||||
"""Start the Connpy REST API."""
|
||||
print(f"DEBUG SystemService: port type={type(port)} value={port}")
|
||||
from connpy.api import start_api
|
||||
try:
|
||||
start_api(port, config=self.config)
|
||||
except Exception as e:
|
||||
raise ConnpyError(f"Failed to start API: {e}")</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Start the Connpy REST API.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.system_service.SystemService.stop_api"><code class="name flex">
|
||||
<span>def <span class="ident">stop_api</span></span>(<span>self)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def stop_api(self):
|
||||
"""Stop the Connpy REST API."""
|
||||
try:
|
||||
import os
|
||||
import signal
|
||||
|
||||
pids = ["/run/connpy.pid", "/tmp/connpy.pid"]
|
||||
stopped = False
|
||||
for pid_file in pids:
|
||||
if os.path.exists(pid_file):
|
||||
try:
|
||||
with open(pid_file, "r") as f:
|
||||
# Read only the first line (PID)
|
||||
line = f.readline().strip()
|
||||
if not line:
|
||||
continue
|
||||
pid = int(line)
|
||||
os.kill(pid, signal.SIGTERM)
|
||||
# Remove the PID file after successful kill
|
||||
os.remove(pid_file)
|
||||
stopped = True
|
||||
except (ValueError, OSError, ProcessLookupError):
|
||||
# If process is already dead, just remove the stale PID file
|
||||
try:
|
||||
os.remove(pid_file)
|
||||
except OSError:
|
||||
pass
|
||||
continue
|
||||
return stopped
|
||||
except Exception as e:
|
||||
raise ConnpyError(f"Failed to stop API: {e}")</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Stop the Connpy REST API.</p></div>
|
||||
</dd>
|
||||
</dl>
|
||||
<h3>Inherited members</h3>
|
||||
<ul class="hlist">
|
||||
<li><code><b><a title="connpy.services.base.BaseService" href="base.html#connpy.services.base.BaseService">BaseService</a></b></code>:
|
||||
<ul class="hlist">
|
||||
<li><code><a title="connpy.services.base.BaseService.set_reserved_names" href="base.html#connpy.services.base.BaseService.set_reserved_names">set_reserved_names</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</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.services" href="index.html">connpy.services</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<h4><code><a title="connpy.services.system_service.SystemService" href="#connpy.services.system_service.SystemService">SystemService</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.services.system_service.SystemService.debug_api" href="#connpy.services.system_service.SystemService.debug_api">debug_api</a></code></li>
|
||||
<li><code><a title="connpy.services.system_service.SystemService.get_api_status" href="#connpy.services.system_service.SystemService.get_api_status">get_api_status</a></code></li>
|
||||
<li><code><a title="connpy.services.system_service.SystemService.restart_api" href="#connpy.services.system_service.SystemService.restart_api">restart_api</a></code></li>
|
||||
<li><code><a title="connpy.services.system_service.SystemService.start_api" href="#connpy.services.system_service.SystemService.start_api">start_api</a></code></li>
|
||||
<li><code><a title="connpy.services.system_service.SystemService.stop_api" href="#connpy.services.system_service.SystemService.stop_api">stop_api</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -48,10 +48,6 @@ el.replaceWith(d);
|
||||
<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_capture" href="test_capture.html">connpy.tests.test_capture</a></code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>Tests for connpy.core_plugins.capture</p></div>
|
||||
@@ -64,18 +60,26 @@ el.replaceWith(d);
|
||||
<dd>
|
||||
<div class="desc"><p>Tests for connpy.configfile module.</p></div>
|
||||
</dd>
|
||||
<dt><code class="name"><a title="connpy.tests.test_context" href="test_context.html">connpy.tests.test_context</a></code></dt>
|
||||
<dt><code class="name"><a title="connpy.tests.test_connapp" href="test_connapp.html">connpy.tests.test_connapp</a></code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>Tests for connpy.core_plugins.context</p></div>
|
||||
<div class="desc"></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_execution_service" href="test_execution_service.html">connpy.tests.test_execution_service</a></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></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_node_service" href="test_node_service.html">connpy.tests.test_node_service</a></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></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>
|
||||
@@ -84,9 +88,17 @@ el.replaceWith(d);
|
||||
<dd>
|
||||
<div class="desc"><p>Tests for connpy.printer module.</p></div>
|
||||
</dd>
|
||||
<dt><code class="name"><a title="connpy.tests.test_profile_service" href="test_profile_service.html">connpy.tests.test_profile_service</a></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt><code class="name"><a title="connpy.tests.test_provider" href="test_provider.html">connpy.tests.test_provider</a></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt><code class="name"><a title="connpy.tests.test_sync" href="test_sync.html">connpy.tests.test_sync</a></code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>Tests for connpy.core_plugins.sync</p></div>
|
||||
<div class="desc"><p>Tests for connpy.services.sync_service</p></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</section>
|
||||
@@ -111,15 +123,18 @@ el.replaceWith(d);
|
||||
<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_capture" href="test_capture.html">connpy.tests.test_capture</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_context" href="test_context.html">connpy.tests.test_context</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp" href="test_connapp.html">connpy.tests.test_connapp</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_execution_service" href="test_execution_service.html">connpy.tests.test_execution_service</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_node_service" href="test_node_service.html">connpy.tests.test_node_service</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>
|
||||
<li><code><a title="connpy.tests.test_profile_service" href="test_profile_service.html">connpy.tests.test_profile_service</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_provider" href="test_provider.html">connpy.tests.test_provider</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_sync" href="test_sync.html">connpy.tests.test_sync</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
@@ -63,11 +63,13 @@ el.replaceWith(d);
|
||||
assert myai.engineer_model == "test/test-model"
|
||||
assert myai.architect_model == "test/test-architect"
|
||||
|
||||
def test_init_missing_engineer_key(self, config):
|
||||
"""Raises ValueError if engineer key is missing."""
|
||||
def test_ask_missing_engineer_key(self, config):
|
||||
"""Raises ValueError if engineer key is missing when asking."""
|
||||
from connpy.ai import ai
|
||||
with pytest.raises(ValueError, match="Engineer API key"):
|
||||
ai(config)
|
||||
myai = ai(config)
|
||||
with pytest.raises(ValueError) as exc:
|
||||
myai.ask("hello")
|
||||
assert "Engineer API key not configured" in str(exc.value)
|
||||
|
||||
def test_init_missing_architect_key_warns(self, ai_config, capsys, mock_litellm):
|
||||
"""Warns if architect key is missing but doesn't crash."""
|
||||
@@ -104,6 +106,24 @@ el.replaceWith(d);
|
||||
<div class="desc"></div>
|
||||
<h3>Methods</h3>
|
||||
<dl>
|
||||
<dt id="connpy.tests.test_ai.TestAIInit.test_ask_missing_engineer_key"><code class="name flex">
|
||||
<span>def <span class="ident">test_ask_missing_engineer_key</span></span>(<span>self, config)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def test_ask_missing_engineer_key(self, config):
|
||||
"""Raises ValueError if engineer key is missing when asking."""
|
||||
from connpy.ai import ai
|
||||
myai = ai(config)
|
||||
with pytest.raises(ValueError) as exc:
|
||||
myai.ask("hello")
|
||||
assert "Engineer API key not configured" in str(exc.value)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Raises ValueError if engineer key is missing when asking.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_ai.TestAIInit.test_default_models"><code class="name flex">
|
||||
<span>def <span class="ident">test_default_models</span></span>(<span>self, config)</span>
|
||||
</code></dt>
|
||||
@@ -166,22 +186,6 @@ el.replaceWith(d);
|
||||
</details>
|
||||
<div class="desc"><p>Warns if architect key is missing but doesn't crash.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_ai.TestAIInit.test_init_missing_engineer_key"><code class="name flex">
|
||||
<span>def <span class="ident">test_init_missing_engineer_key</span></span>(<span>self, config)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def test_init_missing_engineer_key(self, config):
|
||||
"""Raises ValueError if engineer key is missing."""
|
||||
from connpy.ai import ai
|
||||
with pytest.raises(ValueError, match="Engineer API key"):
|
||||
ai(config)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Raises ValueError if engineer key is missing.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_ai.TestAIInit.test_init_with_keys"><code class="name flex">
|
||||
<span>def <span class="ident">test_init_with_keys</span></span>(<span>self, ai_config, mock_litellm)</span>
|
||||
</code></dt>
|
||||
@@ -1615,10 +1619,10 @@ def myai(self, ai_config, mock_litellm):
|
||||
<li>
|
||||
<h4><code><a title="connpy.tests.test_ai.TestAIInit" href="#connpy.tests.test_ai.TestAIInit">TestAIInit</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.tests.test_ai.TestAIInit.test_ask_missing_engineer_key" href="#connpy.tests.test_ai.TestAIInit.test_ask_missing_engineer_key">test_ask_missing_engineer_key</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_ai.TestAIInit.test_default_models" href="#connpy.tests.test_ai.TestAIInit.test_default_models">test_default_models</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_ai.TestAIInit.test_init_loads_memory" href="#connpy.tests.test_ai.TestAIInit.test_init_loads_memory">test_init_loads_memory</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_ai.TestAIInit.test_init_missing_architect_key_warns" href="#connpy.tests.test_ai.TestAIInit.test_init_missing_architect_key_warns">test_init_missing_architect_key_warns</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_ai.TestAIInit.test_init_missing_engineer_key" href="#connpy.tests.test_ai.TestAIInit.test_init_missing_engineer_key">test_init_missing_engineer_key</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_ai.TestAIInit.test_init_with_keys" href="#connpy.tests.test_ai.TestAIInit.test_init_with_keys">test_init_with_keys</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
@@ -45,6 +45,20 @@ el.replaceWith(d);
|
||||
<section>
|
||||
<h2 class="section-title" id="header-functions">Functions</h2>
|
||||
<dl>
|
||||
<dt id="connpy.tests.test_capture.RemoteCapture"><code class="name flex">
|
||||
<span>def <span class="ident">RemoteCapture</span></span>(<span>)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@pytest.fixture
|
||||
def RemoteCapture():
|
||||
return Entrypoint.get_remote_capture_class()</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_capture.mock_connapp"><code class="name flex">
|
||||
<span>def <span class="ident">mock_connapp</span></span>(<span>)</span>
|
||||
</code></dt>
|
||||
@@ -56,13 +70,14 @@ el.replaceWith(d);
|
||||
<pre><code class="python">@pytest.fixture
|
||||
def mock_connapp():
|
||||
app = MagicMock()
|
||||
app.nodes_list = ["test_node"]
|
||||
app.config.getitem.return_value = {"host": "127.0.0.1", "protocol": "ssh"}
|
||||
app.services.nodes.list_nodes.return_value = ["test_node"]
|
||||
app.services.nodes.get_node_details.return_value = {"host": "127.0.0.1", "protocol": "ssh"}
|
||||
app.services.config_svc.get_settings().get.return_value = "/fake/ws"
|
||||
|
||||
mock_node = MagicMock()
|
||||
mock_node.protocol = "ssh"
|
||||
mock_node.unique = "test_node"
|
||||
app.node.return_value = mock_node
|
||||
app.config.config = {"wireshark_path": "/fake/ws"}
|
||||
return app</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
@@ -81,34 +96,54 @@ def mock_connapp():
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class TestRemoteCapture:
|
||||
def test_init_node_not_found(self, mock_connapp):
|
||||
# Attempt to capture a node not in nodes_list
|
||||
mock_connapp.nodes_list = ["other_node"]
|
||||
def test_init_node_not_found(self, mock_connapp, RemoteCapture):
|
||||
# Attempt to capture a node not in inventory
|
||||
mock_connapp.services.nodes.list_nodes.return_value = []
|
||||
with pytest.raises(SystemExit) as exc:
|
||||
RemoteCapture(mock_connapp, "test_node", "eth0")
|
||||
assert exc.value.code == 2
|
||||
|
||||
def test_init_success(self, mock_connapp):
|
||||
def test_init_success(self, mock_connapp, RemoteCapture):
|
||||
rc = RemoteCapture(mock_connapp, "test_node", "eth0")
|
||||
assert rc.node_name == "test_node"
|
||||
assert rc.interface == "eth0"
|
||||
assert rc.wireshark_path == "/fake/ws"
|
||||
|
||||
@patch("connpy.core_plugins.capture.socket")
|
||||
def test_is_port_in_use(self, mock_socket, mock_connapp):
|
||||
def test_is_port_in_use(self, mock_connapp, RemoteCapture):
|
||||
rc = RemoteCapture(mock_connapp, "test_node", "eth0")
|
||||
mock_sock_instance = MagicMock()
|
||||
mock_socket.socket.return_value.__enter__.return_value = mock_sock_instance
|
||||
|
||||
mock_sock_instance.connect_ex.return_value = 0
|
||||
assert rc._is_port_in_use(8080) is True
|
||||
|
||||
mock_sock_instance.connect_ex.return_value = 1
|
||||
assert rc._is_port_in_use(8080) is False
|
||||
with patch("socket.socket") as mock_socket:
|
||||
mock_sock_instance = MagicMock()
|
||||
mock_socket.return_value.__enter__.return_value = mock_sock_instance
|
||||
|
||||
mock_sock_instance.connect_ex.return_value = 0
|
||||
assert rc._is_port_in_use(8080) is True
|
||||
|
||||
mock_sock_instance.connect_ex.return_value = 1
|
||||
assert rc._is_port_in_use(8080) is False
|
||||
|
||||
@patch.object(RemoteCapture, "_is_port_in_use")
|
||||
def test_find_free_port(self, mock_is_in_use, mock_connapp):
|
||||
def test_find_free_port(self, mock_connapp, RemoteCapture):
|
||||
rc = RemoteCapture(mock_connapp, "test_node", "eth0")
|
||||
with patch.object(RemoteCapture, "_is_port_in_use") as mock_is_in_use:
|
||||
# First 2 ports in use, 3rd is free
|
||||
mock_is_in_use.side_effect = [True, True, False]
|
||||
port = rc._find_free_port(20000, 30000)
|
||||
assert 20000 <= port <= 30000
|
||||
assert mock_is_in_use.call_count == 3</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
<h3>Methods</h3>
|
||||
<dl>
|
||||
<dt id="connpy.tests.test_capture.TestRemoteCapture.test_find_free_port"><code class="name flex">
|
||||
<span>def <span class="ident">test_find_free_port</span></span>(<span>self, mock_connapp, RemoteCapture)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def test_find_free_port(self, mock_connapp, RemoteCapture):
|
||||
rc = RemoteCapture(mock_connapp, "test_node", "eth0")
|
||||
with patch.object(RemoteCapture, "_is_port_in_use") as mock_is_in_use:
|
||||
# First 2 ports in use, 3rd is free
|
||||
mock_is_in_use.side_effect = [True, True, False]
|
||||
port = rc._find_free_port(20000, 30000)
|
||||
@@ -116,38 +151,18 @@ def mock_connapp():
|
||||
assert mock_is_in_use.call_count == 3</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
<h3>Methods</h3>
|
||||
<dl>
|
||||
<dt id="connpy.tests.test_capture.TestRemoteCapture.test_find_free_port"><code class="name flex">
|
||||
<span>def <span class="ident">test_find_free_port</span></span>(<span>self, mock_is_in_use, mock_connapp)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch.object(RemoteCapture, "_is_port_in_use")
|
||||
def test_find_free_port(self, mock_is_in_use, mock_connapp):
|
||||
rc = RemoteCapture(mock_connapp, "test_node", "eth0")
|
||||
# First 2 ports in use, 3rd is free
|
||||
mock_is_in_use.side_effect = [True, True, False]
|
||||
port = rc._find_free_port(20000, 30000)
|
||||
assert 20000 <= port <= 30000
|
||||
assert mock_is_in_use.call_count == 3</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_capture.TestRemoteCapture.test_init_node_not_found"><code class="name flex">
|
||||
<span>def <span class="ident">test_init_node_not_found</span></span>(<span>self, mock_connapp)</span>
|
||||
<span>def <span class="ident">test_init_node_not_found</span></span>(<span>self, mock_connapp, RemoteCapture)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def test_init_node_not_found(self, mock_connapp):
|
||||
# Attempt to capture a node not in nodes_list
|
||||
mock_connapp.nodes_list = ["other_node"]
|
||||
<pre><code class="python">def test_init_node_not_found(self, mock_connapp, RemoteCapture):
|
||||
# Attempt to capture a node not in inventory
|
||||
mock_connapp.services.nodes.list_nodes.return_value = []
|
||||
with pytest.raises(SystemExit) as exc:
|
||||
RemoteCapture(mock_connapp, "test_node", "eth0")
|
||||
assert exc.value.code == 2</code></pre>
|
||||
@@ -155,14 +170,14 @@ def test_find_free_port(self, mock_is_in_use, mock_connapp):
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_capture.TestRemoteCapture.test_init_success"><code class="name flex">
|
||||
<span>def <span class="ident">test_init_success</span></span>(<span>self, mock_connapp)</span>
|
||||
<span>def <span class="ident">test_init_success</span></span>(<span>self, mock_connapp, RemoteCapture)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def test_init_success(self, mock_connapp):
|
||||
<pre><code class="python">def test_init_success(self, mock_connapp, RemoteCapture):
|
||||
rc = RemoteCapture(mock_connapp, "test_node", "eth0")
|
||||
assert rc.node_name == "test_node"
|
||||
assert rc.interface == "eth0"
|
||||
@@ -171,24 +186,24 @@ def test_find_free_port(self, mock_is_in_use, mock_connapp):
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_capture.TestRemoteCapture.test_is_port_in_use"><code class="name flex">
|
||||
<span>def <span class="ident">test_is_port_in_use</span></span>(<span>self, mock_socket, mock_connapp)</span>
|
||||
<span>def <span class="ident">test_is_port_in_use</span></span>(<span>self, mock_connapp, RemoteCapture)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.core_plugins.capture.socket")
|
||||
def test_is_port_in_use(self, mock_socket, mock_connapp):
|
||||
<pre><code class="python">def test_is_port_in_use(self, mock_connapp, RemoteCapture):
|
||||
rc = RemoteCapture(mock_connapp, "test_node", "eth0")
|
||||
mock_sock_instance = MagicMock()
|
||||
mock_socket.socket.return_value.__enter__.return_value = mock_sock_instance
|
||||
|
||||
mock_sock_instance.connect_ex.return_value = 0
|
||||
assert rc._is_port_in_use(8080) is True
|
||||
|
||||
mock_sock_instance.connect_ex.return_value = 1
|
||||
assert rc._is_port_in_use(8080) is False</code></pre>
|
||||
with patch("socket.socket") as mock_socket:
|
||||
mock_sock_instance = MagicMock()
|
||||
mock_socket.return_value.__enter__.return_value = mock_sock_instance
|
||||
|
||||
mock_sock_instance.connect_ex.return_value = 0
|
||||
assert rc._is_port_in_use(8080) is True
|
||||
|
||||
mock_sock_instance.connect_ex.return_value = 1
|
||||
assert rc._is_port_in_use(8080) is False</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
@@ -209,6 +224,7 @@ def test_is_port_in_use(self, mock_socket, mock_connapp):
|
||||
</li>
|
||||
<li><h3><a href="#header-functions">Functions</a></h3>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.tests.test_capture.RemoteCapture" href="#connpy.tests.test_capture.RemoteCapture">RemoteCapture</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_capture.mock_connapp" href="#connpy.tests.test_capture.mock_connapp">mock_connapp</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
@@ -64,7 +64,7 @@ el.replaceWith(d);
|
||||
subdir = tmp_path / "subdir"
|
||||
subdir.mkdir()
|
||||
|
||||
result = _getcwd(["run", "run"], "run")
|
||||
result = get_cwd(["run", "run"])
|
||||
# Should list files
|
||||
assert any("file1.txt" in r for r in result)
|
||||
assert any("subdir/" in r for r in result)
|
||||
@@ -75,7 +75,7 @@ el.replaceWith(d);
|
||||
(tmp_path / "script.yaml").touch()
|
||||
(tmp_path / "script2.yaml").touch()
|
||||
|
||||
result = _getcwd(["run", "script"], "run")
|
||||
result = get_cwd(["run", "script"])
|
||||
assert any("script" in r for r in result)
|
||||
|
||||
def test_folder_only(self, tmp_path, monkeypatch):
|
||||
@@ -85,7 +85,7 @@ el.replaceWith(d);
|
||||
subdir = tmp_path / "mydir"
|
||||
subdir.mkdir()
|
||||
|
||||
result = _getcwd(["export", "export"], "export", folderonly=True)
|
||||
result = get_cwd(["export", "export"], folderonly=True)
|
||||
files_in_result = [r for r in result if "file.txt" in r]
|
||||
assert len(files_in_result) == 0
|
||||
dirs_in_result = [r for r in result if "mydir" in r]
|
||||
@@ -110,7 +110,7 @@ el.replaceWith(d);
|
||||
subdir = tmp_path / "subdir"
|
||||
subdir.mkdir()
|
||||
|
||||
result = _getcwd(["run", "run"], "run")
|
||||
result = get_cwd(["run", "run"])
|
||||
# Should list files
|
||||
assert any("file1.txt" in r for r in result)
|
||||
assert any("subdir/" in r for r in result)</code></pre>
|
||||
@@ -132,7 +132,7 @@ el.replaceWith(d);
|
||||
subdir = tmp_path / "mydir"
|
||||
subdir.mkdir()
|
||||
|
||||
result = _getcwd(["export", "export"], "export", folderonly=True)
|
||||
result = get_cwd(["export", "export"], folderonly=True)
|
||||
files_in_result = [r for r in result if "file.txt" in r]
|
||||
assert len(files_in_result) == 0
|
||||
dirs_in_result = [r for r in result if "mydir" in r]
|
||||
@@ -154,179 +154,13 @@ el.replaceWith(d);
|
||||
(tmp_path / "script.yaml").touch()
|
||||
(tmp_path / "script2.yaml").touch()
|
||||
|
||||
result = _getcwd(["run", "script"], "run")
|
||||
result = get_cwd(["run", "script"])
|
||||
assert any("script" 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):
|
||||
"""--disable returns enabled plugins."""
|
||||
plugin_dir = tmp_path / "plugins"
|
||||
plugin_dir.mkdir()
|
||||
(plugin_dir / "active.py").touch()
|
||||
(plugin_dir / "disabled.py.bkp").touch()
|
||||
|
||||
result = _get_plugins("--disable", str(tmp_path))
|
||||
assert "active" in result
|
||||
assert "disabled" not in result
|
||||
|
||||
def test_get_plugins_enable(self, tmp_path):
|
||||
"""--enable returns disabled plugins."""
|
||||
plugin_dir = tmp_path / "plugins"
|
||||
plugin_dir.mkdir()
|
||||
(plugin_dir / "active.py").touch()
|
||||
(plugin_dir / "disabled.py.bkp").touch()
|
||||
|
||||
result = _get_plugins("--enable", str(tmp_path))
|
||||
assert "disabled" in result
|
||||
assert "active" not in result
|
||||
|
||||
def test_get_plugins_del(self, tmp_path):
|
||||
"""--del returns all plugins."""
|
||||
plugin_dir = tmp_path / "plugins"
|
||||
plugin_dir.mkdir()
|
||||
(plugin_dir / "active.py").touch()
|
||||
(plugin_dir / "disabled.py.bkp").touch()
|
||||
|
||||
result = _get_plugins("--del", str(tmp_path))
|
||||
assert "active" in result
|
||||
assert "disabled" in result
|
||||
|
||||
def test_get_plugins_all(self, tmp_path):
|
||||
"""'all' returns dict with paths."""
|
||||
plugin_dir = tmp_path / "plugins"
|
||||
plugin_dir.mkdir()
|
||||
(plugin_dir / "myplugin.py").touch()
|
||||
|
||||
result = _get_plugins("all", str(tmp_path))
|
||||
assert isinstance(result, dict)
|
||||
assert "myplugin" in result
|
||||
|
||||
def test_get_plugins_empty_dir(self, tmp_path):
|
||||
"""Empty plugins directory returns empty list."""
|
||||
plugin_dir = tmp_path / "plugins"
|
||||
plugin_dir.mkdir()
|
||||
|
||||
result = _get_plugins("--disable", 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):
|
||||
"""'all' returns dict with paths."""
|
||||
plugin_dir = tmp_path / "plugins"
|
||||
plugin_dir.mkdir()
|
||||
(plugin_dir / "myplugin.py").touch()
|
||||
|
||||
result = _get_plugins("all", str(tmp_path))
|
||||
assert isinstance(result, dict)
|
||||
assert "myplugin" 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):
|
||||
"""--del returns all plugins."""
|
||||
plugin_dir = tmp_path / "plugins"
|
||||
plugin_dir.mkdir()
|
||||
(plugin_dir / "active.py").touch()
|
||||
(plugin_dir / "disabled.py.bkp").touch()
|
||||
|
||||
result = _get_plugins("--del", str(tmp_path))
|
||||
assert "active" in result
|
||||
assert "disabled" in result</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>–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):
|
||||
"""--disable returns enabled plugins."""
|
||||
plugin_dir = tmp_path / "plugins"
|
||||
plugin_dir.mkdir()
|
||||
(plugin_dir / "active.py").touch()
|
||||
(plugin_dir / "disabled.py.bkp").touch()
|
||||
|
||||
result = _get_plugins("--disable", str(tmp_path))
|
||||
assert "active" in result
|
||||
assert "disabled" not in result</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>–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):
|
||||
"""Empty plugins directory returns empty list."""
|
||||
plugin_dir = tmp_path / "plugins"
|
||||
plugin_dir.mkdir()
|
||||
|
||||
result = _get_plugins("--disable", 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):
|
||||
"""--enable returns disabled plugins."""
|
||||
plugin_dir = tmp_path / "plugins"
|
||||
plugin_dir.mkdir()
|
||||
(plugin_dir / "active.py").touch()
|
||||
(plugin_dir / "disabled.py.bkp").touch()
|
||||
|
||||
result = _get_plugins("--enable", str(tmp_path))
|
||||
assert "disabled" in result
|
||||
assert "active" not in result</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>–enable returns disabled plugins.</p></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_completion.TestLoadTxtCache"><code class="flex name class">
|
||||
<span>class <span class="ident">TestLoadTxtCache</span></span>
|
||||
</code></dt>
|
||||
@@ -411,16 +245,6 @@ el.replaceWith(d);
|
||||
</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>
|
||||
<li>
|
||||
<h4><code><a title="connpy.tests.test_completion.TestLoadTxtCache" href="#connpy.tests.test_completion.TestLoadTxtCache">TestLoadTxtCache</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.tests.test_completion.TestLoadTxtCache.test_load_existing_cache" href="#connpy.tests.test_completion.TestLoadTxtCache.test_load_existing_cache">test_load_existing_cache</a></code></li>
|
||||
|
||||
@@ -1140,8 +1140,9 @@ el.replaceWith(d);
|
||||
assert "server1@office" not in nodes
|
||||
|
||||
def test_getallnodes_filter_invalid_type(self, populated_config):
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(SystemExit) as exc:
|
||||
populated_config._getallnodes(123)
|
||||
assert exc.value.code == 1
|
||||
|
||||
def test_getallfolders(self, populated_config):
|
||||
folders = populated_config._getallfolders()
|
||||
@@ -1236,8 +1237,9 @@ el.replaceWith(d);
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def test_getallnodes_filter_invalid_type(self, populated_config):
|
||||
with pytest.raises(ValueError):
|
||||
populated_config._getallnodes(123)</code></pre>
|
||||
with pytest.raises(SystemExit) as exc:
|
||||
populated_config._getallnodes(123)
|
||||
assert exc.value.code == 1</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
|
||||
@@ -0,0 +1,705 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.tests.test_connapp 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.test_connapp</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-functions">Functions</h2>
|
||||
<dl>
|
||||
<dt id="connpy.tests.test_connapp.app"><code class="name flex">
|
||||
<span>def <span class="ident">app</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 app(populated_config):
|
||||
"""Returns an instance of connapp initialized with the mock config."""
|
||||
return connapp(populated_config)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Returns an instance of connapp initialized with the mock config.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_connapp.test_ai"><code class="name flex">
|
||||
<span>def <span class="ident">test_ai</span></span>(<span>mock_status, mock_ask, app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.services.ai_service.AIService.ask")
|
||||
@patch("connpy.connapp.console.status")
|
||||
def test_ai(mock_status, mock_ask, app):
|
||||
mock_ask.return_value = {"response": "AI output", "usage": {"total": 10, "input": 5, "output": 5}}
|
||||
|
||||
app.start(["ai", "--engineer-api-key", "testkey", "how are you"])
|
||||
mock_ask.assert_called_once()</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_connapp.test_ai_list"><code class="name flex">
|
||||
<span>def <span class="ident">test_ai_list</span></span>(<span>mock_list_sessions, app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.services.ai_service.AIService.list_sessions")
|
||||
def test_ai_list(mock_list_sessions, app):
|
||||
mock_list_sessions.return_value = [{"id": "1", "title": "t", "created_at": "now", "model": "m"}]
|
||||
app.start(["ai", "--list"])
|
||||
mock_list_sessions.assert_called_once()</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_connapp.test_api_debug"><code class="name flex">
|
||||
<span>def <span class="ident">test_api_debug</span></span>(<span>mock_status, app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.services.system_service.SystemService.get_api_status")
|
||||
def test_api_debug(mock_status, app):
|
||||
mock_status.return_value = {"running": False}
|
||||
app.services.system.debug_api = MagicMock()
|
||||
app.start(["api", "-d", "8080"])
|
||||
app.services.system.debug_api.assert_called_once_with(port=8080)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_connapp.test_api_start"><code class="name flex">
|
||||
<span>def <span class="ident">test_api_start</span></span>(<span>mock_status, app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.services.system_service.SystemService.get_api_status")
|
||||
def test_api_start(mock_status, app):
|
||||
mock_status.return_value = {"running": False}
|
||||
app.services.system.start_api = MagicMock()
|
||||
app.start(["api", "-s", "8080"])
|
||||
app.services.system.start_api.assert_called_once_with(port=8080)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_connapp.test_api_stop"><code class="name flex">
|
||||
<span>def <span class="ident">test_api_stop</span></span>(<span>mock_status, app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.services.system_service.SystemService.get_api_status")
|
||||
def test_api_stop(mock_status, app):
|
||||
mock_status.return_value = {"running": True, "pid": "1234"}
|
||||
app.services.system.stop_api = MagicMock(return_value=True)
|
||||
app.start(["api", "-x"])
|
||||
app.services.system.stop_api.assert_called_once()</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_connapp.test_bulk"><code class="name flex">
|
||||
<span>def <span class="ident">test_bulk</span></span>(<span>mock_bulk_add, mock_q_bulk, app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.cli.forms.Forms.questions_bulk")
|
||||
@patch("connpy.services.node_service.NodeService.bulk_add")
|
||||
def test_bulk(mock_bulk_add, mock_q_bulk, app):
|
||||
mock_q_bulk.return_value = {"ids": "node1", "host": "host1", "location": ""}
|
||||
mock_bulk_add.return_value = 1
|
||||
app.start(["bulk"])
|
||||
mock_bulk_add.assert_called_once()</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_connapp.test_config"><code class="name flex">
|
||||
<span>def <span class="ident">test_config</span></span>(<span>mock_update_setting, app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.services.config_service.ConfigService.update_setting")
|
||||
def test_config(mock_update_setting, app):
|
||||
app.start(["config", "--allow-uppercase", "true"])
|
||||
mock_update_setting.assert_called_with("case", True)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_connapp.test_config_folder"><code class="name flex">
|
||||
<span>def <span class="ident">test_config_folder</span></span>(<span>mock_set_config_folder, app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.services.config_service.ConfigService.set_config_folder")
|
||||
def test_config_folder(mock_set_config_folder, app):
|
||||
app.start(["config", "--configfolder", "/new/path"])
|
||||
mock_set_config_folder.assert_called_once_with("/new/path")</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_connapp.test_config_various"><code class="name flex">
|
||||
<span>def <span class="ident">test_config_various</span></span>(<span>mock_update_setting, app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.services.config_service.ConfigService.update_setting")
|
||||
def test_config_various(mock_update_setting, app):
|
||||
app.start(["config", "--fzf", "true"])
|
||||
mock_update_setting.assert_called_with("fzf", True)
|
||||
app.start(["config", "--keepalive", "60"])
|
||||
mock_update_setting.assert_called_with("idletime", 60)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_connapp.test_connapp_init"><code class="name flex">
|
||||
<span>def <span class="ident">test_connapp_init</span></span>(<span>app, populated_config)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def test_connapp_init(app, populated_config):
|
||||
"""Test that connapp initializes correctly with config."""
|
||||
assert app.config == populated_config
|
||||
assert app.case == populated_config.config.get("case", False)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Test that connapp initializes correctly with config.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_connapp.test_copy"><code class="name flex">
|
||||
<span>def <span class="ident">test_copy</span></span>(<span>mock_move_node, app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.services.node_service.NodeService.move_node")
|
||||
def test_copy(mock_move_node, app):
|
||||
app.start(["copy", "src_node", "dst_node"])
|
||||
mock_move_node.assert_called_once_with("src_node", "dst_node", copy=True)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_connapp.test_export"><code class="name flex">
|
||||
<span>def <span class="ident">test_export</span></span>(<span>mock_export, app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.services.import_export_service.ImportExportService.export_to_file")
|
||||
def test_export(mock_export, app):
|
||||
with pytest.raises(SystemExit):
|
||||
app.start(["export", "file.yml", "@folder1"])
|
||||
mock_export.assert_called_once_with("file.yml", folders=["@folder1"])</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_connapp.test_import"><code class="name flex">
|
||||
<span>def <span class="ident">test_import</span></span>(<span>mock_import, mock_prompt, mock_exists, app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("os.path.exists")
|
||||
@patch("inquirer.prompt")
|
||||
@patch("connpy.services.import_export_service.ImportExportService.import_from_file")
|
||||
def test_import(mock_import, mock_prompt, mock_exists, app):
|
||||
mock_exists.return_value = True
|
||||
mock_prompt.return_value = {"import": True}
|
||||
app.start(["import", "file.yml"])
|
||||
mock_import.assert_called_once_with("file.yml")</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_connapp.test_list_folders"><code class="name flex">
|
||||
<span>def <span class="ident">test_list_folders</span></span>(<span>mock_list_folders, app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.services.node_service.NodeService.list_folders")
|
||||
def test_list_folders(mock_list_folders, app):
|
||||
mock_list_folders.return_value = ["folder1"]
|
||||
app.start(["list", "folders"])
|
||||
# Called during init and during the list command
|
||||
assert mock_list_folders.call_count >= 2</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_connapp.test_move"><code class="name flex">
|
||||
<span>def <span class="ident">test_move</span></span>(<span>mock_move_node, app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.services.node_service.NodeService.move_node")
|
||||
def test_move(mock_move_node, app):
|
||||
app.start(["move", "src_node", "dst_node"])
|
||||
mock_move_node.assert_called_once_with("src_node", "dst_node", copy=False)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_connapp.test_node_add"><code class="name flex">
|
||||
<span>def <span class="ident">test_node_add</span></span>(<span>mock_func_node, app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.cli.node_handler.NodeHandler.dispatch")
|
||||
def test_node_add(mock_func_node, app):
|
||||
"""Test that 'node -a' command correctly parses."""
|
||||
app.start(["node", "-a", "new_router"])
|
||||
mock_func_node.assert_called_once()
|
||||
args = mock_func_node.call_args[0][0]
|
||||
assert args.data == "new_router"
|
||||
assert args.action == "add"</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Test that 'node -a' command correctly parses.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_connapp.test_node_default"><code class="name flex">
|
||||
<span>def <span class="ident">test_node_default</span></span>(<span>mock_func_node, app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.cli.node_handler.NodeHandler.dispatch")
|
||||
def test_node_default(mock_func_node, app):
|
||||
"""Test that default 'node' command correctly parses and calls _func_node."""
|
||||
app.start(["node", "router1"])
|
||||
mock_func_node.assert_called_once()
|
||||
args = mock_func_node.call_args[0][0]
|
||||
assert args.data == "router1"
|
||||
assert args.action == "connect"</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Test that default 'node' command correctly parses and calls _func_node.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_connapp.test_node_del"><code class="name flex">
|
||||
<span>def <span class="ident">test_node_del</span></span>(<span>mock_prompt, mock_delete_node, mock_list_nodes, app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.services.node_service.NodeService.list_nodes")
|
||||
@patch("connpy.services.node_service.NodeService.delete_node")
|
||||
@patch("inquirer.prompt")
|
||||
def test_node_del(mock_prompt, mock_delete_node, mock_list_nodes, app):
|
||||
mock_list_nodes.return_value = ["router1"]
|
||||
mock_prompt.return_value = {"delete": True}
|
||||
app.start(["node", "-r", "router1"])
|
||||
mock_delete_node.assert_called_once_with("router1", is_folder=False)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_connapp.test_node_list"><code class="name flex">
|
||||
<span>def <span class="ident">test_node_list</span></span>(<span>mock_list_nodes, app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.services.node_service.NodeService.list_nodes")
|
||||
def test_node_list(mock_list_nodes, app):
|
||||
"""Test 'list nodes' invokes node service."""
|
||||
mock_list_nodes.return_value = ["router1", "server1"]
|
||||
app.start(["list", "nodes"])
|
||||
# Should be called during init and during the list command
|
||||
assert mock_list_nodes.call_count >= 2</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Test 'list nodes' invokes node service.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_connapp.test_node_mod"><code class="name flex">
|
||||
<span>def <span class="ident">test_node_mod</span></span>(<span>mock_q_nodes, mock_q_edit, mock_update_node, mock_get_details, mock_list_nodes, app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.services.node_service.NodeService.list_nodes")
|
||||
@patch("connpy.services.node_service.NodeService.get_node_details")
|
||||
@patch("connpy.services.node_service.NodeService.update_node")
|
||||
@patch("connpy.cli.forms.Forms.questions_edit")
|
||||
@patch("connpy.cli.forms.Forms.questions_nodes")
|
||||
def test_node_mod(mock_q_nodes, mock_q_edit, mock_update_node, mock_get_details, mock_list_nodes, app):
|
||||
mock_list_nodes.return_value = ["router1"]
|
||||
mock_get_details.return_value = {"host": "1.1.1.1", "port": 22}
|
||||
mock_q_edit.return_value = {"host": True}
|
||||
mock_q_nodes.return_value = {"host": "2.2.2.2", "port": 22}
|
||||
|
||||
app.start(["node", "-e", "router1"])
|
||||
mock_update_node.assert_called_once()</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_connapp.test_node_show"><code class="name flex">
|
||||
<span>def <span class="ident">test_node_show</span></span>(<span>mock_data, app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.printer.data")
|
||||
def test_node_show(mock_data, app):
|
||||
app.nodes_list = ["router1"]
|
||||
app.config.getitem = MagicMock(return_value={"host": "1.1.1.1"})
|
||||
app.start(["node", "-s", "router1"])
|
||||
mock_data.assert_called()</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_connapp.test_plugin_add"><code class="name flex">
|
||||
<span>def <span class="ident">test_plugin_add</span></span>(<span>mock_verify, mock_copy, mock_exists, app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("os.path.exists")
|
||||
@patch("shutil.copy2")
|
||||
@patch("connpy.plugins.Plugins.verify_script")
|
||||
def test_plugin_add(mock_verify, mock_copy, mock_exists, app):
|
||||
def mock_exists_side_effect(path):
|
||||
if "testplug.py" in path: return False
|
||||
if "testplug.py.bkp" in path: return False
|
||||
if "file.py" in path: return True
|
||||
return True
|
||||
mock_exists.side_effect = mock_exists_side_effect
|
||||
mock_verify.return_value = None
|
||||
app.commands = []
|
||||
app.start(["plugin", "--add", "testplug", "file.py"])
|
||||
mock_copy.assert_called()</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_connapp.test_plugin_delete"><code class="name flex">
|
||||
<span>def <span class="ident">test_plugin_delete</span></span>(<span>mock_delete, app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.services.plugin_service.PluginService.delete_plugin")
|
||||
def test_plugin_delete(mock_delete, app):
|
||||
app.start(["plugin", "--del", "testplug"])
|
||||
mock_delete.assert_called_once_with("testplug")</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_connapp.test_plugin_disable"><code class="name flex">
|
||||
<span>def <span class="ident">test_plugin_disable</span></span>(<span>mock_disable, app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.services.plugin_service.PluginService.disable_plugin")
|
||||
def test_plugin_disable(mock_disable, app):
|
||||
app.start(["plugin", "--disable", "testplug"])
|
||||
mock_disable.assert_called_once_with("testplug")</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_connapp.test_plugin_enable"><code class="name flex">
|
||||
<span>def <span class="ident">test_plugin_enable</span></span>(<span>mock_enable, app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.services.plugin_service.PluginService.enable_plugin")
|
||||
def test_plugin_enable(mock_enable, app):
|
||||
app.start(["plugin", "--enable", "testplug"])
|
||||
mock_enable.assert_called_once_with("testplug")</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_connapp.test_plugin_list"><code class="name flex">
|
||||
<span>def <span class="ident">test_plugin_list</span></span>(<span>mock_list_plugins, app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.services.plugin_service.PluginService.list_plugins")
|
||||
def test_plugin_list(mock_list_plugins, app):
|
||||
mock_list_plugins.return_value = {"testplug": {"enabled": True}}
|
||||
app.start(["plugin", "--list"])
|
||||
mock_list_plugins.assert_called_once()</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_connapp.test_profile_add"><code class="name flex">
|
||||
<span>def <span class="ident">test_profile_add</span></span>(<span>mock_q_profiles, mock_add_profile, mock_list_profiles, app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.services.profile_service.ProfileService.list_profiles")
|
||||
@patch("connpy.services.profile_service.ProfileService.add_profile")
|
||||
@patch("connpy.cli.forms.Forms.questions_profiles")
|
||||
def test_profile_add(mock_q_profiles, mock_add_profile, mock_list_profiles, app):
|
||||
mock_list_profiles.return_value = ["default"]
|
||||
mock_q_profiles.return_value = {"host": "test"}
|
||||
app.start(["profile", "-a", "new_profile"])
|
||||
mock_add_profile.assert_called_once_with("new_profile", {"host": "test"})</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_connapp.test_profile_del"><code class="name flex">
|
||||
<span>def <span class="ident">test_profile_del</span></span>(<span>mock_prompt, mock_delete_profile, mock_get_profile, app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.services.profile_service.ProfileService.get_profile")
|
||||
@patch("connpy.services.profile_service.ProfileService.delete_profile")
|
||||
@patch("inquirer.prompt")
|
||||
def test_profile_del(mock_prompt, mock_delete_profile, mock_get_profile, app):
|
||||
mock_get_profile.return_value = {"host": "test"}
|
||||
mock_prompt.return_value = {"delete": True}
|
||||
app.start(["profile", "-r", "test_profile"])
|
||||
mock_delete_profile.assert_called_once_with("test_profile")</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_connapp.test_profile_list"><code class="name flex">
|
||||
<span>def <span class="ident">test_profile_list</span></span>(<span>mock_print, mock_list_profiles, app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.services.profile_service.ProfileService.list_profiles")
|
||||
@patch("connpy.connapp.printer.console.print")
|
||||
def test_profile_list(mock_print, mock_list_profiles, app):
|
||||
"""Test 'profile list' invokes profile service correctly."""
|
||||
mock_list_profiles.return_value = ["default", "office-user"]
|
||||
app.start(["list", "profiles"])
|
||||
assert mock_list_profiles.call_count >= 2</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Test 'profile list' invokes profile service correctly.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_connapp.test_profile_mod"><code class="name flex">
|
||||
<span>def <span class="ident">test_profile_mod</span></span>(<span>mock_q_profiles, mock_q_edit, mock_update_profile, mock_get_profile, app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.services.profile_service.ProfileService.get_profile")
|
||||
@patch("connpy.services.profile_service.ProfileService.update_profile")
|
||||
@patch("connpy.cli.forms.Forms.questions_edit")
|
||||
@patch("connpy.cli.forms.Forms.questions_profiles")
|
||||
def test_profile_mod(mock_q_profiles, mock_q_edit, mock_update_profile, mock_get_profile, app):
|
||||
mock_get_profile.return_value = {"host": "test", "port": 22}
|
||||
mock_q_edit.return_value = {"host": True}
|
||||
mock_q_profiles.return_value = {"id": "test_profile", "host": "new_host", "port": 22}
|
||||
app.start(["profile", "-e", "test_profile"])
|
||||
mock_update_profile.assert_called_once_with("test_profile", {"id": "test_profile", "host": "new_host", "port": 22})</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_connapp.test_profile_show"><code class="name flex">
|
||||
<span>def <span class="ident">test_profile_show</span></span>(<span>mock_data, mock_get_profile, app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.services.profile_service.ProfileService.get_profile")
|
||||
@patch("connpy.printer.data")
|
||||
def test_profile_show(mock_data, mock_get_profile, app):
|
||||
mock_get_profile.return_value = {"host": "test"}
|
||||
app.start(["profile", "-s", "test_profile"])
|
||||
mock_data.assert_called()</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_connapp.test_run"><code class="name flex">
|
||||
<span>def <span class="ident">test_run</span></span>(<span>mock_run_commands, app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.services.execution_service.ExecutionService.run_commands")
|
||||
def test_run(mock_run_commands, app):
|
||||
app.start(["run", "node1", "command1", "command2"])
|
||||
mock_run_commands.assert_called_once()
|
||||
assert mock_run_commands.call_args[1]["nodes_filter"] == "node1"
|
||||
assert mock_run_commands.call_args[1]["commands"] == ["command1 command2"]</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_connapp.test_type_node_reserved_word"><code class="name flex">
|
||||
<span>def <span class="ident">test_type_node_reserved_word</span></span>(<span>app)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def test_type_node_reserved_word(app):
|
||||
app.commands = ["bulk", "ai", "run"]
|
||||
with patch("sys.argv", ["connpy", "node", "-a", "bulk"]):
|
||||
with pytest.raises(SystemExit) as exc:
|
||||
app._type_node("bulk")
|
||||
assert exc.value.code == 2
|
||||
|
||||
# In move/copy it also raises because destination cannot be reserved
|
||||
with patch("sys.argv", ["connpy", "mv", "test1", "bulk"]):
|
||||
with pytest.raises(SystemExit) as exc:
|
||||
app._type_node("bulk")
|
||||
assert exc.value.code == 2</code></pre>
|
||||
</details>
|
||||
<div class="desc"></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="">
|
||||
<li><code><a title="connpy.tests.test_connapp.app" href="#connpy.tests.test_connapp.app">app</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp.test_ai" href="#connpy.tests.test_connapp.test_ai">test_ai</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp.test_ai_list" href="#connpy.tests.test_connapp.test_ai_list">test_ai_list</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp.test_api_debug" href="#connpy.tests.test_connapp.test_api_debug">test_api_debug</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp.test_api_start" href="#connpy.tests.test_connapp.test_api_start">test_api_start</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp.test_api_stop" href="#connpy.tests.test_connapp.test_api_stop">test_api_stop</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp.test_bulk" href="#connpy.tests.test_connapp.test_bulk">test_bulk</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp.test_config" href="#connpy.tests.test_connapp.test_config">test_config</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp.test_config_folder" href="#connpy.tests.test_connapp.test_config_folder">test_config_folder</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp.test_config_various" href="#connpy.tests.test_connapp.test_config_various">test_config_various</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp.test_connapp_init" href="#connpy.tests.test_connapp.test_connapp_init">test_connapp_init</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp.test_copy" href="#connpy.tests.test_connapp.test_copy">test_copy</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp.test_export" href="#connpy.tests.test_connapp.test_export">test_export</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp.test_import" href="#connpy.tests.test_connapp.test_import">test_import</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp.test_list_folders" href="#connpy.tests.test_connapp.test_list_folders">test_list_folders</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp.test_move" href="#connpy.tests.test_connapp.test_move">test_move</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp.test_node_add" href="#connpy.tests.test_connapp.test_node_add">test_node_add</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp.test_node_default" href="#connpy.tests.test_connapp.test_node_default">test_node_default</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp.test_node_del" href="#connpy.tests.test_connapp.test_node_del">test_node_del</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp.test_node_list" href="#connpy.tests.test_connapp.test_node_list">test_node_list</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp.test_node_mod" href="#connpy.tests.test_connapp.test_node_mod">test_node_mod</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp.test_node_show" href="#connpy.tests.test_connapp.test_node_show">test_node_show</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp.test_plugin_add" href="#connpy.tests.test_connapp.test_plugin_add">test_plugin_add</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp.test_plugin_delete" href="#connpy.tests.test_connapp.test_plugin_delete">test_plugin_delete</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp.test_plugin_disable" href="#connpy.tests.test_connapp.test_plugin_disable">test_plugin_disable</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp.test_plugin_enable" href="#connpy.tests.test_connapp.test_plugin_enable">test_plugin_enable</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp.test_plugin_list" href="#connpy.tests.test_connapp.test_plugin_list">test_plugin_list</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp.test_profile_add" href="#connpy.tests.test_connapp.test_profile_add">test_profile_add</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp.test_profile_del" href="#connpy.tests.test_connapp.test_profile_del">test_profile_del</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp.test_profile_list" href="#connpy.tests.test_connapp.test_profile_list">test_profile_list</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp.test_profile_mod" href="#connpy.tests.test_connapp.test_profile_mod">test_profile_mod</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp.test_profile_show" href="#connpy.tests.test_connapp.test_profile_show">test_profile_show</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp.test_run" href="#connpy.tests.test_connapp.test_run">test_run</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_connapp.test_type_node_reserved_word" href="#connpy.tests.test_connapp.test_type_node_reserved_word">test_type_node_reserved_word</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.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -115,8 +115,9 @@ el.replaceWith(d);
|
||||
|
||||
def test_invalid_protocol_raises(self):
|
||||
n = self._make_node(protocol="invalid_proto")
|
||||
with pytest.raises(ValueError, match="Invalid protocol"):
|
||||
with pytest.raises(SystemExit) as exc:
|
||||
n._get_cmd()
|
||||
assert exc.value.code == 1
|
||||
|
||||
def test_ssh_cmd_no_user(self):
|
||||
n = self._make_node(user="")
|
||||
@@ -155,8 +156,9 @@ el.replaceWith(d);
|
||||
</summary>
|
||||
<pre><code class="python">def test_invalid_protocol_raises(self):
|
||||
n = self._make_node(protocol="invalid_proto")
|
||||
with pytest.raises(ValueError, match="Invalid protocol"):
|
||||
n._get_cmd()</code></pre>
|
||||
with pytest.raises(SystemExit) as exc:
|
||||
n._get_cmd()
|
||||
assert exc.value.code == 1</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.tests.test_execution_service 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.test_execution_service</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-functions">Functions</h2>
|
||||
<dl>
|
||||
<dt id="connpy.tests.test_execution_service.test_run_commands_callback"><code class="name flex">
|
||||
<span>def <span class="ident">test_run_commands_callback</span></span>(<span>populated_config)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def test_run_commands_callback(populated_config):
|
||||
"""Test that run_commands correctly passes on_node_complete to the executor."""
|
||||
service = ExecutionService(populated_config)
|
||||
|
||||
# Mock the Nodes class in connpy.services.execution_service
|
||||
with patch("connpy.services.execution_service.Nodes") as MockNodes:
|
||||
mock_executor = MockNodes.return_value
|
||||
mock_executor.run.return_value = {"router1": "output"}
|
||||
|
||||
callback = MagicMock()
|
||||
|
||||
service.run_commands(
|
||||
nodes_filter="router1",
|
||||
commands=["show version"],
|
||||
on_node_complete=callback
|
||||
)
|
||||
|
||||
# Verify executor.run was called with on_complete=callback
|
||||
# Note: ExecutionService calls executor.run(..., on_complete=on_node_complete, ...)
|
||||
MockNodes.return_value.run.assert_called_once()
|
||||
args, kwargs = MockNodes.return_value.run.call_args
|
||||
assert kwargs["on_complete"] == callback</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Test that run_commands correctly passes on_node_complete to the executor.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_execution_service.test_test_commands_callback_regression"><code class="name flex">
|
||||
<span>def <span class="ident">test_test_commands_callback_regression</span></span>(<span>populated_config)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def test_test_commands_callback_regression(populated_config):
|
||||
"""
|
||||
Test that test_commands correctly passes on_node_complete to the executor.
|
||||
Regression: ExecutionService.test_commands currently ignores on_node_complete.
|
||||
"""
|
||||
service = ExecutionService(populated_config)
|
||||
|
||||
with patch("connpy.services.execution_service.Nodes") as MockNodes:
|
||||
mock_executor = MockNodes.return_value
|
||||
mock_executor.test.return_value = {"router1": {"PASS": True}}
|
||||
|
||||
callback = MagicMock()
|
||||
|
||||
service.test_commands(
|
||||
nodes_filter="router1",
|
||||
commands=["show version"],
|
||||
expected=["12.4"],
|
||||
on_node_complete=callback
|
||||
)
|
||||
|
||||
# This is expected to FAIL because ExecutionService.test_commands
|
||||
# doesn't pass on_complete to executor.test
|
||||
MockNodes.return_value.test.assert_called_once()
|
||||
args, kwargs = MockNodes.return_value.test.call_args
|
||||
|
||||
# We expect 'on_complete' to be in kwargs and equal to our callback
|
||||
assert "on_complete" in kwargs, "on_complete parameter missing in call to executor.test"
|
||||
assert kwargs["on_complete"] == callback</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Test that test_commands correctly passes on_node_complete to the executor.
|
||||
Regression: ExecutionService.test_commands currently ignores on_node_complete.</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="">
|
||||
<li><code><a title="connpy.tests.test_execution_service.test_run_commands_callback" href="#connpy.tests.test_execution_service.test_run_commands_callback">test_run_commands_callback</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_execution_service.test_test_commands_callback_regression" href="#connpy.tests.test_execution_service.test_test_commands_callback_regression">test_test_commands_callback_regression</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.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,184 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.tests.test_node_service 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.test_node_service</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-functions">Functions</h2>
|
||||
<dl>
|
||||
<dt id="connpy.tests.test_node_service.test_list_nodes_case_sensitivity"><code class="name flex">
|
||||
<span>def <span class="ident">test_list_nodes_case_sensitivity</span></span>(<span>populated_config)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def test_list_nodes_case_sensitivity(populated_config):
|
||||
"""Test that filtering respects the case setting in config."""
|
||||
service = NodeService(populated_config)
|
||||
|
||||
# Default case is False (case-insensitive)
|
||||
nodes = service.list_nodes(filter_str="ROUTER")
|
||||
assert "router1" in nodes</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Test that filtering respects the case setting in config.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_node_service.test_list_nodes_dynamic_formatting"><code class="name flex">
|
||||
<span>def <span class="ident">test_list_nodes_dynamic_formatting</span></span>(<span>populated_config)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def test_list_nodes_dynamic_formatting(populated_config):
|
||||
"""
|
||||
Test that list_nodes supports dynamic formatting for any node attribute.
|
||||
Regression: NodeService currently has hardcoded support for name, location, host.
|
||||
"""
|
||||
service = NodeService(populated_config)
|
||||
|
||||
# Try to format using 'user' and 'protocol' which are NOT in the hardcoded list
|
||||
# (name, location, host)
|
||||
format_str = "{name} -> {user}@{host} ({protocol})"
|
||||
|
||||
# router1: host=10.0.0.1, user=admin, protocol=ssh
|
||||
# Expected: "router1 -> admin@10.0.0.1 (ssh)"
|
||||
|
||||
formatted = service.list_nodes(filter_str="router1", format_str=format_str)
|
||||
|
||||
assert len(formatted) == 1
|
||||
# This will FAIL if it only supports {name}, {location}, {host}
|
||||
assert formatted[0] == "router1 -> admin@10.0.0.1 (ssh)"</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Test that list_nodes supports dynamic formatting for any node attribute.
|
||||
Regression: NodeService currently has hardcoded support for name, location, host.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_node_service.test_list_nodes_filtering_parity"><code class="name flex">
|
||||
<span>def <span class="ident">test_list_nodes_filtering_parity</span></span>(<span>populated_config)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def test_list_nodes_filtering_parity(populated_config):
|
||||
"""
|
||||
Test that list_nodes uses literal 'in' logic instead of re.search.
|
||||
Regression: NodeService currently uses re.search in some versions,
|
||||
but we want to ensure it uses literal 'in' for parity.
|
||||
"""
|
||||
service = NodeService(populated_config)
|
||||
|
||||
# If it uses 'in' logic, '1' should match all nodes containing '1'
|
||||
# router1, server1@office, db1@datacenter@office
|
||||
nodes = service.list_nodes(filter_str="1")
|
||||
assert len(nodes) == 3
|
||||
assert "router1" in nodes
|
||||
assert "server1@office" in nodes
|
||||
assert "db1@datacenter@office" in nodes
|
||||
|
||||
# Test regex-specific characters.
|
||||
# NodeService should use re.search, so '^router' will match 'router1'.
|
||||
nodes_regex = service.list_nodes(filter_str="^router")
|
||||
|
||||
assert "router1" in nodes_regex</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Test that list_nodes uses literal 'in' logic instead of re.search.
|
||||
Regression: NodeService currently uses re.search in some versions,
|
||||
but we want to ensure it uses literal 'in' for parity.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_node_service.test_node_editing_parity"><code class="name flex">
|
||||
<span>def <span class="ident">test_node_editing_parity</span></span>(<span>populated_config)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def test_node_editing_parity(populated_config):
|
||||
"""
|
||||
Test that add_node improperly raises NodeAlreadyExistsError when used for editing.
|
||||
Regression: connapp._mod calls add_node instead of update_node.
|
||||
"""
|
||||
service = NodeService(populated_config)
|
||||
|
||||
# router1 already exists in populated_config
|
||||
# We confirm that calling add_node with an existing ID raises NodeAlreadyExistsError
|
||||
# which is why connapp._mod (which calls add_node) is currently broken for editing.
|
||||
with pytest.raises(NodeAlreadyExistsError):
|
||||
service.add_node("router1", {"host": "1.1.1.1"})</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Test that add_node improperly raises NodeAlreadyExistsError when used for editing.
|
||||
Regression: connapp._mod calls add_node instead of update_node.</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="">
|
||||
<li><code><a title="connpy.tests.test_node_service.test_list_nodes_case_sensitivity" href="#connpy.tests.test_node_service.test_list_nodes_case_sensitivity">test_list_nodes_case_sensitivity</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_node_service.test_list_nodes_dynamic_formatting" href="#connpy.tests.test_node_service.test_list_nodes_dynamic_formatting">test_list_nodes_dynamic_formatting</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_node_service.test_list_nodes_filtering_parity" href="#connpy.tests.test_node_service.test_list_nodes_filtering_parity">test_list_nodes_filtering_parity</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_node_service.test_node_editing_parity" href="#connpy.tests.test_node_service.test_node_editing_parity">test_node_editing_parity</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.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -98,11 +98,80 @@ el.replaceWith(d);
|
||||
assert lines[0] == "[i] line1"
|
||||
# Second line should be indented by len("[i] ") = 4 chars
|
||||
assert lines[1].startswith(" line2")
|
||||
assert lines[2].startswith(" line3")</code></pre>
|
||||
assert lines[2].startswith(" line3")
|
||||
|
||||
def test_data_output(self, capsys):
|
||||
printer.data("my title", "key: value")
|
||||
captured = capsys.readouterr()
|
||||
# Rich output is formatted with ansi escape sequences or box drawing chars
|
||||
# Just check that title and content appear in the output stream
|
||||
assert "my title" in captured.out
|
||||
assert "key" in captured.out
|
||||
|
||||
def test_node_panel_pass(self, capsys):
|
||||
printer.node_panel("node1", "output line\n", 0)
|
||||
captured = capsys.readouterr()
|
||||
assert "node1" in captured.out
|
||||
assert "PASS" in captured.out
|
||||
assert "output line" in captured.out
|
||||
|
||||
def test_node_panel_fail(self, capsys):
|
||||
printer.node_panel("node2", "error line\n", 1)
|
||||
captured = capsys.readouterr()
|
||||
assert "node2" in captured.out
|
||||
assert "FAIL" in captured.out
|
||||
assert "error line" in captured.out
|
||||
|
||||
def test_test_panel(self, capsys):
|
||||
printer.test_panel("node1", "output", 0, {"check1": True, "check2": False})
|
||||
captured = capsys.readouterr()
|
||||
assert "node1" in captured.out
|
||||
assert "check1" in captured.out
|
||||
assert "check2" in captured.out
|
||||
|
||||
def test_test_summary(self, capsys):
|
||||
results = {"node1": {"test1": True}, "node2": {"test2": False}}
|
||||
printer.test_summary(results)
|
||||
captured = capsys.readouterr()
|
||||
assert "node1" in captured.out
|
||||
assert "node2" in captured.out
|
||||
assert "test1" in captured.out
|
||||
assert "test2" in captured.out
|
||||
|
||||
def test_header_output(self, capsys):
|
||||
printer.header("My Header")
|
||||
captured = capsys.readouterr()
|
||||
assert "My Header" in captured.out
|
||||
|
||||
def test_kv_output(self, capsys):
|
||||
printer.kv("mykeystring", "myvaluestring")
|
||||
captured = capsys.readouterr()
|
||||
assert "mykeystring" in captured.out
|
||||
assert "myvaluestring" in captured.out
|
||||
|
||||
def test_confirm_action(self, capsys):
|
||||
printer.confirm_action("router1", "delete")
|
||||
captured = capsys.readouterr()
|
||||
assert "[i] delete: router1" in captured.out</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
<h3>Methods</h3>
|
||||
<dl>
|
||||
<dt id="connpy.tests.test_printer.TestPrinter.test_confirm_action"><code class="name flex">
|
||||
<span>def <span class="ident">test_confirm_action</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_confirm_action(self, capsys):
|
||||
printer.confirm_action("router1", "delete")
|
||||
captured = capsys.readouterr()
|
||||
assert "[i] delete: router1" in captured.out</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<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>
|
||||
@@ -118,6 +187,24 @@ el.replaceWith(d);
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_printer.TestPrinter.test_data_output"><code class="name flex">
|
||||
<span>def <span class="ident">test_data_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_data_output(self, capsys):
|
||||
printer.data("my title", "key: value")
|
||||
captured = capsys.readouterr()
|
||||
# Rich output is formatted with ansi escape sequences or box drawing chars
|
||||
# Just check that title and content appear in the output stream
|
||||
assert "my title" in captured.out
|
||||
assert "key" 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>
|
||||
@@ -148,6 +235,21 @@ el.replaceWith(d);
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_printer.TestPrinter.test_header_output"><code class="name flex">
|
||||
<span>def <span class="ident">test_header_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_header_output(self, capsys):
|
||||
printer.header("My Header")
|
||||
captured = capsys.readouterr()
|
||||
assert "My Header" in captured.out</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>
|
||||
@@ -163,6 +265,22 @@ el.replaceWith(d);
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_printer.TestPrinter.test_kv_output"><code class="name flex">
|
||||
<span>def <span class="ident">test_kv_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_kv_output(self, capsys):
|
||||
printer.kv("mykeystring", "myvaluestring")
|
||||
captured = capsys.readouterr()
|
||||
assert "mykeystring" in captured.out
|
||||
assert "myvaluestring" 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>
|
||||
@@ -182,6 +300,40 @@ el.replaceWith(d);
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_printer.TestPrinter.test_node_panel_fail"><code class="name flex">
|
||||
<span>def <span class="ident">test_node_panel_fail</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_node_panel_fail(self, capsys):
|
||||
printer.node_panel("node2", "error line\n", 1)
|
||||
captured = capsys.readouterr()
|
||||
assert "node2" in captured.out
|
||||
assert "FAIL" in captured.out
|
||||
assert "error line" in captured.out</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_printer.TestPrinter.test_node_panel_pass"><code class="name flex">
|
||||
<span>def <span class="ident">test_node_panel_pass</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_node_panel_pass(self, capsys):
|
||||
printer.node_panel("node1", "output line\n", 0)
|
||||
captured = capsys.readouterr()
|
||||
assert "node1" in captured.out
|
||||
assert "PASS" in captured.out
|
||||
assert "output line" in captured.out</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>
|
||||
@@ -212,6 +364,42 @@ el.replaceWith(d);
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_printer.TestPrinter.test_test_panel"><code class="name flex">
|
||||
<span>def <span class="ident">test_test_panel</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_test_panel(self, capsys):
|
||||
printer.test_panel("node1", "output", 0, {"check1": True, "check2": False})
|
||||
captured = capsys.readouterr()
|
||||
assert "node1" in captured.out
|
||||
assert "check1" in captured.out
|
||||
assert "check2" in captured.out</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_printer.TestPrinter.test_test_summary"><code class="name flex">
|
||||
<span>def <span class="ident">test_test_summary</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_test_summary(self, capsys):
|
||||
results = {"node1": {"test1": True}, "node2": {"test2": False}}
|
||||
printer.test_summary(results)
|
||||
captured = capsys.readouterr()
|
||||
assert "node1" in captured.out
|
||||
assert "node2" in captured.out
|
||||
assert "test1" in captured.out
|
||||
assert "test2" 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>
|
||||
@@ -247,13 +435,21 @@ el.replaceWith(d);
|
||||
<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_confirm_action" href="#connpy.tests.test_printer.TestPrinter.test_confirm_action">test_confirm_action</a></code></li>
|
||||
<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_data_output" href="#connpy.tests.test_printer.TestPrinter.test_data_output">test_data_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_header_output" href="#connpy.tests.test_printer.TestPrinter.test_header_output">test_header_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_kv_output" href="#connpy.tests.test_printer.TestPrinter.test_kv_output">test_kv_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_node_panel_fail" href="#connpy.tests.test_printer.TestPrinter.test_node_panel_fail">test_node_panel_fail</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_printer.TestPrinter.test_node_panel_pass" href="#connpy.tests.test_printer.TestPrinter.test_node_panel_pass">test_node_panel_pass</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_test_panel" href="#connpy.tests.test_printer.TestPrinter.test_test_panel">test_test_panel</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_printer.TestPrinter.test_test_summary" href="#connpy.tests.test_printer.TestPrinter.test_test_summary">test_test_summary</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>
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.tests.test_profile_service 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.test_profile_service</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-functions">Functions</h2>
|
||||
<dl>
|
||||
<dt id="connpy.tests.test_profile_service.test_delete_default_profile_fails"><code class="name flex">
|
||||
<span>def <span class="ident">test_delete_default_profile_fails</span></span>(<span>populated_config)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def test_delete_default_profile_fails(populated_config):
|
||||
"""Test that deleting the 'default' profile is prohibited."""
|
||||
service = ProfileService(populated_config)
|
||||
from connpy.services.exceptions import InvalidConfigurationError
|
||||
|
||||
with pytest.raises(InvalidConfigurationError, match="Cannot delete the 'default' profile"):
|
||||
service.delete_profile("default")</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Test that deleting the 'default' profile is prohibited.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_profile_service.test_delete_used_profile_fails"><code class="name flex">
|
||||
<span>def <span class="ident">test_delete_used_profile_fails</span></span>(<span>populated_config)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def test_delete_used_profile_fails(populated_config):
|
||||
"""Test that deleting a profile used by nodes is prohibited."""
|
||||
service = ProfileService(populated_config)
|
||||
from connpy.services.exceptions import InvalidConfigurationError
|
||||
|
||||
# In populated_config, we need to make sure a node uses a profile
|
||||
# Let's add a node that uses 'office-user'
|
||||
populated_config._connections_add(id="testnode", host="1.1.1.1", user="@office-user")
|
||||
|
||||
with pytest.raises(InvalidConfigurationError, match="is used by nodes"):
|
||||
service.delete_profile("office-user")</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Test that deleting a profile used by nodes is prohibited.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_profile_service.test_profile_crud"><code class="name flex">
|
||||
<span>def <span class="ident">test_profile_crud</span></span>(<span>populated_config)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def test_profile_crud(populated_config):
|
||||
"""Test basic CRUD operations for profiles."""
|
||||
service = ProfileService(populated_config)
|
||||
|
||||
# List
|
||||
profiles = service.list_profiles()
|
||||
assert "default" in profiles
|
||||
assert "office-user" in profiles
|
||||
|
||||
# Get
|
||||
office = service.get_profile("office-user")
|
||||
assert office["user"] == "officeadmin"
|
||||
|
||||
# Add
|
||||
new_data = {
|
||||
"user": "newadmin",
|
||||
"password": "newpassword"
|
||||
}
|
||||
service.add_profile("new-profile", new_data)
|
||||
assert "new-profile" in service.list_profiles()
|
||||
assert service.get_profile("new-profile")["user"] == "newadmin"
|
||||
|
||||
# Update
|
||||
update_data = {
|
||||
"user": "updatedadmin"
|
||||
}
|
||||
service.update_profile("new-profile", update_data)
|
||||
assert service.get_profile("new-profile")["user"] == "updatedadmin"
|
||||
|
||||
# Delete
|
||||
service.delete_profile("new-profile")
|
||||
assert "new-profile" not in service.list_profiles()</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Test basic CRUD operations for profiles.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_profile_service.test_profile_inheritance_parity"><code class="name flex">
|
||||
<span>def <span class="ident">test_profile_inheritance_parity</span></span>(<span>populated_config)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def test_profile_inheritance_parity(populated_config):
|
||||
"""
|
||||
Test that profiles can inherit from other profiles.
|
||||
Regression: ProfileService currently doesn't resolve inheritance within profiles.
|
||||
"""
|
||||
service = ProfileService(populated_config)
|
||||
|
||||
# Create a profile that inherits from 'office-user'
|
||||
# 'office-user' has user='officeadmin', password='officepass'
|
||||
inherited_data = {
|
||||
"user": "@office-user",
|
||||
"options": "-v"
|
||||
}
|
||||
service.add_profile("inherited-profile", inherited_data)
|
||||
|
||||
# When we get the profile, we expect it to be resolved if inheritance is supported
|
||||
# This is a common pattern in connpy for nodes, but should it work for profiles?
|
||||
# The task mentions "profile CRUD and inheritance parity".
|
||||
|
||||
profile = service.get_profile("inherited-profile")
|
||||
|
||||
# If inheritance is resolved, user should be 'officeadmin'
|
||||
# This is expected to FAIL if ProfileService just returns the raw dict.
|
||||
assert profile["user"] == "officeadmin"
|
||||
assert profile["password"] == "officepass"
|
||||
assert profile["options"] == "-v"</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Test that profiles can inherit from other profiles.
|
||||
Regression: ProfileService currently doesn't resolve inheritance within profiles.</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="">
|
||||
<li><code><a title="connpy.tests.test_profile_service.test_delete_default_profile_fails" href="#connpy.tests.test_profile_service.test_delete_default_profile_fails">test_delete_default_profile_fails</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_profile_service.test_delete_used_profile_fails" href="#connpy.tests.test_profile_service.test_delete_used_profile_fails">test_delete_used_profile_fails</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_profile_service.test_profile_crud" href="#connpy.tests.test_profile_service.test_profile_crud">test_profile_crud</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_profile_service.test_profile_inheritance_parity" href="#connpy.tests.test_profile_service.test_profile_inheritance_parity">test_profile_inheritance_parity</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.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,145 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.tests.test_provider 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.test_provider</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-functions">Functions</h2>
|
||||
<dl>
|
||||
<dt id="connpy.tests.test_provider.test_service_provider_local_mode"><code class="name flex">
|
||||
<span>def <span class="ident">test_service_provider_local_mode</span></span>(<span>)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def test_service_provider_local_mode():
|
||||
config_mock = MagicMock()
|
||||
with patch("connpy.services.provider.NodeService", create=True) as MockNodeService, \
|
||||
patch("connpy.services.provider.ProfileService", create=True), \
|
||||
patch("connpy.services.provider.ConfigService", create=True), \
|
||||
patch("connpy.services.provider.PluginService", create=True), \
|
||||
patch("connpy.services.provider.AIService", create=True), \
|
||||
patch("connpy.services.provider.SystemService", create=True), \
|
||||
patch("connpy.services.provider.ExecutionService", create=True), \
|
||||
patch("connpy.services.provider.ImportExportService", create=True):
|
||||
|
||||
provider = ServiceProvider(config_mock, mode="local")
|
||||
|
||||
assert provider.mode == "local"
|
||||
assert provider.config == config_mock
|
||||
# Verify that an attribute was created
|
||||
assert provider.nodes is not None</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_provider.test_service_provider_remote_mode"><code class="name flex">
|
||||
<span>def <span class="ident">test_service_provider_remote_mode</span></span>(<span>)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def test_service_provider_remote_mode():
|
||||
config_mock = MagicMock()
|
||||
with patch("connpy.services.provider.ConfigService", create=True) as MockConfigService, \
|
||||
patch("grpc.insecure_channel", create=True) as MockChannel:
|
||||
|
||||
provider = ServiceProvider(config_mock, mode="remote", remote_host="localhost:50051")
|
||||
|
||||
# Verify ConfigService is initialized locally
|
||||
assert provider.config_svc is not None
|
||||
|
||||
# Verify grpc channel was created
|
||||
MockChannel.assert_called_once_with("localhost:50051")
|
||||
|
||||
# Verify a stub was assigned
|
||||
assert provider.nodes is not None</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_provider.test_service_provider_unknown_mode"><code class="name flex">
|
||||
<span>def <span class="ident">test_service_provider_unknown_mode</span></span>(<span>)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def test_service_provider_unknown_mode():
|
||||
config_mock = MagicMock()
|
||||
with pytest.raises(ValueError, match="Unknown service mode: invalid_mode"):
|
||||
ServiceProvider(config_mock, mode="invalid_mode")</code></pre>
|
||||
</details>
|
||||
<div class="desc"></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="">
|
||||
<li><code><a title="connpy.tests.test_provider.test_service_provider_local_mode" href="#connpy.tests.test_provider.test_service_provider_local_mode">test_service_provider_local_mode</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_provider.test_service_provider_remote_mode" href="#connpy.tests.test_provider.test_service_provider_remote_mode">test_service_provider_remote_mode</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_provider.test_service_provider_unknown_mode" href="#connpy.tests.test_provider.test_service_provider_unknown_mode">test_service_provider_unknown_mode</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.5</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
+156
-192
@@ -5,7 +5,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<title>connpy.tests.test_sync API documentation</title>
|
||||
<meta name="description" content="Tests for connpy.core_plugins.sync">
|
||||
<meta name="description" content="Tests for connpy.services.sync_service">
|
||||
<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>
|
||||
@@ -36,7 +36,7 @@ el.replaceWith(d);
|
||||
<h1 class="title">Module <code>connpy.tests.test_sync</code></h1>
|
||||
</header>
|
||||
<section id="section-intro">
|
||||
<p>Tests for connpy.core_plugins.sync</p>
|
||||
<p>Tests for connpy.services.sync_service</p>
|
||||
</section>
|
||||
<section>
|
||||
</section>
|
||||
@@ -45,8 +45,8 @@ el.replaceWith(d);
|
||||
<section>
|
||||
<h2 class="section-title" id="header-functions">Functions</h2>
|
||||
<dl>
|
||||
<dt id="connpy.tests.test_sync.mock_connapp"><code class="name flex">
|
||||
<span>def <span class="ident">mock_connapp</span></span>(<span>)</span>
|
||||
<dt id="connpy.tests.test_sync.mock_config"><code class="name flex">
|
||||
<span>def <span class="ident">mock_config</span></span>(<span>)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
@@ -54,13 +54,15 @@ el.replaceWith(d);
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@pytest.fixture
|
||||
def mock_connapp():
|
||||
app = MagicMock()
|
||||
app.config.defaultdir = "/fake/dir"
|
||||
app.config.file = "/fake/dir/config.yaml"
|
||||
app.config.key = "/fake/dir/.osk"
|
||||
app.config.config = {"sync": True}
|
||||
return app</code></pre>
|
||||
def mock_config():
|
||||
config = MagicMock()
|
||||
config.defaultdir = "/fake/dir"
|
||||
config.file = "/fake/dir/config.yaml"
|
||||
config.key = "/fake/dir/.osk"
|
||||
config.cachefile = "/fake/dir/.cache"
|
||||
config.fzf_cachefile = "/fake/dir/.fzf_cache"
|
||||
config.config = {"sync": True, "sync_remote": False}
|
||||
return config</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
@@ -69,79 +71,85 @@ def mock_connapp():
|
||||
<section>
|
||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
||||
<dl>
|
||||
<dt id="connpy.tests.test_sync.TestSyncPlugin"><code class="flex name class">
|
||||
<span>class <span class="ident">TestSyncPlugin</span></span>
|
||||
<dt id="connpy.tests.test_sync.TestSyncService"><code class="flex name class">
|
||||
<span>class <span class="ident">TestSyncService</span></span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class TestSyncPlugin:
|
||||
def test_init(self, mock_connapp):
|
||||
s = sync(mock_connapp)
|
||||
assert s.sync is True
|
||||
assert s.file == "/fake/dir/config.yaml"
|
||||
assert s.token_file == "/fake/dir/gtoken.json"
|
||||
<pre><code class="python">class TestSyncService:
|
||||
def test_init(self, mock_config):
|
||||
s = SyncService(mock_config)
|
||||
assert s.sync_enabled is True
|
||||
assert s.token_file == os.path.join("/fake/dir", "gtoken.json")
|
||||
|
||||
@patch("connpy.core_plugins.sync.os.path.exists")
|
||||
@patch("connpy.core_plugins.sync.Credentials")
|
||||
def test_get_credentials_success(self, MockCreds, mock_exists, mock_connapp):
|
||||
@patch("connpy.services.sync_service.os.path.exists")
|
||||
@patch("connpy.services.sync_service.Credentials")
|
||||
def test_get_credentials_success(self, MockCreds, mock_exists, mock_config):
|
||||
mock_exists.return_value = True
|
||||
mock_cred_instance = MagicMock()
|
||||
mock_cred_instance.valid = True
|
||||
MockCreds.from_authorized_user_file.return_value = mock_cred_instance
|
||||
|
||||
s = sync(mock_connapp)
|
||||
s = SyncService(mock_config)
|
||||
creds = s.get_credentials()
|
||||
assert creds == mock_cred_instance
|
||||
|
||||
@patch("connpy.core_plugins.sync.os.path.exists")
|
||||
def test_get_credentials_not_found(self, mock_exists, mock_connapp):
|
||||
@patch("connpy.services.sync_service.os.path.exists")
|
||||
def test_get_credentials_not_found(self, mock_exists, mock_config):
|
||||
mock_exists.return_value = False
|
||||
s = sync(mock_connapp)
|
||||
assert s.get_credentials() == 0
|
||||
s = SyncService(mock_config)
|
||||
assert s.get_credentials() is None
|
||||
|
||||
@patch("connpy.core_plugins.sync.zipfile.ZipFile")
|
||||
@patch("connpy.core_plugins.sync.os.path.basename")
|
||||
def test_compress_specific_files(self, mock_basename, MockZipFile, mock_connapp):
|
||||
@patch("connpy.services.sync_service.zipfile.ZipFile")
|
||||
@patch("connpy.services.sync_service.os.path.exists")
|
||||
@patch("connpy.services.sync_service.os.path.basename")
|
||||
def test_compress_and_upload_local(self, mock_basename, mock_exists, MockZipFile, mock_config):
|
||||
mock_basename.return_value = "config.yaml"
|
||||
s = sync(mock_connapp)
|
||||
mock_exists.return_value = True
|
||||
s = SyncService(mock_config)
|
||||
|
||||
# Mocking list_backups and upload_file to avoid real API calls
|
||||
s.list_backups = MagicMock(return_value=[])
|
||||
s.upload_file = MagicMock(return_value=True)
|
||||
|
||||
zip_mock = MagicMock()
|
||||
MockZipFile.return_value.__enter__.return_value = zip_mock
|
||||
|
||||
s.compress_specific_files("/fake/zip.zip")
|
||||
zip_mock.write.assert_any_call(s.file, "config.yaml")
|
||||
zip_mock.write.assert_any_call(s.key, ".osk")
|
||||
s.compress_and_upload()
|
||||
# Verify zip was created with local config and key
|
||||
zip_mock.write.assert_any_call(s.config.file, "config.yaml")
|
||||
zip_mock.write.assert_any_call(s.config.key, ".osk")
|
||||
|
||||
@patch("connpy.core_plugins.sync.zipfile.ZipFile")
|
||||
@patch("connpy.core_plugins.sync.os.path.dirname")
|
||||
def test_decompress_zip_yaml(self, mock_dirname, MockZipFile, mock_connapp):
|
||||
@patch("connpy.services.sync_service.zipfile.ZipFile")
|
||||
@patch("connpy.services.sync_service.os.path.exists")
|
||||
@patch("connpy.services.sync_service.os.path.dirname")
|
||||
@patch("connpy.services.sync_service.os.remove")
|
||||
def test_perform_restore(self, mock_remove, mock_dirname, mock_exists, MockZipFile, mock_config):
|
||||
mock_dirname.return_value = "/fake/dir"
|
||||
s = sync(mock_connapp)
|
||||
# Mock exists to return True for key and zip, but False for caches during the cleanup phase
|
||||
def exists_side_effect(path):
|
||||
if ".cache" in path or ".fzf_cache" in path:
|
||||
return False
|
||||
return True
|
||||
mock_exists.side_effect = exists_side_effect
|
||||
|
||||
s = SyncService(mock_config)
|
||||
zip_mock = MagicMock()
|
||||
zip_mock.namelist.return_value = ["config.yaml", ".osk"]
|
||||
MockZipFile.return_value.__enter__.return_value = zip_mock
|
||||
|
||||
assert s.decompress_zip("/fake/zip.zip") == 0
|
||||
zip_mock.extract.assert_any_call("config.yaml", "/fake/dir")
|
||||
with patch("connpy.services.sync_service.yaml.safe_load") as mock_load:
|
||||
mock_load.return_value = {"connections": {}, "profiles": {}, "config": {}}
|
||||
assert s.perform_restore("/fake/zip.zip") is True
|
||||
|
||||
zip_mock.extract.assert_any_call(".osk", "/fake/dir")
|
||||
|
||||
@patch("connpy.core_plugins.sync.zipfile.ZipFile")
|
||||
@patch("connpy.core_plugins.sync.os.path.dirname")
|
||||
def test_decompress_zip_json_fallback(self, mock_dirname, MockZipFile, mock_connapp):
|
||||
mock_dirname.return_value = "/fake/dir"
|
||||
s = sync(mock_connapp)
|
||||
zip_mock = MagicMock()
|
||||
zip_mock.namelist.return_value = ["config.json", ".osk"]
|
||||
MockZipFile.return_value.__enter__.return_value = zip_mock
|
||||
|
||||
assert s.decompress_zip("/fake/old_zip.zip") == 0
|
||||
zip_mock.extract.assert_any_call("config.json", "/fake/dir")
|
||||
|
||||
@patch.object(sync, "get_credentials")
|
||||
@patch("connpy.core_plugins.sync.build")
|
||||
def test_get_appdata_files(self, mock_build, mock_get_credentials, mock_connapp):
|
||||
@patch.object(SyncService, "get_credentials")
|
||||
@patch("connpy.services.sync_service.build")
|
||||
def test_list_backups(self, mock_build, mock_get_credentials, mock_config):
|
||||
mock_get_credentials.return_value = MagicMock()
|
||||
mock_service = MagicMock()
|
||||
mock_build.return_value = mock_service
|
||||
@@ -152,131 +160,109 @@ def mock_connapp():
|
||||
]
|
||||
}
|
||||
|
||||
s = sync(mock_connapp)
|
||||
files = s.get_appdata_files()
|
||||
s = SyncService(mock_config)
|
||||
files = s.list_backups()
|
||||
assert len(files) == 1
|
||||
assert files[0]["id"] == "1"
|
||||
assert files[0]["timestamp"] == "1000"
|
||||
|
||||
@patch.object(sync, "get_credentials")
|
||||
@patch("connpy.core_plugins.sync.build")
|
||||
@patch("connpy.core_plugins.sync.MediaFileUpload")
|
||||
@patch("connpy.core_plugins.sync.os.path.basename")
|
||||
def test_backup_file_to_drive(self, mock_basename, mock_media, mock_build, mock_get_credentials, mock_connapp):
|
||||
mock_get_credentials.return_value = MagicMock()
|
||||
mock_basename.return_value = "backup.zip"
|
||||
mock_service = MagicMock()
|
||||
mock_build.return_value = mock_service
|
||||
|
||||
s = sync(mock_connapp)
|
||||
assert s.backup_file_to_drive("/fake/backup.zip", 1234567890000) == 0
|
||||
mock_service.files().create.assert_called_once()</code></pre>
|
||||
assert files[0]["timestamp"] == "1000"</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
<h3>Methods</h3>
|
||||
<dl>
|
||||
<dt id="connpy.tests.test_sync.TestSyncPlugin.test_backup_file_to_drive"><code class="name flex">
|
||||
<span>def <span class="ident">test_backup_file_to_drive</span></span>(<span>self, mock_basename, mock_media, mock_build, mock_get_credentials, mock_connapp)</span>
|
||||
<dt id="connpy.tests.test_sync.TestSyncService.test_compress_and_upload_local"><code class="name flex">
|
||||
<span>def <span class="ident">test_compress_and_upload_local</span></span>(<span>self, mock_basename, mock_exists, MockZipFile, mock_config)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch.object(sync, "get_credentials")
|
||||
@patch("connpy.core_plugins.sync.build")
|
||||
@patch("connpy.core_plugins.sync.MediaFileUpload")
|
||||
@patch("connpy.core_plugins.sync.os.path.basename")
|
||||
def test_backup_file_to_drive(self, mock_basename, mock_media, mock_build, mock_get_credentials, mock_connapp):
|
||||
mock_get_credentials.return_value = MagicMock()
|
||||
mock_basename.return_value = "backup.zip"
|
||||
mock_service = MagicMock()
|
||||
mock_build.return_value = mock_service
|
||||
|
||||
s = sync(mock_connapp)
|
||||
assert s.backup_file_to_drive("/fake/backup.zip", 1234567890000) == 0
|
||||
mock_service.files().create.assert_called_once()</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_sync.TestSyncPlugin.test_compress_specific_files"><code class="name flex">
|
||||
<span>def <span class="ident">test_compress_specific_files</span></span>(<span>self, mock_basename, MockZipFile, mock_connapp)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.core_plugins.sync.zipfile.ZipFile")
|
||||
@patch("connpy.core_plugins.sync.os.path.basename")
|
||||
def test_compress_specific_files(self, mock_basename, MockZipFile, mock_connapp):
|
||||
<pre><code class="python">@patch("connpy.services.sync_service.zipfile.ZipFile")
|
||||
@patch("connpy.services.sync_service.os.path.exists")
|
||||
@patch("connpy.services.sync_service.os.path.basename")
|
||||
def test_compress_and_upload_local(self, mock_basename, mock_exists, MockZipFile, mock_config):
|
||||
mock_basename.return_value = "config.yaml"
|
||||
s = sync(mock_connapp)
|
||||
mock_exists.return_value = True
|
||||
s = SyncService(mock_config)
|
||||
|
||||
# Mocking list_backups and upload_file to avoid real API calls
|
||||
s.list_backups = MagicMock(return_value=[])
|
||||
s.upload_file = MagicMock(return_value=True)
|
||||
|
||||
zip_mock = MagicMock()
|
||||
MockZipFile.return_value.__enter__.return_value = zip_mock
|
||||
|
||||
s.compress_specific_files("/fake/zip.zip")
|
||||
zip_mock.write.assert_any_call(s.file, "config.yaml")
|
||||
zip_mock.write.assert_any_call(s.key, ".osk")</code></pre>
|
||||
s.compress_and_upload()
|
||||
# Verify zip was created with local config and key
|
||||
zip_mock.write.assert_any_call(s.config.file, "config.yaml")
|
||||
zip_mock.write.assert_any_call(s.config.key, ".osk")</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_sync.TestSyncPlugin.test_decompress_zip_json_fallback"><code class="name flex">
|
||||
<span>def <span class="ident">test_decompress_zip_json_fallback</span></span>(<span>self, mock_dirname, MockZipFile, mock_connapp)</span>
|
||||
<dt id="connpy.tests.test_sync.TestSyncService.test_get_credentials_not_found"><code class="name flex">
|
||||
<span>def <span class="ident">test_get_credentials_not_found</span></span>(<span>self, mock_exists, mock_config)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.core_plugins.sync.zipfile.ZipFile")
|
||||
@patch("connpy.core_plugins.sync.os.path.dirname")
|
||||
def test_decompress_zip_json_fallback(self, mock_dirname, MockZipFile, mock_connapp):
|
||||
mock_dirname.return_value = "/fake/dir"
|
||||
s = sync(mock_connapp)
|
||||
zip_mock = MagicMock()
|
||||
zip_mock.namelist.return_value = ["config.json", ".osk"]
|
||||
MockZipFile.return_value.__enter__.return_value = zip_mock
|
||||
|
||||
assert s.decompress_zip("/fake/old_zip.zip") == 0
|
||||
zip_mock.extract.assert_any_call("config.json", "/fake/dir")</code></pre>
|
||||
<pre><code class="python">@patch("connpy.services.sync_service.os.path.exists")
|
||||
def test_get_credentials_not_found(self, mock_exists, mock_config):
|
||||
mock_exists.return_value = False
|
||||
s = SyncService(mock_config)
|
||||
assert s.get_credentials() is None</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_sync.TestSyncPlugin.test_decompress_zip_yaml"><code class="name flex">
|
||||
<span>def <span class="ident">test_decompress_zip_yaml</span></span>(<span>self, mock_dirname, MockZipFile, mock_connapp)</span>
|
||||
<dt id="connpy.tests.test_sync.TestSyncService.test_get_credentials_success"><code class="name flex">
|
||||
<span>def <span class="ident">test_get_credentials_success</span></span>(<span>self, MockCreds, mock_exists, mock_config)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.core_plugins.sync.zipfile.ZipFile")
|
||||
@patch("connpy.core_plugins.sync.os.path.dirname")
|
||||
def test_decompress_zip_yaml(self, mock_dirname, MockZipFile, mock_connapp):
|
||||
mock_dirname.return_value = "/fake/dir"
|
||||
s = sync(mock_connapp)
|
||||
zip_mock = MagicMock()
|
||||
zip_mock.namelist.return_value = ["config.yaml", ".osk"]
|
||||
MockZipFile.return_value.__enter__.return_value = zip_mock
|
||||
<pre><code class="python">@patch("connpy.services.sync_service.os.path.exists")
|
||||
@patch("connpy.services.sync_service.Credentials")
|
||||
def test_get_credentials_success(self, MockCreds, mock_exists, mock_config):
|
||||
mock_exists.return_value = True
|
||||
mock_cred_instance = MagicMock()
|
||||
mock_cred_instance.valid = True
|
||||
MockCreds.from_authorized_user_file.return_value = mock_cred_instance
|
||||
|
||||
assert s.decompress_zip("/fake/zip.zip") == 0
|
||||
zip_mock.extract.assert_any_call("config.yaml", "/fake/dir")
|
||||
zip_mock.extract.assert_any_call(".osk", "/fake/dir")</code></pre>
|
||||
s = SyncService(mock_config)
|
||||
creds = s.get_credentials()
|
||||
assert creds == mock_cred_instance</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_sync.TestSyncPlugin.test_get_appdata_files"><code class="name flex">
|
||||
<span>def <span class="ident">test_get_appdata_files</span></span>(<span>self, mock_build, mock_get_credentials, mock_connapp)</span>
|
||||
<dt id="connpy.tests.test_sync.TestSyncService.test_init"><code class="name flex">
|
||||
<span>def <span class="ident">test_init</span></span>(<span>self, mock_config)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch.object(sync, "get_credentials")
|
||||
@patch("connpy.core_plugins.sync.build")
|
||||
def test_get_appdata_files(self, mock_build, mock_get_credentials, mock_connapp):
|
||||
<pre><code class="python">def test_init(self, mock_config):
|
||||
s = SyncService(mock_config)
|
||||
assert s.sync_enabled is True
|
||||
assert s.token_file == os.path.join("/fake/dir", "gtoken.json")</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_sync.TestSyncService.test_list_backups"><code class="name flex">
|
||||
<span>def <span class="ident">test_list_backups</span></span>(<span>self, mock_build, mock_get_credentials, mock_config)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch.object(SyncService, "get_credentials")
|
||||
@patch("connpy.services.sync_service.build")
|
||||
def test_list_backups(self, mock_build, mock_get_credentials, mock_config):
|
||||
mock_get_credentials.return_value = MagicMock()
|
||||
mock_service = MagicMock()
|
||||
mock_build.return_value = mock_service
|
||||
@@ -287,65 +273,45 @@ def test_get_appdata_files(self, mock_build, mock_get_credentials, mock_connapp)
|
||||
]
|
||||
}
|
||||
|
||||
s = sync(mock_connapp)
|
||||
files = s.get_appdata_files()
|
||||
s = SyncService(mock_config)
|
||||
files = s.list_backups()
|
||||
assert len(files) == 1
|
||||
assert files[0]["id"] == "1"
|
||||
assert files[0]["timestamp"] == "1000"</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_sync.TestSyncPlugin.test_get_credentials_not_found"><code class="name flex">
|
||||
<span>def <span class="ident">test_get_credentials_not_found</span></span>(<span>self, mock_exists, mock_connapp)</span>
|
||||
<dt id="connpy.tests.test_sync.TestSyncService.test_perform_restore"><code class="name flex">
|
||||
<span>def <span class="ident">test_perform_restore</span></span>(<span>self, mock_remove, mock_dirname, mock_exists, MockZipFile, mock_config)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.core_plugins.sync.os.path.exists")
|
||||
def test_get_credentials_not_found(self, mock_exists, mock_connapp):
|
||||
mock_exists.return_value = False
|
||||
s = sync(mock_connapp)
|
||||
assert s.get_credentials() == 0</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_sync.TestSyncPlugin.test_get_credentials_success"><code class="name flex">
|
||||
<span>def <span class="ident">test_get_credentials_success</span></span>(<span>self, MockCreds, mock_exists, mock_connapp)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@patch("connpy.core_plugins.sync.os.path.exists")
|
||||
@patch("connpy.core_plugins.sync.Credentials")
|
||||
def test_get_credentials_success(self, MockCreds, mock_exists, mock_connapp):
|
||||
mock_exists.return_value = True
|
||||
mock_cred_instance = MagicMock()
|
||||
mock_cred_instance.valid = True
|
||||
MockCreds.from_authorized_user_file.return_value = mock_cred_instance
|
||||
<pre><code class="python">@patch("connpy.services.sync_service.zipfile.ZipFile")
|
||||
@patch("connpy.services.sync_service.os.path.exists")
|
||||
@patch("connpy.services.sync_service.os.path.dirname")
|
||||
@patch("connpy.services.sync_service.os.remove")
|
||||
def test_perform_restore(self, mock_remove, mock_dirname, mock_exists, MockZipFile, mock_config):
|
||||
mock_dirname.return_value = "/fake/dir"
|
||||
# Mock exists to return True for key and zip, but False for caches during the cleanup phase
|
||||
def exists_side_effect(path):
|
||||
if ".cache" in path or ".fzf_cache" in path:
|
||||
return False
|
||||
return True
|
||||
mock_exists.side_effect = exists_side_effect
|
||||
|
||||
s = sync(mock_connapp)
|
||||
creds = s.get_credentials()
|
||||
assert creds == mock_cred_instance</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.tests.test_sync.TestSyncPlugin.test_init"><code class="name flex">
|
||||
<span>def <span class="ident">test_init</span></span>(<span>self, mock_connapp)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def test_init(self, mock_connapp):
|
||||
s = sync(mock_connapp)
|
||||
assert s.sync is True
|
||||
assert s.file == "/fake/dir/config.yaml"
|
||||
assert s.token_file == "/fake/dir/gtoken.json"</code></pre>
|
||||
s = SyncService(mock_config)
|
||||
zip_mock = MagicMock()
|
||||
zip_mock.namelist.return_value = ["config.yaml", ".osk"]
|
||||
MockZipFile.return_value.__enter__.return_value = zip_mock
|
||||
|
||||
with patch("connpy.services.sync_service.yaml.safe_load") as mock_load:
|
||||
mock_load.return_value = {"connections": {}, "profiles": {}, "config": {}}
|
||||
assert s.perform_restore("/fake/zip.zip") is True
|
||||
|
||||
zip_mock.extract.assert_any_call(".osk", "/fake/dir")</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
@@ -366,22 +332,20 @@ def test_get_credentials_success(self, MockCreds, mock_exists, mock_connapp):
|
||||
</li>
|
||||
<li><h3><a href="#header-functions">Functions</a></h3>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.tests.test_sync.mock_connapp" href="#connpy.tests.test_sync.mock_connapp">mock_connapp</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_sync.mock_config" href="#connpy.tests.test_sync.mock_config">mock_config</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<h4><code><a title="connpy.tests.test_sync.TestSyncPlugin" href="#connpy.tests.test_sync.TestSyncPlugin">TestSyncPlugin</a></code></h4>
|
||||
<h4><code><a title="connpy.tests.test_sync.TestSyncService" href="#connpy.tests.test_sync.TestSyncService">TestSyncService</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.tests.test_sync.TestSyncPlugin.test_backup_file_to_drive" href="#connpy.tests.test_sync.TestSyncPlugin.test_backup_file_to_drive">test_backup_file_to_drive</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_sync.TestSyncPlugin.test_compress_specific_files" href="#connpy.tests.test_sync.TestSyncPlugin.test_compress_specific_files">test_compress_specific_files</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_sync.TestSyncPlugin.test_decompress_zip_json_fallback" href="#connpy.tests.test_sync.TestSyncPlugin.test_decompress_zip_json_fallback">test_decompress_zip_json_fallback</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_sync.TestSyncPlugin.test_decompress_zip_yaml" href="#connpy.tests.test_sync.TestSyncPlugin.test_decompress_zip_yaml">test_decompress_zip_yaml</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_sync.TestSyncPlugin.test_get_appdata_files" href="#connpy.tests.test_sync.TestSyncPlugin.test_get_appdata_files">test_get_appdata_files</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_sync.TestSyncPlugin.test_get_credentials_not_found" href="#connpy.tests.test_sync.TestSyncPlugin.test_get_credentials_not_found">test_get_credentials_not_found</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_sync.TestSyncPlugin.test_get_credentials_success" href="#connpy.tests.test_sync.TestSyncPlugin.test_get_credentials_success">test_get_credentials_success</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_sync.TestSyncPlugin.test_init" href="#connpy.tests.test_sync.TestSyncPlugin.test_init">test_init</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_sync.TestSyncService.test_compress_and_upload_local" href="#connpy.tests.test_sync.TestSyncService.test_compress_and_upload_local">test_compress_and_upload_local</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_sync.TestSyncService.test_get_credentials_not_found" href="#connpy.tests.test_sync.TestSyncService.test_get_credentials_not_found">test_get_credentials_not_found</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_sync.TestSyncService.test_get_credentials_success" href="#connpy.tests.test_sync.TestSyncService.test_get_credentials_success">test_get_credentials_success</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_sync.TestSyncService.test_init" href="#connpy.tests.test_sync.TestSyncService.test_init">test_init</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_sync.TestSyncService.test_list_backups" href="#connpy.tests.test_sync.TestSyncService.test_list_backups">test_list_backups</a></code></li>
|
||||
<li><code><a title="connpy.tests.test_sync.TestSyncService.test_perform_restore" href="#connpy.tests.test_sync.TestSyncService.test_perform_restore">test_perform_restore</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
Reference in New Issue
Block a user