documentation and completion for plugins

This commit is contained in:
2023-12-15 12:27:51 -03:00
parent 3a4fcfe77b
commit 79172be898
6 changed files with 660 additions and 75 deletions
+5 -2
View File
@@ -76,6 +76,7 @@ options:
conn pc@office
conn server
```
## Plugin Requirements for Connpy
### General Structure
- The plugin script must be a Python file.
- Only the following top-level elements are allowed in the plugin script:
@@ -312,13 +313,15 @@ from .configfile import configfile
from .connapp import connapp
from .api import *
from .ai import ai
from .plugins import Plugins
from ._version import __version__
from pkg_resources import get_distribution
__all__ = ["node", "nodes", "configfile", "connapp", "ai"]
__all__ = ["node", "nodes", "configfile", "connapp", "ai", "Plugins"]
__author__ = "Federico Luzzi"
__pdoc__ = {
'core': False,
'completion': False,
'api': False
'api': False,
'plugins': False
}
+1 -1
View File
@@ -1,2 +1,2 @@
__version__ = "3.8.0b2"
__version__ = "3.8.0b3"
+55 -2
View File
@@ -2,6 +2,7 @@ import sys
import os
import json
import glob
import importlib.util
def _getallnodes(config):
#get all nodes on configfile
@@ -47,15 +48,49 @@ def _getcwd(words, option, folderonly=False):
pathstrings = [s for s in pathstrings if os.path.isdir(s)]
return pathstrings
def _get_plugins(which, defaultdir):
enabled_files = []
disabled_files = []
all_files = []
all_plugins = {}
# Iterate over all files in the specified folder
for file in os.listdir(defaultdir + "/plugins"):
# Check if the file is a Python file
if file.endswith('.py'):
enabled_files.append(os.path.splitext(file)[0])
all_plugins[os.path.splitext(file)[0]] = os.path.join(defaultdir + "/plugins", file)
# Check if the file is a Python backup file
elif file.endswith('.py.bkp'):
disabled_files.append(os.path.splitext(os.path.splitext(file)[0])[0])
if which == "--disable":
return enabled_files
elif which == "--enable":
return disabled_files
elif which == "--del":
all_files.extend(enabled_files)
all_files.extend(disabled_files)
return all_files
elif which == "all":
return all_plugins
def main():
home = os.path.expanduser("~")
defaultdir = home + '/.config/conn'
defaultfile = defaultdir + '/config.json'
pathfile = defaultdir + '/.folder'
try:
with open(pathfile, "r") as f:
configdir = f.read().strip()
except:
configdir = defaultdir
defaultfile = configdir + '/config.json'
jsonconf = open(defaultfile)
config = json.load(jsonconf)
nodes = _getallnodes(config)
folders = _getallfolders(config)
profiles = list(config["profiles"].keys())
plugins = _get_plugins("all", defaultdir)
app = sys.argv[1]
if app in ["bash", "zsh"]:
positions = [2,4]
@@ -64,10 +99,21 @@ def main():
wordsnumber = int(sys.argv[positions[0]])
words = sys.argv[positions[1]:]
if wordsnumber == 2:
strings=["--add", "--del", "--rm", "--edit", "--mod", "--show", "mv", "move", "ls", "list", "cp", "copy", "profile", "run", "bulk", "config", "api", "ai", "export", "import", "--help"]
strings=["--add", "--del", "--rm", "--edit", "--mod", "--show", "mv", "move", "ls", "list", "cp", "copy", "profile", "run", "bulk", "config", "api", "ai", "export", "import", "--help", "plugin"]
if plugins:
strings.extend(plugins.keys())
strings.extend(nodes)
strings.extend(folders)
elif wordsnumber >=3 and words[0] in plugins.keys():
try:
spec = importlib.util.spec_from_file_location("module.name", plugins[words[0]])
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
plugin_completion = getattr(module, "_connpy_completion")
strings = plugin_completion(wordsnumber, words)
except:
exit()
elif wordsnumber >= 3 and words[0] == "ai":
if wordsnumber == 3:
strings = ["--help", "--org", "--model", "--api_key"]
@@ -91,6 +137,8 @@ def main():
strings.extend(folders)
if words[0] in ["--rm", "--del", "-r", "--mod", "--edit", "-e", "--show", "-s", "mv", "move", "cp", "copy"]:
strings.extend(nodes)
if words[0] == "plugin":
strings = ["--help", "--add", "--del", "--enable", "--disable"]
if words[0] in ["run", "import", "export"]:
strings = ["--help"]
if words[0] == "export":
@@ -120,6 +168,11 @@ def main():
strings=["true", "false"]
if words[0] == "config" and words[1] in ["--configfolder"]:
strings=_getcwd(words,words[0],True)
if words[0] == "plugin" and words[1] in ["--del", "--enable", "--disable"]:
strings=_get_plugins(words[1], defaultdir)
elif wordsnumber == 5 and words[0] == "plugin" and words[1] == "--add":
strings=_getcwd(words, words[0])
else:
exit()
+1 -1
View File
@@ -160,7 +160,7 @@ class connapp:
#Add plugins
file_path = self.config.defaultdir + "/plugins"
self.plugins = Plugins()
self.plugins.import_plugins_to_argparse(file_path, subparsers)
self.plugins._import_plugins_to_argparse(file_path, subparsers)
#Generate helps
nodeparser.usage = self._help("usage", subparsers)
nodeparser.epilog = self._help("end", subparsers)
+33 -3
View File
@@ -11,6 +11,36 @@ class Plugins:
self.plugin_parsers = {}
def verify_script(self, file_path):
"""
Verifies that a given Python script meets specific structural requirements.
This function checks a Python script for compliance with predefined structural
rules. It ensures that the script contains only allowed top-level elements
(functions, classes, imports, pass statements, and a specific if __name__ block)
and that it includes mandatory classes with specific attributes and methods.
### Arguments:
- file_path (str): The file path of the Python script to be verified.
### Returns:
- str: A message indicating the type of violation if the script doesn't meet
the requirements, or False if all requirements are met.
### Verifications:
- The presence of only allowed top-level elements.
- The existence of two specific classes: 'Parser' and 'Entrypoint'.
- 'Parser' class must only have an '__init__' method and must assign 'self.parser'
and 'self.description'.
- 'Entrypoint' class must have an '__init__' method accepting specific arguments.
If any of these checks fail, the function returns an error message indicating
the reason. If the script passes all checks, the function returns False,
indicating successful verification.
### Exceptions:
- SyntaxError: If the script contains a syntax error, it is caught and
returned as a part of the error message.
"""
with open(file_path, 'r') as file:
source_code = file.read()
@@ -60,14 +90,14 @@ class Plugins:
else:
return "Classes Entrypoint and Parser are mandatory"
def import_from_path(self, path):
def _import_from_path(self, path):
spec = importlib.util.spec_from_file_location("module.name", path)
module = importlib.util.module_from_spec(spec)
sys.modules["module.name"] = module
spec.loader.exec_module(module)
return module
def import_plugins_to_argparse(self, directory, subparsers):
def _import_plugins_to_argparse(self, directory, subparsers):
for filename in os.listdir(directory):
commands = subparsers.choices.keys()
if filename.endswith(".py"):
@@ -80,7 +110,7 @@ class Plugins:
if check_file:
continue
else:
self.plugins[root_filename] = self.import_from_path(filepath)
self.plugins[root_filename] = self._import_from_path(filepath)
self.plugin_parsers[root_filename] = self.plugins[root_filename].Parser()
subparsers.add_parser(root_filename, parents=[self.plugin_parsers[root_filename].parser], add_help=False, description=self.plugin_parsers[root_filename].description)