upload pyapp
This commit is contained in:
BIN
__pycache__/setup.cpython-39.pyc
Normal file
BIN
__pycache__/setup.cpython-39.pyc
Normal file
Binary file not shown.
9
pyapp.egg-info/PKG-INFO
Normal file
9
pyapp.egg-info/PKG-INFO
Normal file
@@ -0,0 +1,9 @@
|
||||
Metadata-Version: 2.1
|
||||
Name: pyapp
|
||||
Version: 0.1
|
||||
Summary: UNKNOWN
|
||||
License: UNKNOWN
|
||||
Platform: UNKNOWN
|
||||
|
||||
UNKNOWN
|
||||
|
||||
41
pyapp.egg-info/SOURCES.txt
Normal file
41
pyapp.egg-info/SOURCES.txt
Normal file
@@ -0,0 +1,41 @@
|
||||
setup.py
|
||||
pyapp/__init__.py
|
||||
pyapp/application.py
|
||||
pyapp/boot.py
|
||||
pyapp/component.py
|
||||
pyapp/component_factory.py
|
||||
pyapp/config.py
|
||||
pyapp/namespace.py
|
||||
pyapp/runner.py
|
||||
pyapp/status.py
|
||||
pyapp/stereotype.py
|
||||
pyapp.egg-info/PKG-INFO
|
||||
pyapp.egg-info/SOURCES.txt
|
||||
pyapp.egg-info/dependency_links.txt
|
||||
pyapp.egg-info/entry_points.txt
|
||||
pyapp.egg-info/requires.txt
|
||||
pyapp.egg-info/top_level.txt
|
||||
pyapp/templates/__init__.py
|
||||
pyapp/templates/application.py
|
||||
pyapp/templates/config.py
|
||||
pyapp/ui/__init__.py
|
||||
pyapp/ui/client/__init__.py
|
||||
pyapp/ui/client/index.html
|
||||
pyapp/ui/client/static/css/app.f0ad959581daf87e4158e4f8da1285ba.css
|
||||
pyapp/ui/client/static/css/app.f0ad959581daf87e4158e4f8da1285ba.css.map
|
||||
pyapp/ui/client/static/fonts/ionicons.143146f.woff2
|
||||
pyapp/ui/client/static/fonts/ionicons.99ac330.woff
|
||||
pyapp/ui/client/static/fonts/ionicons.d535a25.ttf
|
||||
pyapp/ui/client/static/img/ionicons.a2c4a26.svg
|
||||
pyapp/ui/client/static/js/app.d8ab046c4c34bfc5bd54.js
|
||||
pyapp/ui/client/static/js/app.d8ab046c4c34bfc5bd54.js.map
|
||||
pyapp/ui/client/static/js/manifest.2ae2e69a05c33dfc65f8.js
|
||||
pyapp/ui/client/static/js/manifest.2ae2e69a05c33dfc65f8.js.map
|
||||
pyapp/ui/client/static/js/vendor.9f7b4785a30f0533ee08.js
|
||||
pyapp/ui/client/static/js/vendor.9f7b4785a30f0533ee08.js.map
|
||||
pyapp/ui/server/__init__.py
|
||||
pyapp/ui/server/app.py
|
||||
pyapp/utils/__init__.py
|
||||
pyapp/utils/log.py
|
||||
pyapp/utils/project.py
|
||||
pyapp/utils/timer.py
|
||||
1
pyapp.egg-info/dependency_links.txt
Normal file
1
pyapp.egg-info/dependency_links.txt
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
2
pyapp.egg-info/entry_points.txt
Normal file
2
pyapp.egg-info/entry_points.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
[console_scripts]
|
||||
pa = pyapp.boot:main
|
||||
5
pyapp.egg-info/requires.txt
Normal file
5
pyapp.egg-info/requires.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
pyyaml
|
||||
psutil
|
||||
GPUtil
|
||||
flask
|
||||
flask_cors
|
||||
1
pyapp.egg-info/top_level.txt
Normal file
1
pyapp.egg-info/top_level.txt
Normal file
@@ -0,0 +1 @@
|
||||
pyapp
|
||||
6
pyapp/__init__.py
Normal file
6
pyapp/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
"""
|
||||
PyApp - A Python application framework
|
||||
"""
|
||||
|
||||
__version__ = "0.1"
|
||||
__author__ = "Hofee"
|
||||
BIN
pyapp/__pycache__/__init__.cpython-39.pyc
Normal file
BIN
pyapp/__pycache__/__init__.cpython-39.pyc
Normal file
Binary file not shown.
BIN
pyapp/__pycache__/application.cpython-39.pyc
Normal file
BIN
pyapp/__pycache__/application.cpython-39.pyc
Normal file
Binary file not shown.
BIN
pyapp/__pycache__/boot.cpython-39.pyc
Normal file
BIN
pyapp/__pycache__/boot.cpython-39.pyc
Normal file
Binary file not shown.
BIN
pyapp/__pycache__/component.cpython-39.pyc
Normal file
BIN
pyapp/__pycache__/component.cpython-39.pyc
Normal file
Binary file not shown.
BIN
pyapp/__pycache__/component_factory.cpython-39.pyc
Normal file
BIN
pyapp/__pycache__/component_factory.cpython-39.pyc
Normal file
Binary file not shown.
BIN
pyapp/__pycache__/config.cpython-39.pyc
Normal file
BIN
pyapp/__pycache__/config.cpython-39.pyc
Normal file
Binary file not shown.
BIN
pyapp/__pycache__/namespace.cpython-39.pyc
Normal file
BIN
pyapp/__pycache__/namespace.cpython-39.pyc
Normal file
Binary file not shown.
BIN
pyapp/__pycache__/runner.cpython-39.pyc
Normal file
BIN
pyapp/__pycache__/runner.cpython-39.pyc
Normal file
Binary file not shown.
BIN
pyapp/__pycache__/status.cpython-39.pyc
Normal file
BIN
pyapp/__pycache__/status.cpython-39.pyc
Normal file
Binary file not shown.
BIN
pyapp/__pycache__/stereotype.cpython-39.pyc
Normal file
BIN
pyapp/__pycache__/stereotype.cpython-39.pyc
Normal file
Binary file not shown.
21
pyapp/application.py
Normal file
21
pyapp/application.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from pyapp.utils.log import Log
|
||||
|
||||
application_class = {}
|
||||
def PyappApplication(arg=None):
|
||||
if callable(arg):
|
||||
cls = arg
|
||||
if "default" in application_class:
|
||||
Log.error("Multiple classes annotated with default @PyappApplication, require a 'name' parameter.", True)
|
||||
application_class["default"] = cls
|
||||
return cls
|
||||
|
||||
else:
|
||||
name = arg
|
||||
def decorator(cls):
|
||||
if name is None:
|
||||
raise Log.error("The 'name' parameter is required when using @PyappApplication with arguments.", True)
|
||||
if name in application_class:
|
||||
raise Log.error(f"Multiple classes annotated with @PyappApplication with the same name '{name}' found.", True)
|
||||
application_class[name] = cls
|
||||
return cls
|
||||
return decorator
|
||||
110
pyapp/boot.py
Normal file
110
pyapp/boot.py
Normal file
@@ -0,0 +1,110 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
from pyapp.application import application_class
|
||||
from pyapp.stereotype import get_all_component_classes, get_all_component_comments
|
||||
from pyapp.utils.log import Log
|
||||
from pyapp.utils.timer import Timer
|
||||
from pyapp.utils.project import Project
|
||||
from pyapp.templates.application import template as app_template
|
||||
from pyapp.templates.config import template as config_template
|
||||
from pyapp.ui.server.app import app
|
||||
|
||||
|
||||
def run():
|
||||
root_path = os.getcwd()
|
||||
Project.scan_project(root_path)
|
||||
|
||||
app_name = "default"
|
||||
if len(application_class) == 0:
|
||||
Log.error("No class annotated with @PyappApplication found.", True)
|
||||
if len(sys.argv) < 3 and "default" not in application_class:
|
||||
Log.error("No default @PyappApplication found. Please specify the 'name' parameter.", True)
|
||||
if len(sys.argv) == 3:
|
||||
app_name = sys.argv[2]
|
||||
|
||||
app_cls = application_class.get(app_name)
|
||||
|
||||
if app_cls is None:
|
||||
Log.error(f"No class annotated with @PyappApplication found with the name '{app_name}'.", True)
|
||||
|
||||
if not hasattr(app_cls, "start"):
|
||||
Log.error("The class annotated with @PyappApplication should have a 'start' method.", True)
|
||||
|
||||
Log.info(f"Application '{app_cls.__name__}' started.")
|
||||
timer = Timer(f"Application({app_name})")
|
||||
|
||||
timer.start()
|
||||
app_cls.start()
|
||||
timer.stop()
|
||||
Log.info(timer.get_elasped_time_str(Timer.HOURS))
|
||||
Log.success("Application finished.")
|
||||
|
||||
|
||||
def init():
|
||||
Log.info("Initializing Pyapp project.")
|
||||
root_path = os.getcwd()
|
||||
if len(os.listdir(root_path)) > 0:
|
||||
Log.error("Current directory is not empty. Please provide an empty directory.")
|
||||
else:
|
||||
with open(os.path.join(root_path, "application.py"), "w") as file:
|
||||
file.write(app_template)
|
||||
with open(os.path.join(root_path, "config.yaml"), "w") as file:
|
||||
file.write(config_template)
|
||||
|
||||
Log.success("Pyapp project initialized.")
|
||||
Log.info("Now you can create your components and run the application.")
|
||||
|
||||
def scan():
|
||||
root_path = os.getcwd()
|
||||
Project.scan_project(root_path)
|
||||
comments = get_all_component_comments()
|
||||
Log.info("Components detected in the project:")
|
||||
for stereotype, classes in get_all_component_classes().items():
|
||||
if len(classes) == 0:
|
||||
continue
|
||||
Log.info(f" {stereotype}:")
|
||||
for name, cls in classes.items():
|
||||
comment = comments[stereotype].get(name)
|
||||
if comment is not None:
|
||||
Log.warning(f" - {name}: {cls.__module__}.{cls.__name__} ({comment})")
|
||||
else:
|
||||
Log.success(f" - {name}: {cls.__module__}.{cls.__name__}")
|
||||
|
||||
Log.info("Applications detected in the project:")
|
||||
for app_name, app_cls in application_class.items():
|
||||
Log.success(f" - {app_name}: {app_cls.__module__}.{app_cls.__name__}")
|
||||
Log.success("Scan completed.")
|
||||
|
||||
def ui():
|
||||
port = 5000
|
||||
if len(sys.argv) == 3:
|
||||
port = int(sys.argv[2])
|
||||
Log.success(f"Pyapp UI server started at http://localhost:{port}")
|
||||
app.run(port=port, host="0.0.0.0")
|
||||
|
||||
|
||||
def help():
|
||||
Log.info("Pyapp commands:")
|
||||
Log.info(" init: Initialize a new Pyapp project in the current directory.")
|
||||
Log.info(" run [name]: Run the Pyapp application with the specified name. If no name is provided, the default application will be run.")
|
||||
Log.info(" scan: Scan the project for Pyapp components.")
|
||||
Log.info(" ui [port]: Start the Pyapp UI server. If no port is provided, the default port 5000 will be used.")
|
||||
Log.info(" help: Display this help message.")
|
||||
|
||||
def main():
|
||||
if len(sys.argv) > 1:
|
||||
if sys.argv[1] == "init":
|
||||
init()
|
||||
elif sys.argv[1] == "run":
|
||||
run()
|
||||
elif sys.argv[1] == "scan":
|
||||
scan()
|
||||
elif sys.argv[1] == "ui":
|
||||
ui()
|
||||
elif sys.argv[1] == "help":
|
||||
help()
|
||||
else:
|
||||
Log.error("Invalid command: " + sys.argv[1] + ". Use 'pyapp help' for help.")
|
||||
else:
|
||||
Log.error("Please provide a command to run the application.")
|
||||
21
pyapp/component.py
Normal file
21
pyapp/component.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from pyapp.utils.log import Log
|
||||
|
||||
class Component:
|
||||
TYPE: str
|
||||
NAME: str
|
||||
|
||||
def get_name(self):
|
||||
return self.NAME
|
||||
|
||||
def get_type(self):
|
||||
return self.TYPE
|
||||
|
||||
def get_config(self):
|
||||
return self.config
|
||||
|
||||
def print(self):
|
||||
Log.blue("Component Information")
|
||||
Log.blue(f"- Type: {self.TYPE}")
|
||||
Log.blue(f"- Name: {self.NAME}")
|
||||
Log.blue(f"- Config: \n\t{self.config}")
|
||||
|
||||
27
pyapp/component_factory.py
Normal file
27
pyapp/component_factory.py
Normal file
@@ -0,0 +1,27 @@
|
||||
from pyapp.component import Component
|
||||
from pyapp.stereotype import *
|
||||
from pyapp.utils.log import Log
|
||||
from pyapp.config import ConfigManager
|
||||
|
||||
class ComponentFactory:
|
||||
@staticmethod
|
||||
def create(component_type: str, name: str) -> Component:
|
||||
component_classes = get_component_classes(component_type=component_type)
|
||||
if component_classes is None:
|
||||
Log.error(f"Unsupported component type: {component_type}", True)
|
||||
|
||||
if component_type == "COMPONENT":
|
||||
config = ConfigManager.get(component_type, name)
|
||||
cls = component_classes[config["source"]]
|
||||
component_obj = cls(config)
|
||||
component_obj.NAME = name
|
||||
component_obj.TYPE = component_type
|
||||
return component_obj
|
||||
|
||||
if name not in component_classes:
|
||||
Log.error(f"Unsupported component name: {name}", True)
|
||||
|
||||
cls = component_classes[name]
|
||||
config = ConfigManager.get(component_type, name)
|
||||
return cls(config)
|
||||
|
||||
59
pyapp/config.py
Normal file
59
pyapp/config.py
Normal file
@@ -0,0 +1,59 @@
|
||||
import argparse
|
||||
import os.path
|
||||
import shutil
|
||||
import yaml
|
||||
from pyapp.utils.log import Log
|
||||
|
||||
class ConfigManager:
|
||||
config = None
|
||||
config_path = None
|
||||
|
||||
@staticmethod
|
||||
def get(*args):
|
||||
result = ConfigManager.config
|
||||
for arg in args:
|
||||
result = result[arg]
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def load_config_with(config_file_path):
|
||||
ConfigManager.config_path = config_file_path
|
||||
if not os.path.exists(ConfigManager.config_path):
|
||||
raise ValueError(f"Config file <{config_file_path}> does not exist")
|
||||
with open(config_file_path, 'r') as file:
|
||||
ConfigManager.config = yaml.safe_load(file)
|
||||
|
||||
@staticmethod
|
||||
def backup_config_to(target_config_dir, file_name, prefix="config"):
|
||||
file_name = f"__{prefix}_{file_name}.yaml"
|
||||
target_config_file_path = str(os.path.join(target_config_dir, file_name))
|
||||
shutil.copy(ConfigManager.config_path, target_config_file_path)
|
||||
|
||||
@staticmethod
|
||||
def load_config():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--config', type=str, default='', help='config file path')
|
||||
args = parser.parse_args()
|
||||
if args.config:
|
||||
ConfigManager.load_config_with(args.config)
|
||||
|
||||
@staticmethod
|
||||
def print_config(key: str = None, group: dict = None, level=0):
|
||||
table_size = 80
|
||||
if key and group:
|
||||
value = group[key]
|
||||
if type(value) is dict:
|
||||
Log.blue("\t" * level + f"+-{key}:")
|
||||
for k in value:
|
||||
ConfigManager.print_config(k, value, level=level + 1)
|
||||
else:
|
||||
Log.blue("\t" * level + f"| {key}: {value}")
|
||||
elif key:
|
||||
ConfigManager.print_config(key, ConfigManager.config, level=level)
|
||||
else:
|
||||
Log.blue("+" + "-" * table_size + "+")
|
||||
Log.blue(f"| Configurations in <{ConfigManager.config_path}>:")
|
||||
Log.blue("+" + "-" * table_size + "+")
|
||||
for key in ConfigManager.config:
|
||||
ConfigManager.print_config(key, level=level + 1)
|
||||
Log.blue("+" + "-" * table_size + "+")
|
||||
15
pyapp/namespace.py
Normal file
15
pyapp/namespace.py
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
class Stereotype:
|
||||
APPLICATION:str = "application"
|
||||
RUNNER:str = "runner"
|
||||
FACTORY:str = "factory"
|
||||
SERVER:str = "server"
|
||||
CLIENT:str = "client"
|
||||
|
||||
class LogType:
|
||||
INFO:str = "info"
|
||||
ERROR:str = "error"
|
||||
WARNING:str = "warning"
|
||||
SUCCESS:str = "success"
|
||||
DEBUG:str = "debug"
|
||||
TERMINATE:str = "terminate"
|
||||
51
pyapp/runner.py
Normal file
51
pyapp/runner.py
Normal file
@@ -0,0 +1,51 @@
|
||||
import os
|
||||
import time
|
||||
from abc import abstractmethod, ABC
|
||||
|
||||
from pyapp.config import ConfigManager
|
||||
from pyapp.utils.log import Log
|
||||
|
||||
class Runner(ABC):
|
||||
|
||||
@abstractmethod
|
||||
def __init__(self, config_path):
|
||||
ConfigManager.load_config_with(config_path)
|
||||
ConfigManager.print_config()
|
||||
self.config = ConfigManager.get("runner")
|
||||
self.workspace_config = self.config["workspace"]
|
||||
self.workspace_path = os.path.join(self.workspace_config["root_dir"], self.workspace_config["name"])
|
||||
lt = time.localtime()
|
||||
self.file_name = f"{lt.tm_year}_{lt.tm_mon}_{lt.tm_mday}_{lt.tm_hour}h{lt.tm_min}m{lt.tm_sec}s"
|
||||
self.load_workspace()
|
||||
|
||||
@abstractmethod
|
||||
def run(self):
|
||||
pass
|
||||
|
||||
def load_workspace(self, backup_name=None):
|
||||
if not os.path.exists(self.workspace_path):
|
||||
Log.info(f"workspace {self.workspace_config['name']} does not exists.")
|
||||
self.create_workspace(backup_name)
|
||||
else:
|
||||
Log.info(f"workspace {self.workspace_config['name']}")
|
||||
backup_config_dir = os.path.join(str(self.workspace_path), "configs")
|
||||
if not os.path.exists(backup_config_dir):
|
||||
os.makedirs(backup_config_dir)
|
||||
ConfigManager.backup_config_to(backup_config_dir, self.file_name, backup_name)
|
||||
|
||||
def create_workspace(self, backup_name=None):
|
||||
Log.info("creating workspace: " + self.workspace_config["name"])
|
||||
os.makedirs(self.workspace_path)
|
||||
backup_config_dir = os.path.join(str(self.workspace_path), "configs")
|
||||
os.makedirs(backup_config_dir)
|
||||
ConfigManager.backup_config_to(backup_config_dir, self.file_name, backup_name)
|
||||
log_dir = os.path.join(str(self.workspace_path), "logs")
|
||||
os.makedirs(log_dir)
|
||||
cache_dir = os.path.join(str(self.workspace_path), "cache")
|
||||
os.makedirs(cache_dir)
|
||||
|
||||
def print_info(self):
|
||||
table_size = 80
|
||||
Log.blue("+" + "-" * table_size + "+")
|
||||
Log.blue(f"| Workspace <{self.workspace_config['name']}>")
|
||||
Log.blue("+" + "-" * table_size + "+")
|
||||
56
pyapp/status.py
Normal file
56
pyapp/status.py
Normal file
@@ -0,0 +1,56 @@
|
||||
|
||||
class StatusManager:
|
||||
def __init__(self):
|
||||
self.running_app = {}
|
||||
self.last_status = {}
|
||||
self.curr_status = {}
|
||||
self.progress = {}
|
||||
self.log = []
|
||||
|
||||
def is_running(self):
|
||||
return len(self.running_app) > 0
|
||||
|
||||
def run_app(self, app_name, app):
|
||||
self.running_app[app_name] = app
|
||||
|
||||
def end_app(self, app_name):
|
||||
self.running_app.pop(app_name)
|
||||
|
||||
def set_status(self, app_name, runner_name, key, value):
|
||||
self.last_status = self.curr_status
|
||||
if app_name not in self.curr_status:
|
||||
self.curr_status[app_name] = {}
|
||||
if runner_name not in self.curr_status[app_name]:
|
||||
self.curr_status[app_name][runner_name] = {}
|
||||
self.curr_status[app_name][runner_name][key] = value
|
||||
|
||||
def set_progress(self, app_name, runner_name, key, curr_value, max_value):
|
||||
if app_name not in self.progress:
|
||||
self.progress[app_name] = {}
|
||||
if runner_name not in self.progress[app_name]:
|
||||
self.progress[app_name][runner_name] = {}
|
||||
self.progress[app_name][runner_name][key] = (curr_value, max_value)
|
||||
|
||||
def get_status(self):
|
||||
return self.curr_status
|
||||
|
||||
def get_progress(self):
|
||||
return self.progress
|
||||
|
||||
def add_log(self, time_str, log_type, message):
|
||||
self.log.append((time_str, log_type, message))
|
||||
|
||||
def get_log(self):
|
||||
return self.log
|
||||
|
||||
def get_running_apps(self):
|
||||
return list(self.running_app.keys())
|
||||
|
||||
def get_last_status(self):
|
||||
return self.last_status
|
||||
|
||||
def reset_status(self):
|
||||
self.last_status = {}
|
||||
self.curr_status = {}
|
||||
|
||||
status_manager = StatusManager()
|
||||
106
pyapp/stereotype.py
Normal file
106
pyapp/stereotype.py
Normal file
@@ -0,0 +1,106 @@
|
||||
import inspect
|
||||
|
||||
from pyapp.component import Component
|
||||
from pyapp.utils.log import Log
|
||||
|
||||
|
||||
def ensure_component_subclass(cls, type_name, name):
|
||||
if not issubclass(cls, Component):
|
||||
new_cls = type(cls.__name__, (Component, cls), {
|
||||
**cls.__dict__,
|
||||
"TYPE": type_name,
|
||||
"NAME": name
|
||||
})
|
||||
new_cls.__original_class__ = cls
|
||||
else:
|
||||
new_cls = cls
|
||||
for method_name, method in inspect.getmembers(cls, predicate=inspect.isfunction):
|
||||
if getattr(method, "__isabstractmethod__", False):
|
||||
Log.error(f"Component <{name}> contains abstract method <{method_name}>.", True)
|
||||
|
||||
return cls
|
||||
|
||||
|
||||
all_component_classes = {}
|
||||
all_component_comments = {}
|
||||
|
||||
def register_component_type(component_type, comment=None):
|
||||
all_component_classes[component_type] = {}
|
||||
all_component_comments[component_type] = comment
|
||||
|
||||
# --- Classes --- #
|
||||
|
||||
def component(component_type, component_name, comment=None):
|
||||
def decorator(cls):
|
||||
cls = ensure_component_subclass(cls, "COMPONENT", component_name)
|
||||
if component_type not in all_component_classes:
|
||||
register_component_type(component_type, comment)
|
||||
all_component_classes[component_type][component_name] = cls
|
||||
all_component_comments[component_type][component_name] = comment
|
||||
return cls
|
||||
return decorator
|
||||
|
||||
runner_classes = {}
|
||||
runner_comments = {}
|
||||
def runner(runner_name, comment=None):
|
||||
def decorator(cls):
|
||||
if not hasattr(cls, 'run') or not callable(getattr(cls, 'run')):
|
||||
Log.error(f"runner <{cls.__name__}> must implement a 'run' method", True)
|
||||
cls = ensure_component_subclass(cls, "RUNNER", runner_name)
|
||||
runner_classes[runner_name] = cls
|
||||
runner_comments[runner_name] = comment
|
||||
return cls
|
||||
return decorator
|
||||
|
||||
factory_classes = {}
|
||||
factory_comments = {}
|
||||
def factory(factory_name, comment=None):
|
||||
def decorator(cls):
|
||||
if not hasattr(cls, 'create') or not callable(getattr(cls, 'create')):
|
||||
Log.error(f"factory <{cls.__name__}> must implement a 'create' method", True)
|
||||
|
||||
cls = ensure_component_subclass(cls, "FACTORY", factory_name)
|
||||
factory_classes[factory_name] = cls
|
||||
factory_comments[factory_name] = comment
|
||||
return cls
|
||||
return decorator
|
||||
|
||||
client_classes = {}
|
||||
client_comments = {}
|
||||
def client(client_name, comment=None):
|
||||
def decorator(cls):
|
||||
cls = ensure_component_subclass(cls, "CLIENT", client_name)
|
||||
client_classes[client_name] = cls
|
||||
client_comments[client_name] = comment
|
||||
return cls
|
||||
return decorator
|
||||
|
||||
server_classes = {}
|
||||
server_comments = {}
|
||||
def server(server_name, comment=None):
|
||||
def decorator(cls):
|
||||
cls = ensure_component_subclass(cls, "SERVER", server_name)
|
||||
if not hasattr(cls, 'host') or not hasattr(cls, 'port'):
|
||||
Log.error(f"server <{cls.__name__}> must implement 'host' and 'port' attributes", True)
|
||||
if not hasattr(cls, 'serve') or not callable(getattr(cls, 'serve')):
|
||||
Log.error(f"server <{cls.__name__}> must implement a 'serve' method", True)
|
||||
server_classes[server_name] = cls
|
||||
server_comments[server_name] = comment
|
||||
return cls
|
||||
return decorator
|
||||
|
||||
# --- Utils --- #
|
||||
|
||||
register_component_type("RUNNER", "Runner")
|
||||
register_component_type("FACTORY", "Factory")
|
||||
register_component_type("CLIENT", "Client")
|
||||
register_component_type("SERVER", "Server")
|
||||
|
||||
def get_all_component_classes():
|
||||
return all_component_classes
|
||||
|
||||
def get_all_component_comments():
|
||||
return all_component_comments
|
||||
|
||||
def get_component_classes(component_type):
|
||||
return all_component_classes.get(component_type, None)
|
||||
3
pyapp/templates/__init__.py
Normal file
3
pyapp/templates/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Template modules for PyApp
|
||||
"""
|
||||
BIN
pyapp/templates/__pycache__/__init__.cpython-39.pyc
Normal file
BIN
pyapp/templates/__pycache__/__init__.cpython-39.pyc
Normal file
Binary file not shown.
BIN
pyapp/templates/__pycache__/application.cpython-39.pyc
Normal file
BIN
pyapp/templates/__pycache__/application.cpython-39.pyc
Normal file
Binary file not shown.
BIN
pyapp/templates/__pycache__/config.cpython-39.pyc
Normal file
BIN
pyapp/templates/__pycache__/config.cpython-39.pyc
Normal file
Binary file not shown.
15
pyapp/templates/application.py
Normal file
15
pyapp/templates/application.py
Normal file
@@ -0,0 +1,15 @@
|
||||
template = """from pyapp.application import PyappApplication
|
||||
from runners import YourCustomRunner
|
||||
|
||||
@PyappApplication("your_application_name")
|
||||
class Application:
|
||||
@staticmethod
|
||||
def start():
|
||||
'''
|
||||
call default or your custom runners here, code will be executed
|
||||
automatically when type "pyapp run" in terminal
|
||||
|
||||
example:
|
||||
YourCustomRunner("path_to_your_config").run()
|
||||
'''
|
||||
"""
|
||||
9
pyapp/templates/config.py
Normal file
9
pyapp/templates/config.py
Normal file
@@ -0,0 +1,9 @@
|
||||
template = """
|
||||
runner:
|
||||
general:
|
||||
|
||||
workspace:
|
||||
name: workspace_name
|
||||
root_dir: "workspaces"
|
||||
|
||||
"""
|
||||
3
pyapp/ui/__init__.py
Normal file
3
pyapp/ui/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
UI modules for PyApp
|
||||
"""
|
||||
BIN
pyapp/ui/__pycache__/__init__.cpython-39.pyc
Normal file
BIN
pyapp/ui/__pycache__/__init__.cpython-39.pyc
Normal file
Binary file not shown.
3
pyapp/ui/client/__init__.py
Normal file
3
pyapp/ui/client/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Client-side UI components for PyApp
|
||||
"""
|
||||
1
pyapp/ui/client/index.html
Normal file
1
pyapp/ui/client/index.html
Normal file
@@ -0,0 +1 @@
|
||||
<!DOCTYPE html><html><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>PyApp Project</title><link href=/static/css/app.f0ad959581daf87e4158e4f8da1285ba.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=/static/js/manifest.2ae2e69a05c33dfc65f8.js></script><script type=text/javascript src=/static/js/vendor.9f7b4785a30f0533ee08.js></script><script type=text/javascript src=/static/js/app.d8ab046c4c34bfc5bd54.js></script></body></html>
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
BIN
pyapp/ui/client/static/fonts/ionicons.143146f.woff2
Normal file
BIN
pyapp/ui/client/static/fonts/ionicons.143146f.woff2
Normal file
Binary file not shown.
BIN
pyapp/ui/client/static/fonts/ionicons.99ac330.woff
Normal file
BIN
pyapp/ui/client/static/fonts/ionicons.99ac330.woff
Normal file
Binary file not shown.
BIN
pyapp/ui/client/static/fonts/ionicons.d535a25.ttf
Normal file
BIN
pyapp/ui/client/static/fonts/ionicons.d535a25.ttf
Normal file
Binary file not shown.
870
pyapp/ui/client/static/img/ionicons.a2c4a26.svg
Normal file
870
pyapp/ui/client/static/img/ionicons.a2c4a26.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 542 KiB |
2
pyapp/ui/client/static/js/app.d8ab046c4c34bfc5bd54.js
Normal file
2
pyapp/ui/client/static/js/app.d8ab046c4c34bfc5bd54.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,2 @@
|
||||
!function(r){var n=window.webpackJsonp;window.webpackJsonp=function(e,u,c){for(var f,i,p,a=0,l=[];a<e.length;a++)i=e[a],o[i]&&l.push(o[i][0]),o[i]=0;for(f in u)Object.prototype.hasOwnProperty.call(u,f)&&(r[f]=u[f]);for(n&&n(e,u,c);l.length;)l.shift()();if(c)for(a=0;a<c.length;a++)p=t(t.s=c[a]);return p};var e={},o={2:0};function t(n){if(e[n])return e[n].exports;var o=e[n]={i:n,l:!1,exports:{}};return r[n].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=r,t.c=e,t.d=function(r,n,e){t.o(r,n)||Object.defineProperty(r,n,{configurable:!1,enumerable:!0,get:e})},t.n=function(r){var n=r&&r.__esModule?function(){return r.default}:function(){return r};return t.d(n,"a",n),n},t.o=function(r,n){return Object.prototype.hasOwnProperty.call(r,n)},t.p="/",t.oe=function(r){throw console.error(r),r}}([]);
|
||||
//# sourceMappingURL=manifest.2ae2e69a05c33dfc65f8.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["webpack:///webpack/bootstrap 43a1e13c45f80b089c40"],"names":["parentJsonpFunction","window","chunkIds","moreModules","executeModules","moduleId","chunkId","result","i","resolves","length","installedChunks","push","Object","prototype","hasOwnProperty","call","modules","shift","__webpack_require__","s","installedModules","2","exports","module","l","m","c","d","name","getter","o","defineProperty","configurable","enumerable","get","n","__esModule","object","property","p","oe","err","console","error"],"mappings":"aACA,IAAAA,EAAAC,OAAA,aACAA,OAAA,sBAAAC,EAAAC,EAAAC,GAIA,IADA,IAAAC,EAAAC,EAAAC,EAAAC,EAAA,EAAAC,KACQD,EAAAN,EAAAQ,OAAoBF,IAC5BF,EAAAJ,EAAAM,GACAG,EAAAL,IACAG,EAAAG,KAAAD,EAAAL,GAAA,IAEAK,EAAAL,GAAA,EAEA,IAAAD,KAAAF,EACAU,OAAAC,UAAAC,eAAAC,KAAAb,EAAAE,KACAY,EAAAZ,GAAAF,EAAAE,IAIA,IADAL,KAAAE,EAAAC,EAAAC,GACAK,EAAAC,QACAD,EAAAS,OAAAT,GAEA,GAAAL,EACA,IAAAI,EAAA,EAAYA,EAAAJ,EAAAM,OAA2BF,IACvCD,EAAAY,IAAAC,EAAAhB,EAAAI,IAGA,OAAAD,GAIA,IAAAc,KAGAV,GACAW,EAAA,GAIA,SAAAH,EAAAd,GAGA,GAAAgB,EAAAhB,GACA,OAAAgB,EAAAhB,GAAAkB,QAGA,IAAAC,EAAAH,EAAAhB,IACAG,EAAAH,EACAoB,GAAA,EACAF,YAUA,OANAN,EAAAZ,GAAAW,KAAAQ,EAAAD,QAAAC,IAAAD,QAAAJ,GAGAK,EAAAC,GAAA,EAGAD,EAAAD,QAKAJ,EAAAO,EAAAT,EAGAE,EAAAQ,EAAAN,EAGAF,EAAAS,EAAA,SAAAL,EAAAM,EAAAC,GACAX,EAAAY,EAAAR,EAAAM,IACAhB,OAAAmB,eAAAT,EAAAM,GACAI,cAAA,EACAC,YAAA,EACAC,IAAAL,KAMAX,EAAAiB,EAAA,SAAAZ,GACA,IAAAM,EAAAN,KAAAa,WACA,WAA2B,OAAAb,EAAA,SAC3B,WAAiC,OAAAA,GAEjC,OADAL,EAAAS,EAAAE,EAAA,IAAAA,GACAA,GAIAX,EAAAY,EAAA,SAAAO,EAAAC,GAAsD,OAAA1B,OAAAC,UAAAC,eAAAC,KAAAsB,EAAAC,IAGtDpB,EAAAqB,EAAA,IAGArB,EAAAsB,GAAA,SAAAC,GAA8D,MAApBC,QAAAC,MAAAF,GAAoBA","file":"static/js/manifest.2ae2e69a05c33dfc65f8.js","sourcesContent":[" \t// install a JSONP callback for chunk loading\n \tvar parentJsonpFunction = window[\"webpackJsonp\"];\n \twindow[\"webpackJsonp\"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {\n \t\t// add \"moreModules\" to the modules object,\n \t\t// then flag all \"chunkIds\" as loaded and fire callback\n \t\tvar moduleId, chunkId, i = 0, resolves = [], result;\n \t\tfor(;i < chunkIds.length; i++) {\n \t\t\tchunkId = chunkIds[i];\n \t\t\tif(installedChunks[chunkId]) {\n \t\t\t\tresolves.push(installedChunks[chunkId][0]);\n \t\t\t}\n \t\t\tinstalledChunks[chunkId] = 0;\n \t\t}\n \t\tfor(moduleId in moreModules) {\n \t\t\tif(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {\n \t\t\t\tmodules[moduleId] = moreModules[moduleId];\n \t\t\t}\n \t\t}\n \t\tif(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);\n \t\twhile(resolves.length) {\n \t\t\tresolves.shift()();\n \t\t}\n \t\tif(executeModules) {\n \t\t\tfor(i=0; i < executeModules.length; i++) {\n \t\t\t\tresult = __webpack_require__(__webpack_require__.s = executeModules[i]);\n \t\t\t}\n \t\t}\n \t\treturn result;\n \t};\n\n \t// The module cache\n \tvar installedModules = {};\n\n \t// objects to store loaded and loading chunks\n \tvar installedChunks = {\n \t\t2: 0\n \t};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/\";\n\n \t// on error function for async loading\n \t__webpack_require__.oe = function(err) { console.error(err); throw err; };\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap 43a1e13c45f80b089c40"],"sourceRoot":""}
|
||||
91
pyapp/ui/client/static/js/vendor.9f7b4785a30f0533ee08.js
Normal file
91
pyapp/ui/client/static/js/vendor.9f7b4785a30f0533ee08.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
3
pyapp/ui/server/__init__.py
Normal file
3
pyapp/ui/server/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Server-side UI components for PyApp
|
||||
"""
|
||||
BIN
pyapp/ui/server/__pycache__/__init__.cpython-39.pyc
Normal file
BIN
pyapp/ui/server/__pycache__/__init__.cpython-39.pyc
Normal file
Binary file not shown.
BIN
pyapp/ui/server/__pycache__/app.cpython-39.pyc
Normal file
BIN
pyapp/ui/server/__pycache__/app.cpython-39.pyc
Normal file
Binary file not shown.
177
pyapp/ui/server/app.py
Normal file
177
pyapp/ui/server/app.py
Normal file
@@ -0,0 +1,177 @@
|
||||
import os
|
||||
import threading
|
||||
import socket
|
||||
import logging
|
||||
import psutil
|
||||
import GPUtil
|
||||
import platform
|
||||
|
||||
from flask import Flask, jsonify, request, send_from_directory
|
||||
from flask_cors import CORS
|
||||
from pyapp.utils.project import Project
|
||||
from pyapp.stereotype import get_all_component_classes, get_all_component_comments
|
||||
from pyapp.application import application_class
|
||||
from pyapp.status import status_manager
|
||||
from pyapp.utils.log import Log
|
||||
from pyapp.utils.timer import Timer
|
||||
|
||||
app = Flask(__name__, static_folder="../client")
|
||||
app.logger.setLevel("WARNING")
|
||||
logging.getLogger("werkzeug").disabled = True
|
||||
CORS(app)
|
||||
root_path = os.getcwd()
|
||||
Project.scan_project(root_path)
|
||||
configs = Project.scan_configs(root_path)
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def serve_index():
|
||||
return send_from_directory(app.static_folder, "index.html")
|
||||
|
||||
|
||||
@app.route("/<path:path>")
|
||||
def serve_file(path):
|
||||
return send_from_directory(app.static_folder, path)
|
||||
|
||||
|
||||
@app.route("/test", methods=["POST"])
|
||||
def hello_world():
|
||||
return jsonify(message="Hello, World!")
|
||||
|
||||
|
||||
@app.route("/project/structure", methods=["POST"])
|
||||
def project_structure():
|
||||
component_info = {}
|
||||
for st, cls_dict in get_all_component_classes().items():
|
||||
component_info[st] = {k: v.__name__ for k, v in cls_dict.items()}
|
||||
comment_info = get_all_component_comments()
|
||||
app_info = {}
|
||||
for app_name, app_cls in application_class.items():
|
||||
app_info[app_name] = app_cls.__name__
|
||||
|
||||
return jsonify(
|
||||
components=component_info,
|
||||
comments=comment_info,
|
||||
applications=app_info,
|
||||
configs=configs,
|
||||
root_path=root_path,
|
||||
)
|
||||
|
||||
|
||||
@app.route("/project/run_app", methods=["POST"])
|
||||
def run_application():
|
||||
data = request.json
|
||||
app_name = data.get("app_name")
|
||||
app_cls = application_class.get(app_name)
|
||||
|
||||
if app_cls is None:
|
||||
Log.error(
|
||||
f"No class annotated with @PyappApplication found with the name '{app_name}'.",
|
||||
True,
|
||||
)
|
||||
return jsonify(
|
||||
{
|
||||
"message": f"No application found with the name '{app_name}'",
|
||||
"status": "error",
|
||||
}
|
||||
)
|
||||
|
||||
if not hasattr(app_cls, "start"):
|
||||
Log.error(
|
||||
"The class annotated with @PyappApplication should have a 'start' method.",
|
||||
True,
|
||||
)
|
||||
return jsonify(
|
||||
{"message": "The class should have a 'start' method", "status": "error"}
|
||||
)
|
||||
|
||||
def run_in_background():
|
||||
Log.info(f"Application '{app_cls.__name__}' started.")
|
||||
timer = Timer("Application")
|
||||
timer.start()
|
||||
status_manager.run_app(app_name, app_cls)
|
||||
app_cls.start()
|
||||
status_manager.end_app(app_name)
|
||||
timer.stop()
|
||||
Log.info(timer.get_elasped_time_str(Timer.HOURS))
|
||||
Log.success("Application finished.")
|
||||
|
||||
threading.Thread(target=run_in_background).start()
|
||||
|
||||
return jsonify(
|
||||
{"message": f"Application '{app_name}' is running now.", "status": "success"}
|
||||
)
|
||||
|
||||
|
||||
@app.route("/project/get_status", methods=["POST"])
|
||||
def get_status():
|
||||
cpu_info = {
|
||||
"model": platform.processor(),
|
||||
"usage_percent": psutil.cpu_percent(interval=1),
|
||||
}
|
||||
virtual_memory = psutil.virtual_memory()
|
||||
memory_info = {
|
||||
"used": round(virtual_memory.used / (1024**3), 3),
|
||||
"total": round(virtual_memory.total / (1024**3), 3),
|
||||
}
|
||||
|
||||
gpus = GPUtil.getGPUs()
|
||||
gpu_info = []
|
||||
for gpu in gpus:
|
||||
gpu_info.append(
|
||||
{
|
||||
"name": gpu.name,
|
||||
"memory_used": gpu.memoryUsed,
|
||||
"memory_total": gpu.memoryTotal,
|
||||
}
|
||||
)
|
||||
|
||||
return jsonify(
|
||||
curr_status=status_manager.get_status(),
|
||||
last_status=status_manager.get_last_status(),
|
||||
logs=status_manager.get_log(),
|
||||
progress=status_manager.get_progress(),
|
||||
running_apps=status_manager.get_running_apps(),
|
||||
cpu=cpu_info,
|
||||
memory=memory_info,
|
||||
gpus=gpu_info,
|
||||
)
|
||||
|
||||
|
||||
@app.route("/project/set_status", methods=["POST"])
|
||||
def set_status():
|
||||
status = request.json.get("status")
|
||||
progress = request.json.get("progress")
|
||||
if status:
|
||||
status_manager.set_status(
|
||||
app_name=status["app_name"],
|
||||
runner_name=status["runner_name"],
|
||||
key=status["key"],
|
||||
value=status["value"],
|
||||
)
|
||||
if progress:
|
||||
status_manager.set_progress(
|
||||
app_name=progress["app_name"],
|
||||
runner_name=progress["runner_name"],
|
||||
key=progress["key"],
|
||||
curr_value=progress["curr_value"],
|
||||
max_value=progress["max_value"],
|
||||
)
|
||||
return jsonify({"status": "success"})
|
||||
|
||||
@app.route("/project/add_log", methods=["POST"])
|
||||
def add_log():
|
||||
log = request.json.get("log")
|
||||
Log.log(log["message"], log["log_type"])
|
||||
return jsonify({"status": "success"})
|
||||
|
||||
def find_free_port(start_port):
|
||||
"""Find a free port starting from start_port."""
|
||||
port = start_port
|
||||
while True:
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
||||
result = sock.connect_ex(("localhost", port))
|
||||
if result != 0:
|
||||
return port
|
||||
port += 1
|
||||
|
||||
3
pyapp/utils/__init__.py
Normal file
3
pyapp/utils/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Utility modules for PyApp
|
||||
"""
|
||||
BIN
pyapp/utils/__pycache__/__init__.cpython-39.pyc
Normal file
BIN
pyapp/utils/__pycache__/__init__.cpython-39.pyc
Normal file
Binary file not shown.
BIN
pyapp/utils/__pycache__/log.cpython-39.pyc
Normal file
BIN
pyapp/utils/__pycache__/log.cpython-39.pyc
Normal file
Binary file not shown.
BIN
pyapp/utils/__pycache__/project.cpython-39.pyc
Normal file
BIN
pyapp/utils/__pycache__/project.cpython-39.pyc
Normal file
Binary file not shown.
BIN
pyapp/utils/__pycache__/timer.cpython-39.pyc
Normal file
BIN
pyapp/utils/__pycache__/timer.cpython-39.pyc
Normal file
Binary file not shown.
99
pyapp/utils/log.py
Normal file
99
pyapp/utils/log.py
Normal file
@@ -0,0 +1,99 @@
|
||||
import time
|
||||
import json
|
||||
from pyapp.status import status_manager
|
||||
from pyapp.namespace import LogType
|
||||
class Log:
|
||||
MAX_TITLE_LENGTH:int = 7
|
||||
TYPE_COLOR_MAP = {
|
||||
LogType.INFO: "\033[94m",
|
||||
LogType.ERROR: "\033[91m",
|
||||
LogType.WARNING: "\033[93m",
|
||||
LogType.SUCCESS: "\033[92m",
|
||||
LogType.DEBUG: "\033[95m",
|
||||
LogType.TERMINATE: "\033[96m"
|
||||
}
|
||||
|
||||
def get_time():
|
||||
return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
|
||||
|
||||
def blue(message):
|
||||
# blue
|
||||
print(f"\033[94m{message}\033[0m")
|
||||
def red(message):
|
||||
# red
|
||||
print(f"\033[91m{message}\033[0m")
|
||||
def yellow(message):
|
||||
# yellow
|
||||
print(f"\033[93m{message}\033[0m")
|
||||
def green(message):
|
||||
# green
|
||||
print(f"\033[92m{message}\033[0m")
|
||||
|
||||
def log(message, log_type: str):
|
||||
time_str = Log.get_time()
|
||||
space = ""
|
||||
if len(log_type) < Log.MAX_TITLE_LENGTH:
|
||||
space = " " * (Log.MAX_TITLE_LENGTH - len(log_type))
|
||||
if isinstance(message, dict):
|
||||
formatted_message = json.dumps(message, indent=2, ensure_ascii=False)
|
||||
else:
|
||||
formatted_message = message
|
||||
|
||||
print (f"\033[1m\033[4m({time_str})\033[0m \033[1m{Log.TYPE_COLOR_MAP[log_type]}[{log_type.capitalize()}]\033[0m{space} {Log.TYPE_COLOR_MAP[log_type]}{formatted_message}\033[0m")
|
||||
status_manager.add_log(time_str, log_type, formatted_message)
|
||||
|
||||
def bold(message):
|
||||
print(f"\033[1m{message}\033[0m")
|
||||
def underline(message):
|
||||
print(f"\033[4m{message}\033[0m")
|
||||
|
||||
def info(message):
|
||||
Log.log(message, LogType.INFO)
|
||||
|
||||
def error(message, terminate=False):
|
||||
Log.log(message, LogType.ERROR)
|
||||
if terminate:
|
||||
Log.terminate("Application Terminated.")
|
||||
|
||||
|
||||
def warning(message):
|
||||
Log.log(message, LogType.WARNING)
|
||||
def success(message):
|
||||
Log.log(message, LogType.SUCCESS)
|
||||
|
||||
def debug(message):
|
||||
Log.log(message, LogType.DEBUG)
|
||||
|
||||
def terminate(message):
|
||||
Log.log(message, LogType.TERMINATE)
|
||||
exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
Log.info("This is a info message")
|
||||
Log.error("This is a error message")
|
||||
Log.warning("This is a warning message")
|
||||
Log.success("This is a success message")
|
||||
Log.debug("This is a debug message")
|
||||
Log.blue("This is a blue message")
|
||||
Log.red("This is a red message")
|
||||
Log.yellow("This is a yellow message")
|
||||
Log.green("This is a green message")
|
||||
|
||||
Log.bold("This is a bold message")
|
||||
Log.underline("This is a underline message")
|
||||
|
||||
test_dict = {
|
||||
"name": "John Doe",
|
||||
"age": 25,
|
||||
"city": "New York",
|
||||
"hobbies": ["reading", "swimming", "programming"],
|
||||
"address": {
|
||||
"street": "Main Street",
|
||||
"number": 123
|
||||
}
|
||||
}
|
||||
Log.info("Dictionary test:")
|
||||
Log.info(test_dict)
|
||||
|
||||
|
||||
|
||||
50
pyapp/utils/project.py
Normal file
50
pyapp/utils/project.py
Normal file
@@ -0,0 +1,50 @@
|
||||
import os
|
||||
import sys
|
||||
import yaml
|
||||
import importlib
|
||||
|
||||
class Project:
|
||||
@staticmethod
|
||||
def scan_project(root_path):
|
||||
sys.path.append(root_path)
|
||||
if not os.path.exists(root_path) or not os.path.isdir(root_path):
|
||||
raise ValueError(f"The provided root_path '{root_path}' is not a valid directory.")
|
||||
|
||||
parent_dir = os.path.dirname(root_path)
|
||||
sys.path.insert(0, parent_dir)
|
||||
|
||||
def import_all_modules(path, package_name):
|
||||
for root, dirs, files in os.walk(path):
|
||||
relative_path = os.path.relpath(root, root_path)
|
||||
if relative_path == '.':
|
||||
module_package = package_name
|
||||
else:
|
||||
module_package = f"{package_name}.{relative_path.replace(os.sep, '.')}"
|
||||
for file in files:
|
||||
if file.endswith(".py") and file != "__init__.py":
|
||||
module_name = file[:-3]
|
||||
full_module_name = f"{module_package}.{module_name}"
|
||||
if full_module_name not in sys.modules:
|
||||
importlib.import_module(full_module_name)
|
||||
dirs[:] = [d for d in dirs if not d.startswith('.')]
|
||||
|
||||
package_name = os.path.basename(root_path)
|
||||
import_all_modules(root_path, package_name)
|
||||
|
||||
@staticmethod
|
||||
def scan_configs(root_path):
|
||||
configs = {}
|
||||
for root, dirs, files in os.walk(root_path):
|
||||
for file in files:
|
||||
if file.endswith(('.yaml', '.yml')):
|
||||
if file.startswith('__'):
|
||||
continue
|
||||
file_path = os.path.join(root, file)
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
try:
|
||||
content = yaml.safe_load(f)
|
||||
configs[os.path.splitext(file)[0]] = content
|
||||
except yaml.YAMLError as e:
|
||||
print(f"Error reading {file_path}: {e}")
|
||||
|
||||
return configs
|
||||
32
pyapp/utils/timer.py
Normal file
32
pyapp/utils/timer.py
Normal file
@@ -0,0 +1,32 @@
|
||||
import time
|
||||
|
||||
class Timer:
|
||||
MILLI_SECONDS = "milliseconds"
|
||||
SECONDS = "seconds"
|
||||
MINUTES = "minutes"
|
||||
HOURS = "hours"
|
||||
def __init__(self, name=None):
|
||||
self.start_time = None
|
||||
self.end_time = None
|
||||
self.name = name
|
||||
|
||||
def start(self):
|
||||
self.start_time = time.time()
|
||||
|
||||
def stop(self):
|
||||
self.end_time = time.time()
|
||||
|
||||
def elapsed_time(self):
|
||||
return int(self.end_time - self.start_time)
|
||||
|
||||
def get_elasped_time_str(self, format):
|
||||
if format == Timer.SECONDS:
|
||||
return f"Elapsed time in <{self.name}>: {self.elapsed_time()} seconds"
|
||||
elif format == Timer.MINUTES:
|
||||
return f"Elapsed time in <{self.name}>: {self.elapsed_time() // 60} minutes, {self.elapsed_time() % 60} seconds"
|
||||
elif format == Timer.HOURS:
|
||||
return f"Elapsed time in <{self.name}>: {self.elapsed_time() // 3600} hours, {(self.elapsed_time() % 3600)//60} minutes, {self.elapsed_time() % 60} seconds"
|
||||
elif format == Timer.MILLI_SECONDS:
|
||||
return f"Elapsed time in <{self.name}>: {(self.end_time - self.start_time) * 1000} milliseconds"
|
||||
else:
|
||||
return f"Invalid format: {format}"
|
||||
24
setup.py
Normal file
24
setup.py
Normal file
@@ -0,0 +1,24 @@
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name="pyapp",
|
||||
version="0.1",
|
||||
packages=find_packages(),
|
||||
package_data={
|
||||
'pyapp': ['templates/*.py',"ui/server/*.py", "ui/client/*",
|
||||
"ui/client/static/css/*","ui/client/static/fonts/*",
|
||||
"ui/client/static/img/*","ui/client/static/js/*"],
|
||||
},
|
||||
install_requires=[
|
||||
'pyyaml',
|
||||
'psutil',
|
||||
'GPUtil',
|
||||
'flask',
|
||||
'flask_cors',
|
||||
],
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'pa = pyapp.boot:main',
|
||||
],
|
||||
},
|
||||
)
|
||||
Reference in New Issue
Block a user