initial commit
This commit is contained in:
85
shell/Handlers/ABS.py
Normal file
85
shell/Handlers/ABS.py
Normal file
@@ -0,0 +1,85 @@
|
||||
from pathlib import Path
|
||||
|
||||
from modelspace.Repository import Repository
|
||||
global_repo = Repository(str(Path('..') / 'repo'))
|
||||
|
||||
|
||||
class ExecutionError(RuntimeError): pass
|
||||
|
||||
|
||||
class Handler:
|
||||
syntax_error = SyntaxError
|
||||
execution_error = ExecutionError
|
||||
|
||||
def __init__(self):
|
||||
self.forwarding_table: dict[str, Handler] = {}
|
||||
self.handle_table: dict = {}
|
||||
self.succeed = False
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def _check_arg(keys: dict, arg: str):
|
||||
if not keys.get(arg, None): raise TypeError('Unfilled argument:', arg)
|
||||
|
||||
|
||||
def handle(self, command: list[str], pos = 0):
|
||||
self.succeed = False
|
||||
if len(command) <= pos: raise self.syntax_error
|
||||
verb = command[pos].lower()
|
||||
|
||||
if verb in self.forwarding_table:
|
||||
self.forwarding_table[verb].handle(command, pos + 1)
|
||||
if self.forwarding_table[verb].succeed:
|
||||
self.succeed = True
|
||||
return
|
||||
|
||||
elif verb in self.handle_table:
|
||||
self.handle_table[verb](command, pos + 1)
|
||||
if self.succeed:
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
def parse_arguments(args_list: list[str], expected_args: list[str], key_mode=False) -> tuple[dict[str, str | None], list[str]]:
|
||||
"""
|
||||
Парсит список аргументов согласно ожидаемым именам.
|
||||
|
||||
Args:
|
||||
args_list: список аргументов для обработки
|
||||
expected_args: список ожидаемых имен аргументов
|
||||
|
||||
Returns:
|
||||
Кортеж из двух элементов:
|
||||
- Словарь с ключами из expected_args и значениями аргументов (или None)
|
||||
- Список необработанных аргументов
|
||||
"""
|
||||
# Инициализируем результат значениями None для всех ожидаемых аргументов
|
||||
result: dict[str, str | None] = {arg: None for arg in expected_args}
|
||||
|
||||
# Извлекаем именованные аргументы (в формате "имя:значение")
|
||||
remaining_args = []
|
||||
|
||||
for arg in args_list:
|
||||
if ':' in arg:
|
||||
key, value = arg.split(':', 1)
|
||||
if key in expected_args:
|
||||
result[key] = value
|
||||
else:
|
||||
# Если имя аргумента не ожидается, добавляем в оставшиеся
|
||||
remaining_args.append(arg)
|
||||
else:
|
||||
remaining_args.append(arg)
|
||||
|
||||
# Заполняем оставшиеся аргументы по порядку
|
||||
arg_index = 0
|
||||
if not key_mode:
|
||||
for arg_name in expected_args:
|
||||
if result[arg_name] is None and arg_index < len(remaining_args):
|
||||
result[arg_name] = remaining_args[arg_index]
|
||||
arg_index += 1
|
||||
|
||||
# Все оставшиеся аргументы добавляем в unsorted
|
||||
unsorted = remaining_args[arg_index:]
|
||||
|
||||
return result, unsorted
|
||||
|
||||
|
||||
27
shell/Handlers/GlobalHandler.py
Normal file
27
shell/Handlers/GlobalHandler.py
Normal file
@@ -0,0 +1,27 @@
|
||||
from shell.Handlers.ABS import Handler
|
||||
from shell.Handlers.PythonappHandler import PythonappHandler
|
||||
from shell.Handlers.ModelSpaceHandler import ModelSpaceHandler
|
||||
|
||||
|
||||
class GlobalHandler(Handler):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.forwarding_table: dict[str, Handler] = {
|
||||
'pythonapp': PythonappHandler(),
|
||||
'modelspace': ModelSpaceHandler(),
|
||||
}
|
||||
self.handle_table: dict = {
|
||||
'tell': self._tell
|
||||
|
||||
}
|
||||
|
||||
def _tell(self, command: list[str], pos = 0):
|
||||
command_str = ''
|
||||
for word in command[pos:]: command_str += word + ' '
|
||||
print(command_str)
|
||||
self.succeed = True
|
||||
|
||||
def _exit(self, command: list[str], pos = 0):
|
||||
raise KeyboardInterrupt
|
||||
|
||||
|
||||
18
shell/Handlers/ModelSpaceHandler.py
Normal file
18
shell/Handlers/ModelSpaceHandler.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from shell.Handlers.ABS import Handler, global_repo
|
||||
|
||||
|
||||
class ModelSpaceHandler(Handler):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.forwarding_table: dict[str, Handler] = {
|
||||
}
|
||||
self.handle_table: dict = {
|
||||
'create_inter': self._create_inter
|
||||
|
||||
}
|
||||
|
||||
pass
|
||||
|
||||
def _create_inter(self, command: list[str], pos=0):
|
||||
global_repo.add_model_package_interactive()
|
||||
self.succeed = True
|
||||
88
shell/Handlers/PythonappHandler.py
Normal file
88
shell/Handlers/PythonappHandler.py
Normal file
@@ -0,0 +1,88 @@
|
||||
from shell.Handlers.ABS import Handler
|
||||
|
||||
from pythonapp.Instance.Instance import Instance
|
||||
|
||||
|
||||
class PythonappHandler(Handler):
|
||||
|
||||
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.forwarding_table: dict[str, Handler] = {
|
||||
'package': PackageHandler(self)
|
||||
}
|
||||
self.handle_table: dict = {
|
||||
'create': self._create,
|
||||
'load': self._load,
|
||||
'show': self._show,
|
||||
'activate': self._activate,
|
||||
}
|
||||
self._loaded_instances: dict[str, Instance] = {}
|
||||
self._active_instance: Instance | None = None
|
||||
|
||||
|
||||
def _load(self, command: list[str], pos = 0):
|
||||
keys, args = self.parse_arguments(command[pos:], ['path', 'name'])
|
||||
self._check_arg(keys, 'path')
|
||||
if not keys['name']: keys['name'] = 'app'
|
||||
i = Instance(path=str(keys['path']))
|
||||
if not i.config.created: raise self.execution_error("ACTIVATE INSTANCE: instance not exists")
|
||||
self._loaded_instances[keys['name']] = i
|
||||
self._active_instance = i
|
||||
print(f"instance {keys['path']} loaded and activated. identified by {keys['name']}")
|
||||
|
||||
def _activate(self, command: list[str], pos = 0):
|
||||
keys, args = self.parse_arguments(command[pos:], ['name'])
|
||||
self._check_arg(keys, 'name')
|
||||
i = self._loaded_instances.get(command[1], None)
|
||||
if i:
|
||||
self._active_instance = i
|
||||
else:
|
||||
raise ValueError(f"pyapp {keys['name']} not loaded")
|
||||
|
||||
def _show(self, command: list[str], pos = 0):
|
||||
print("Environment type:", self._active_instance.config.env_type)
|
||||
# TODO Add new config info (app section)
|
||||
|
||||
def _create(self, command: list[str], pos = 0):
|
||||
keys, args = self.parse_arguments(command[pos:], ['env', 'path', 'python'])
|
||||
|
||||
|
||||
self.succeed = True
|
||||
|
||||
pass
|
||||
|
||||
@property
|
||||
def active_instance(self):
|
||||
return self._active_instance
|
||||
|
||||
|
||||
class PackageHandler(Handler):
|
||||
def __init__(self,parent: PythonappHandler):
|
||||
super().__init__()
|
||||
self.forwarding_table: dict[str, Handler] = {}
|
||||
self.handle_table: dict = {
|
||||
'install': self._install,
|
||||
'exclude': self._exclude
|
||||
}
|
||||
self.parent = parent
|
||||
|
||||
def _install(self, command: list[str], pos = 0):
|
||||
if not self.parent.active_instance: raise self.execution_error("I don't have active instance yet!")
|
||||
keys, args = self.parse_arguments(command[pos:], ['pin', 'index', 'extra'], key_mode=True)
|
||||
if keys['pin']: pin = True
|
||||
else: pin = False
|
||||
self.parent.active_instance.install_packages(pkgs=args, repo=keys.get('index', None), extra=keys.get('extra', None), pin=pin)
|
||||
|
||||
def _exclude(self, command: list[str], pos = 0):
|
||||
if not self.parent.active_instance: raise self.execution_error("I don't have active instance yet!")
|
||||
pkgs = command[pos:]
|
||||
self.parent.active_instance.exclude_packages(pkgs)
|
||||
|
||||
|
||||
|
||||
|
||||
self.succeed = True
|
||||
|
||||
|
||||
0
shell/Handlers/__init__.py
Normal file
0
shell/Handlers/__init__.py
Normal file
BIN
shell/Handlers/__pycache__/ABS.cpython-313.pyc
Normal file
BIN
shell/Handlers/__pycache__/ABS.cpython-313.pyc
Normal file
Binary file not shown.
BIN
shell/Handlers/__pycache__/GlobalHandler.cpython-313.pyc
Normal file
BIN
shell/Handlers/__pycache__/GlobalHandler.cpython-313.pyc
Normal file
Binary file not shown.
BIN
shell/Handlers/__pycache__/ModelSpaceHandler.cpython-313.pyc
Normal file
BIN
shell/Handlers/__pycache__/ModelSpaceHandler.cpython-313.pyc
Normal file
Binary file not shown.
BIN
shell/Handlers/__pycache__/PythonappHandler.cpython-313.pyc
Normal file
BIN
shell/Handlers/__pycache__/PythonappHandler.cpython-313.pyc
Normal file
Binary file not shown.
BIN
shell/Handlers/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
shell/Handlers/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
96
shell/Interactive.py
Normal file
96
shell/Interactive.py
Normal file
@@ -0,0 +1,96 @@
|
||||
import sys
|
||||
from colorama import init, Fore, Style
|
||||
|
||||
from shell.Handlers.GlobalHandler import GlobalHandler
|
||||
from shell.Parser import Parser
|
||||
|
||||
# Инициализация colorama для кроссплатформенной поддержки цветов
|
||||
init()
|
||||
|
||||
|
||||
class Interactive:
|
||||
def __init__(self):
|
||||
self.prompt = Interactive._get_colored_prompt()
|
||||
self.parser = Parser()
|
||||
self.handler = GlobalHandler()
|
||||
|
||||
|
||||
@classmethod
|
||||
def _get_colored_prompt(cls, prompt = "Vaiola> "):
|
||||
"""Создает градиентный цветной промпт 'Vaiola>'"""
|
||||
colored_prompt = ""
|
||||
|
||||
# Цвета для градиента (от ярко-белого к фиолетовому и обратно к белому)
|
||||
colors = [
|
||||
Fore.LIGHTWHITE_EX, # V
|
||||
Fore.MAGENTA, # a
|
||||
Fore.MAGENTA, # i (немного менее яркий фиолетовый)
|
||||
Fore.LIGHTMAGENTA_EX, # o (фиолетовый)
|
||||
Fore.LIGHTMAGENTA_EX, # l (немного более яркий фиолетовый)
|
||||
Fore.LIGHTWHITE_EX, # a (ярко-фиолетовый)
|
||||
Fore.LIGHTWHITE_EX # > (ярко-белый)
|
||||
]
|
||||
|
||||
# Применяем цвета к каждому символу промпта
|
||||
for i, char in enumerate(prompt):
|
||||
if i < len(colors):
|
||||
colored_prompt += colors[i] + char
|
||||
else:
|
||||
colored_prompt += Fore.LIGHTWHITE_EX + char
|
||||
|
||||
colored_prompt += Style.RESET_ALL
|
||||
return colored_prompt
|
||||
|
||||
def input(self):
|
||||
args = ['']
|
||||
while True:
|
||||
new_args = self.parser.parse(input(self.prompt).strip())
|
||||
if len(new_args) == 0:
|
||||
continue
|
||||
args[len(args) - 1] += '\n' + new_args[0] if args[len(args) - 1] != '' else new_args[0]
|
||||
args.extend(new_args[1:])
|
||||
if self.parser.in_quotes:
|
||||
self.prompt = self._get_colored_prompt("\"____\"> ")
|
||||
continue
|
||||
else:
|
||||
self.prompt = self._get_colored_prompt()
|
||||
break
|
||||
return args
|
||||
|
||||
|
||||
|
||||
def start(self):
|
||||
"""Запускает интерактивную оболочку"""
|
||||
while True:
|
||||
try:
|
||||
command = self.input()
|
||||
try:
|
||||
self.handler.handle(command)
|
||||
except ValueError as e:
|
||||
print(f"Error: {e}")
|
||||
except RuntimeWarning:
|
||||
print("Warning: last command failed")
|
||||
|
||||
# # Выход из цикла по команде exit или quit
|
||||
# if command.lower() in ['exit', 'quit']:
|
||||
# print("До свидания!")
|
||||
# break
|
||||
|
||||
# # Парсим команду
|
||||
# try:
|
||||
# parsed = Parser.parse(command)
|
||||
# print(f"Парсинг результата: {parsed}")
|
||||
# # Здесь можно добавить обработку команд
|
||||
# except ValueError as e:
|
||||
# print(f"Ошибка: {e}")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\nGoodbye!")
|
||||
break
|
||||
except EOFError:
|
||||
print("\nGoodbye!")
|
||||
break
|
||||
|
||||
# Запуск интерактивной оболочки
|
||||
if __name__ == "__main__":
|
||||
Interactive().start()
|
||||
42
shell/Parser.py
Normal file
42
shell/Parser.py
Normal file
@@ -0,0 +1,42 @@
|
||||
class Parser:
|
||||
def __init__(self):
|
||||
self.in_quotes = False
|
||||
|
||||
|
||||
def parse(self, command: str) -> list[str]:
|
||||
tokens = []
|
||||
current_token = []
|
||||
|
||||
for char in command:
|
||||
if char == '"':
|
||||
if self.in_quotes:
|
||||
# Завершаем токен внутри кавычек
|
||||
tokens.append(''.join(current_token))
|
||||
current_token = []
|
||||
self.in_quotes = False
|
||||
else:
|
||||
# Начинаем новый токен в кавычках
|
||||
if current_token:
|
||||
# Если до кавычек были символы, добавляем их как отдельный токен
|
||||
tokens.append(''.join(current_token))
|
||||
current_token = []
|
||||
self.in_quotes = True
|
||||
elif char == ' ':
|
||||
if self.in_quotes:
|
||||
# Внутри кавычек пробелы добавляем к текущему токену
|
||||
current_token.append(char)
|
||||
else:
|
||||
if current_token:
|
||||
# Завершаем текущий токен, если он есть
|
||||
tokens.append(''.join(current_token))
|
||||
current_token = []
|
||||
else:
|
||||
# Любой символ, кроме кавычек и пробела
|
||||
current_token.append(char)
|
||||
|
||||
if current_token:
|
||||
tokens.append(''.join(current_token))
|
||||
|
||||
return tokens
|
||||
|
||||
|
||||
0
shell/__init__.py
Normal file
0
shell/__init__.py
Normal file
BIN
shell/__pycache__/Interactive.cpython-313.pyc
Normal file
BIN
shell/__pycache__/Interactive.cpython-313.pyc
Normal file
Binary file not shown.
BIN
shell/__pycache__/Parser.cpython-313.pyc
Normal file
BIN
shell/__pycache__/Parser.cpython-313.pyc
Normal file
Binary file not shown.
BIN
shell/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
shell/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
Reference in New Issue
Block a user