Implement hooking system for classes and methods. Cleanup of code
This commit is contained in:
parent
87bb6302ff
commit
1bd9bd62c5
@ -169,8 +169,8 @@ The `modify` method allows you to alter instances of a class at the time they ar
|
|||||||
connapp.config.modify(modify_config)
|
connapp.config.modify(modify_config)
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Implementing Hooks with `register_pre_hook` and `register_post_hook`
|
#### Implementing Method Hooks
|
||||||
These methods allow you to define custom logic to be executed before (`register_pre_hook`) or after (`register_post_hook`) the main logic of a method. This is particularly useful for logging, auditing, preprocessing inputs, postprocessing outputs or adding functionalities.
|
There are 2 methods that allows you to define custom logic to be executed before (`register_pre_hook`) or after (`register_post_hook`) the main logic of a method. This is particularly useful for logging, auditing, preprocessing inputs, postprocessing outputs or adding functionalities.
|
||||||
|
|
||||||
- **Usage**: Register hooks to methods to execute additional logic before or after the main method execution.
|
- **Usage**: Register hooks to methods to execute additional logic before or after the main method execution.
|
||||||
- **Registration Methods Signature**:
|
- **Registration Methods Signature**:
|
||||||
@ -305,7 +305,7 @@ import connpy
|
|||||||
conf = connpy.configfile()
|
conf = connpy.configfile()
|
||||||
organization = 'openai-org'
|
organization = 'openai-org'
|
||||||
api_key = "openai-key"
|
api_key = "openai-key"
|
||||||
myia = ai(conf, organization, api_key)
|
myia = connpy.ai(conf, organization, api_key)
|
||||||
input = "go to router 1 and get me the full configuration"
|
input = "go to router 1 and get me the full configuration"
|
||||||
result = myia.ask(input, dryrun = False)
|
result = myia.ask(input, dryrun = False)
|
||||||
print(result)
|
print(result)
|
||||||
|
@ -149,8 +149,8 @@ The `modify` method allows you to alter instances of a class at the time they ar
|
|||||||
connapp.config.modify(modify_config)
|
connapp.config.modify(modify_config)
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Implementing Hooks with `register_pre_hook` and `register_post_hook`
|
#### Implementing Method Hooks
|
||||||
These methods allow you to define custom logic to be executed before (`register_pre_hook`) or after (`register_post_hook`) the main logic of a method. This is particularly useful for logging, auditing, preprocessing inputs, postprocessing outputs or adding functionalities.
|
There are 2 methods that allows you to define custom logic to be executed before (`register_pre_hook`) or after (`register_post_hook`) the main logic of a method. This is particularly useful for logging, auditing, preprocessing inputs, postprocessing outputs or adding functionalities.
|
||||||
|
|
||||||
- **Usage**: Register hooks to methods to execute additional logic before or after the main method execution.
|
- **Usage**: Register hooks to methods to execute additional logic before or after the main method execution.
|
||||||
- **Registration Methods Signature**:
|
- **Registration Methods Signature**:
|
||||||
@ -392,7 +392,7 @@ import connpy
|
|||||||
conf = connpy.configfile()
|
conf = connpy.configfile()
|
||||||
organization = 'openai-org'
|
organization = 'openai-org'
|
||||||
api_key = "openai-key"
|
api_key = "openai-key"
|
||||||
myia = ai(conf, organization, api_key)
|
myia = connpy.ai(conf, organization, api_key)
|
||||||
input = "go to router 1 and get me the full configuration"
|
input = "go to router 1 and get me the full configuration"
|
||||||
result = myia.ask(input, dryrun = False)
|
result = myia.ask(input, dryrun = False)
|
||||||
print(result)
|
print(result)
|
||||||
@ -413,5 +413,14 @@ __pdoc__ = {
|
|||||||
'core': False,
|
'core': False,
|
||||||
'completion': False,
|
'completion': False,
|
||||||
'api': False,
|
'api': False,
|
||||||
'plugins': False
|
'plugins': False,
|
||||||
|
'core_plugins': False,
|
||||||
|
'hooks': False,
|
||||||
|
'connapp.start': False,
|
||||||
|
'ai.deferred_class_hooks': False,
|
||||||
|
'configfile.deferred_class_hooks': False,
|
||||||
|
'node.deferred_class_hooks': False,
|
||||||
|
'nodes.deferred_class_hooks': False,
|
||||||
|
'connapp': False,
|
||||||
|
'connapp.encrypt': True
|
||||||
}
|
}
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
__version__ = "4.0.0b5"
|
__version__ = "4.0.0"
|
||||||
|
|
||||||
|
11
connpy/ai.py
11
connpy/ai.py
@ -177,17 +177,6 @@ Categorize the user's request based on the operation they want to perform on the
|
|||||||
self.__prompt["confirmation_function"]["parameters"]["properties"]["response"]["type"] = "string"
|
self.__prompt["confirmation_function"]["parameters"]["properties"]["response"]["type"] = "string"
|
||||||
self.__prompt["confirmation_function"]["parameters"]["required"] = ["result"]
|
self.__prompt["confirmation_function"]["parameters"]["required"] = ["result"]
|
||||||
|
|
||||||
@MethodHook
|
|
||||||
def process_string(self, s):
|
|
||||||
if s.startswith('[') and s.endswith(']') and not (s.startswith("['") and s.endswith("']")) and not (s.startswith('["') and s.endswith('"]')):
|
|
||||||
# Extract the content inside square brackets and split by comma
|
|
||||||
content = s[1:-1].split(',')
|
|
||||||
# Add single quotes around each item and join them back together with commas
|
|
||||||
new_content = ', '.join(f"'{item.strip()}'" for item in content)
|
|
||||||
# Replace the old content with the new content
|
|
||||||
s = '[' + new_content + ']'
|
|
||||||
return s
|
|
||||||
|
|
||||||
@MethodHook
|
@MethodHook
|
||||||
def _retry_function(self, function, max_retries, backoff_num, *args):
|
def _retry_function(self, function, max_retries, backoff_num, *args):
|
||||||
#Retry openai requests
|
#Retry openai requests
|
||||||
|
@ -49,32 +49,45 @@ def _getcwd(words, option, folderonly=False):
|
|||||||
return pathstrings
|
return pathstrings
|
||||||
|
|
||||||
def _get_plugins(which, defaultdir):
|
def _get_plugins(which, defaultdir):
|
||||||
enabled_files = []
|
# Path to core_plugins relative to this script
|
||||||
disabled_files = []
|
core_path = os.path.dirname(os.path.realpath(__file__)) + "/core_plugins"
|
||||||
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])
|
|
||||||
|
|
||||||
|
def get_plugins_from_directory(directory):
|
||||||
|
enabled_files = []
|
||||||
|
disabled_files = []
|
||||||
|
all_plugins = {}
|
||||||
|
# Iterate over all files in the specified folder
|
||||||
|
if os.path.exists(directory):
|
||||||
|
for file in os.listdir(directory):
|
||||||
|
# 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(directory, 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])
|
||||||
|
return enabled_files, disabled_files, all_plugins
|
||||||
|
|
||||||
|
# Get plugins from both directories
|
||||||
|
user_enabled, user_disabled, user_all_plugins = get_plugins_from_directory(defaultdir + "/plugins")
|
||||||
|
core_enabled, core_disabled, core_all_plugins = get_plugins_from_directory(core_path)
|
||||||
|
|
||||||
|
# Combine the results from user and core plugins
|
||||||
|
enabled_files = user_enabled
|
||||||
|
disabled_files = user_disabled
|
||||||
|
all_plugins = {**user_all_plugins, **core_all_plugins} # Merge dictionaries
|
||||||
|
|
||||||
|
# Return based on the command
|
||||||
if which == "--disable":
|
if which == "--disable":
|
||||||
return enabled_files
|
return enabled_files
|
||||||
elif which == "--enable":
|
elif which == "--enable":
|
||||||
return disabled_files
|
return disabled_files
|
||||||
elif which in ["--del", "--update"]:
|
elif which in ["--del", "--update"]:
|
||||||
all_files.extend(enabled_files)
|
all_files = enabled_files + disabled_files
|
||||||
all_files.extend(disabled_files)
|
|
||||||
return all_files
|
return all_files
|
||||||
elif which == "all":
|
elif which == "all":
|
||||||
return all_plugins
|
return all_plugins
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
home = os.path.expanduser("~")
|
home = os.path.expanduser("~")
|
||||||
defaultdir = home + '/.config/conn'
|
defaultdir = home + '/.config/conn'
|
||||||
@ -144,7 +157,7 @@ def main():
|
|||||||
if words[0] in ["--rm", "--del", "-r", "--mod", "--edit", "-e", "--show", "-s", "mv", "move", "cp", "copy"]:
|
if words[0] in ["--rm", "--del", "-r", "--mod", "--edit", "-e", "--show", "-s", "mv", "move", "cp", "copy"]:
|
||||||
strings.extend(nodes)
|
strings.extend(nodes)
|
||||||
if words[0] == "plugin":
|
if words[0] == "plugin":
|
||||||
strings = ["--help", "--add", "--update", "--del", "--enable", "--disable"]
|
strings = ["--help", "--add", "--update", "--del", "--enable", "--disable", "--list"]
|
||||||
if words[0] in ["run", "import", "export"]:
|
if words[0] in ["run", "import", "export"]:
|
||||||
strings = ["--help"]
|
strings = ["--help"]
|
||||||
if words[0] == "export":
|
if words[0] == "export":
|
||||||
|
@ -4,6 +4,7 @@ import json
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from Crypto.PublicKey import RSA
|
from Crypto.PublicKey import RSA
|
||||||
|
from Crypto.Cipher import PKCS1_OAEP
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from .hooks import MethodHook, ClassHook
|
from .hooks import MethodHook, ClassHook
|
||||||
@ -392,3 +393,32 @@ class configfile:
|
|||||||
nodes.extend(layer3)
|
nodes.extend(layer3)
|
||||||
return nodes
|
return nodes
|
||||||
|
|
||||||
|
@MethodHook
|
||||||
|
def encrypt(self, password, keyfile=None):
|
||||||
|
'''
|
||||||
|
Encrypts password using RSA keyfile
|
||||||
|
|
||||||
|
### Parameters:
|
||||||
|
|
||||||
|
- password (str): Plaintext password to encrypt.
|
||||||
|
|
||||||
|
### Optional Parameters:
|
||||||
|
|
||||||
|
- keyfile (str): Path/file to keyfile. Default is config keyfile.
|
||||||
|
|
||||||
|
|
||||||
|
### Returns:
|
||||||
|
|
||||||
|
str: Encrypted password.
|
||||||
|
|
||||||
|
'''
|
||||||
|
if keyfile is None:
|
||||||
|
keyfile = self.key
|
||||||
|
with open(keyfile) as f:
|
||||||
|
key = RSA.import_key(f.read())
|
||||||
|
f.close()
|
||||||
|
publickey = key.publickey()
|
||||||
|
encryptor = PKCS1_OAEP.new(publickey)
|
||||||
|
password = encryptor.encrypt(password.encode("utf-8"))
|
||||||
|
return str(password)
|
||||||
|
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
#Imports
|
#Imports
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from Crypto.PublicKey import RSA
|
|
||||||
from Crypto.Cipher import PKCS1_OAEP
|
|
||||||
import ast
|
import ast
|
||||||
import argparse
|
import argparse
|
||||||
import sys
|
import sys
|
||||||
@ -1285,7 +1283,7 @@ class connapp:
|
|||||||
passa = inquirer.prompt(passq)
|
passa = inquirer.prompt(passq)
|
||||||
if passa == None:
|
if passa == None:
|
||||||
return False
|
return False
|
||||||
answer["password"] = self.encrypt(passa["password"])
|
answer["password"] = self.config.encrypt(passa["password"])
|
||||||
elif answer["password"] == "Profiles":
|
elif answer["password"] == "Profiles":
|
||||||
passq = [(inquirer.Text("password", message="Set a @profile or a comma separated list of @profiles", validate=self._pass_validation))]
|
passq = [(inquirer.Text("password", message="Set a @profile or a comma separated list of @profiles", validate=self._pass_validation))]
|
||||||
passa = inquirer.prompt(passq)
|
passa = inquirer.prompt(passq)
|
||||||
@ -1355,7 +1353,7 @@ class connapp:
|
|||||||
return False
|
return False
|
||||||
if "password" in answer.keys():
|
if "password" in answer.keys():
|
||||||
if answer["password"] != "":
|
if answer["password"] != "":
|
||||||
answer["password"] = self.encrypt(answer["password"])
|
answer["password"] = self.config.encrypt(answer["password"])
|
||||||
if "tags" in answer.keys() and answer["tags"]:
|
if "tags" in answer.keys() and answer["tags"]:
|
||||||
answer["tags"] = ast.literal_eval(answer["tags"])
|
answer["tags"] = ast.literal_eval(answer["tags"])
|
||||||
result = {**answer, **profile}
|
result = {**answer, **profile}
|
||||||
@ -1383,7 +1381,7 @@ class connapp:
|
|||||||
if answer["password"] == "Local Password":
|
if answer["password"] == "Local Password":
|
||||||
passq = [inquirer.Password("password", message="Set Password")]
|
passq = [inquirer.Password("password", message="Set Password")]
|
||||||
passa = inquirer.prompt(passq)
|
passa = inquirer.prompt(passq)
|
||||||
answer["password"] = self.encrypt(passa["password"])
|
answer["password"] = self.config.encrypt(passa["password"])
|
||||||
elif answer["password"] == "Profiles":
|
elif answer["password"] == "Profiles":
|
||||||
passq = [(inquirer.Text("password", message="Set a @profile or a comma separated list of @profiles", validate=self._pass_validation))]
|
passq = [(inquirer.Text("password", message="Set a @profile or a comma separated list of @profiles", validate=self._pass_validation))]
|
||||||
passa = inquirer.prompt(passq)
|
passa = inquirer.prompt(passq)
|
||||||
@ -1550,31 +1548,3 @@ tasks:
|
|||||||
output: null
|
output: null
|
||||||
...'''
|
...'''
|
||||||
|
|
||||||
def encrypt(self, password, keyfile=None):
|
|
||||||
'''
|
|
||||||
Encrypts password using RSA keyfile
|
|
||||||
|
|
||||||
### Parameters:
|
|
||||||
|
|
||||||
- password (str): Plaintext password to encrypt.
|
|
||||||
|
|
||||||
### Optional Parameters:
|
|
||||||
|
|
||||||
- keyfile (str): Path/file to keyfile. Default is config keyfile.
|
|
||||||
|
|
||||||
|
|
||||||
### Returns:
|
|
||||||
|
|
||||||
str: Encrypted password.
|
|
||||||
|
|
||||||
'''
|
|
||||||
if keyfile is None:
|
|
||||||
keyfile = self.config.key
|
|
||||||
with open(keyfile) as f:
|
|
||||||
key = RSA.import_key(f.read())
|
|
||||||
f.close()
|
|
||||||
publickey = key.publickey()
|
|
||||||
encryptor = PKCS1_OAEP.new(publickey)
|
|
||||||
password = encryptor.encrypt(password.encode("utf-8"))
|
|
||||||
return str(password)
|
|
||||||
|
|
||||||
|
@ -35,21 +35,34 @@ class sync:
|
|||||||
# The file token.json stores the user's access and refresh tokens.
|
# The file token.json stores the user's access and refresh tokens.
|
||||||
if os.path.exists(self.token_file):
|
if os.path.exists(self.token_file):
|
||||||
creds = Credentials.from_authorized_user_file(self.token_file, self.scopes)
|
creds = Credentials.from_authorized_user_file(self.token_file, self.scopes)
|
||||||
|
|
||||||
# If there are no valid credentials available, let the user log in.
|
try:
|
||||||
if not creds or not creds.valid:
|
# If there are no valid credentials available, let the user log in.
|
||||||
if creds and creds.expired and creds.refresh_token:
|
if not creds or not creds.valid:
|
||||||
creds.refresh(Request())
|
if creds and creds.expired and creds.refresh_token:
|
||||||
else:
|
creds.refresh(Request())
|
||||||
flow = InstalledAppFlow.from_client_secrets_file(
|
else:
|
||||||
self.google_client, self.scopes)
|
flow = InstalledAppFlow.from_client_secrets_file(
|
||||||
creds = flow.run_local_server(port=0, access_type='offline')
|
self.google_client, self.scopes)
|
||||||
|
creds = flow.run_local_server(port=0, access_type='offline')
|
||||||
# Save the credentials for the next run
|
|
||||||
|
# Save the credentials for the next run
|
||||||
|
with open(self.token_file, 'w') as token:
|
||||||
|
token.write(creds.to_json())
|
||||||
|
|
||||||
|
print("Logged in successfully.")
|
||||||
|
|
||||||
|
except RefreshError as e:
|
||||||
|
# If refresh fails, delete the invalid token file and start a new login flow
|
||||||
|
if os.path.exists(self.token_file):
|
||||||
|
os.remove(self.token_file)
|
||||||
|
print("Existing token was invalid and has been removed. Please log in again.")
|
||||||
|
flow = InstalledAppFlow.from_client_secrets_file(
|
||||||
|
self.google_client, self.scopes)
|
||||||
|
creds = flow.run_local_server(port=0, access_type='offline')
|
||||||
with open(self.token_file, 'w') as token:
|
with open(self.token_file, 'w') as token:
|
||||||
token.write(creds.to_json())
|
token.write(creds.to_json())
|
||||||
|
print("Logged in successfully after re-authentication.")
|
||||||
print("Logged in successfully.")
|
|
||||||
|
|
||||||
def logout(self):
|
def logout(self):
|
||||||
if os.path.exists(self.token_file):
|
if os.path.exists(self.token_file):
|
||||||
@ -300,6 +313,8 @@ class sync:
|
|||||||
if self.check_login_status() == True:
|
if self.check_login_status() == True:
|
||||||
if not kwargs["result"]:
|
if not kwargs["result"]:
|
||||||
self.compress_and_upload()
|
self.compress_and_upload()
|
||||||
|
else:
|
||||||
|
print("Sync cannot be performed. Please check your login status.")
|
||||||
return kwargs["result"]
|
return kwargs["result"]
|
||||||
|
|
||||||
def config_listener_pre(self, *args, **kwargs):
|
def config_listener_pre(self, *args, **kwargs):
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
#Imports
|
#Imports
|
||||||
from functools import wraps, partial
|
from functools import wraps, partial, update_wrapper
|
||||||
|
|
||||||
#functions and classes
|
#functions and classes
|
||||||
|
|
||||||
@ -59,6 +59,7 @@ class ClassHook:
|
|||||||
"""Decorator class to enable Class Modifying"""
|
"""Decorator class to enable Class Modifying"""
|
||||||
def __init__(self, cls):
|
def __init__(self, cls):
|
||||||
self.cls = cls
|
self.cls = cls
|
||||||
|
update_wrapper(self, cls, updated=()) # Update wrapper without changing underlying items
|
||||||
# Initialize deferred class hooks if they don't already exist
|
# Initialize deferred class hooks if they don't already exist
|
||||||
if not hasattr(cls, 'deferred_class_hooks'):
|
if not hasattr(cls, 'deferred_class_hooks'):
|
||||||
cls.deferred_class_hooks = []
|
cls.deferred_class_hooks = []
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user