Фреймворк для удобной организации архитектуры небольших программ.
В общем виде архитектура представляет из себя один класс родителя и множество детей - плагинов или модулей
Базовый класс для плагинов и класс родителя имеют удобный интерфейс для расширения программы собственными плагинами и управления для взаимодействия между плагинами.
Прелесть такой системы в том, что все плагины имеют указатель на родителя, а тот в свою очередь имеет (при необходимости) указатели на плагины и модули
Плагин - объект класса base.Plugin или его наследника
плагин обязан иметь конкретный интерфейс, которым будет пользоваться объект класса base.Parent или его наследника.
Плагин имеет интерфейс доступа к другим плагнам, модулям и объекту класса base.Parent или его наследника.
Модуль - некоторый объект/класс/область видимости с собственными функция.
Модуль не обязан иметь какой-либо интерфейс.
Задача родиля в данном случае сводит лишь к харению и предоставлению доступа к модулю.
Модуль все цело замкнут в себе.
SQL - Обертка над драйвером MySQL
Констркутор принимает четыре обязательных параметра:
- host - хост БД
- port - порт БД
- user - логин
- passwd - пароль
Так же можно передать:
- ddl - словарь, ключ - название таблицы table_name, значение DDL-скрипт
- scheme - схема по-умолчанию
Интерфейс класса:
def execute(self, query, commit=False, multi=False)
def select_all(self, query, unique_cursor=False)
def select(self, query, unique_cursor=False)
Метод execute - универсальный метод обращений с БД.
Возвращает кортеж : Флаг успеха и список кортежей (записей), если запрос подразумевает результат.
Метод select_all - облегченная версия execute, годная лишь для чтения БД.
Результат - список кортежей (записей).
В случае ошибки вернет None.
Метод select - облегченная версия execute, годная лишь для чтения БД.
Результат - генератор, который возвращает по кортежу (по записе).
В случае ошибки кинет исключение.
Neon - Веб-сервер. Особенность такого веб-севера в том, что он запускается не в главном треде, что удобно для программы, где веб не первостепенная задача.
Конфигурация передается веб-серверу через параметры в методе init:
- allowed_hosts - default: {'any'}, - список разрешенных значений HTTP-заголовка Host
- only_local_hosts - default: False, - Если True, то обрабатывает запросы только с приватных IP
- believe_x_from_y - default: False, - Если True, то значение заголовка X-From-Y считается реальным IP клиента
- http_port - default: 8080, - порт для HTTP трафика
- https_port - default: 8081, - порт для HTTPS трафика
- use_ssl - default: False, - использовать SSL
- ca_cert - default: None, - путь к файлу с корневыми сертификатами
- ssl_certs - default: {} - настройки ssl: ассоциативный массив, ключ - имя хоста, значение - словарь настроек
Пример такой настройки: {
-- certfile - путь к серверному сертификату в кодировке PEM
-- keyfile - путь к закрытому ключу от сертификата
-- keypassword - пароль от ключа (опционально)
} - max_data_length - default: 4КБ, - параметр управление разрешенного объема данных в запросе
- max_header_count - default: 32, - параметр управление разрешенного объема данных в запросе
- max_header_length - default: 2КБ, - параметр управление разрешенного объема данных в запросе
- site_directory - default: './var', - путь к папке с исходными файлами сайта (html/js/css)
- threading - default: False, - обрабатывать каждый запрос в новом треде?
- use_neon_server - default: False, - использовать встроенный обработчие запросов ? (выдача статических файлов/содержимого директории)
- response_settings - default: {}, - настройки заголовков ответа
-- cache_min - default 120, - значение 'Cache-Control' в секундах
-- max_response_size - default 2^20, - Масимальный размер ответа на запрос (для статических файлов) - single_request_per_socket - default: True, - Если True, то работает принцип "одно соединение - один запрос"
- enable_stats - default: True, - Если True, то неон будет собирать статистику
Пользовательский интерфейс объекта этого класса:
def add_site_module(self, module, path: str=None, response_type: str=None)
def add_ws_handler(self, handler, path: str=None)
def add_middleware(self, target=None, post=None)
Метод add_site_module добавляет module в список возможных обработчиков запросов.
Модуль будет ассоциироваться со всеми url, у которых начало совпадает с path (естественно из ассоциированных модулей выбирается с наибольшим path)
Модулем может быть любой объект, класс или модуль, удовлетворяющий определенном интерфейсу.
А именно должны быть методы:
- get(request) - обработчик запросов метода GET.
- post(requests) - обработчик запросов метода POST.
- head(requests) - обработчик запросов метода HEAD.
- put(requests) - обработчик запросов метода PUT.
- delete(requests) - обработчик запросов метода DELETE.
- options(requests) - обработчик запросов метода OPTIONS.
Если пришел запрос с методом, который не поддерживается модулем, клиенту ортправляется 405.
Объект requests содержит в себе поле resp - объект класса Responce или наследника, отвечающий за ответ клиенту, который должен быть заполнен в обработчике запроса.
Если response_type был передан, то поле респ указывает на объект известного класса:
response_type | response class |
---|---|
base | Response |
static | StaticResponse |
rest | RestResponse |
Метод add_ws_handler добавляет обработчик соединений по протоколу WebSocket.
Если path не был передан, будет взято значение поля Path у обработчика.
Обработчик обязан быть классом kframe.plugins.xeon.WSHandler или его наследником.
Обработчик будет вызван, только если полностью совпадает заявленный путь и имеется специальный хедер upgrade со хзначением 'websocket'.
Метод add_middleware добавляет target или post в список предобработчиков или постобработчиков соответсвенно, которые будут вызваны между парсингом данных из сокета и вызовом обработчика запроса или после вызова обработчика соответственно.
Предобработчик - вызываемый объект (callable), которому будут переданы два параметра: запрос и обработчик запроса.
Постобработчик - вызываемый объект (callable), которому будут переданы три параметра: запрос, ответ и обработчик запроса, сегенерировавший ответ.
Все предобработчики будут вызваны в той же последовательности, что и были зарегестрированы.
Все постобработчики будут вызваны в обратном порядке, что и были зарегестрированы.
Xeon - реализация протокола WebSocket.
Класс WSHandler имеет необходимый интерфейс для работы с веб-сокетами:
def on_request(self)
def on_validate(self)
def on_end(self)
def handle_incoming_msg(self, message)
def handle_incoming_bin(self, message)
def send_message(self, message)
def send_binay(self, message)
def close(self)
def finish(self)
Метод on_request будет вызван до установки соедниения.
Метод on_validate будет вызван после установки соедниения.
Метод on_end будет вызван прям перед закрытием соединения.
Методы handle_incoming_bin и handle_incoming_msg будут вызваны при получении сообщения бинарного или текстового соотв.
Методы send_message и send_binary отправляют сообщение текстового или бинарного типа соотв.
Метод close будет вызван как сигнал от сервера, что желательно закрыть соединение. Сервер будет ожидать закрытия.
Метод finish корректное закрытие соедиение.
Auth - Для авторизации и поддержки сессий
Этот класс отвечает за генерацию и проверку куки файла.
И этот класс не использует базу данных.
Логика такова, что значение куки - это зашифрованный объект, содержащий в себе идентификаторы юзера, временные метки и некоторые прочие данные.
Класс имеет пользовательский интерфейс в виде двух методов:
def generate_cookie(self, user_id, **kwargs)
def valid_cookie(self, cookie, ip=None)
Первый метод создает значение куки-файла ивозвращает байтовую строку.
Помимо идентификатора юзера (целое число) могут быть переданы:
- expires - целое число секунд сколько будет валидным этот куки с момента генерации
- ip - (строка) ip-адрес клиента. Если передан, то при проверки будет проверяться, что клиент имеет тот же ip
Второй метод принимает байтовую строку и опционально ip адрес клиент.
Результатом будет либо идентификатор юзера, которую принадлежит куки, либо None, если куки невалиден.
Если был передан ip, то будет проверятся, совпадают ли переданный ip и ip при генерации куки.
Stats - Сбор статистики
Объект этого класса выполняет аккумулирующую функцию для данных разных типов.
Имеется пользовательский интерфейс в виде четырех методов:
def init(self, **kwargs)
def init_stat(self, key, type, **kwargs)
def add(self, key, value=None)
def init_and_add(self, key, type, value=None, **kwargs)
def get(self, key)
def export(self, extension=False)
init может принимать следующие параметры:
- add_neon_handler - default False - добавить в Неон ручку выдачи статистики
- neon_handler_cfg - default {} - набор параметров для модуля расширения неона.
Пример:
-- only_local_hosts - default True - выдача только на запросы с приватных IP (127.0.0.1, 192.168.. итд)
-- stat_url - default '/{parent.name}-admin/stats' - url, по которому будет выдаваться статистика
init_stat - объявляет о начале сбора статистики с ключом key и типа type
Статистика может быть типов:
- aver - среднее арифметическое на основе последних 500 замеров. (можно изменить передав count в kwargs)
- single - имеет единственное значение.
- inc - счетчик. (можно изменить наш счетчика, передав increment в kwargs)
- sum - сумматор.
- event - сохраняет события с их временными метками, автроматически удаляет устаревшие (поле limit)
- event_counter - сохраняет кол-во событый произошедших в каждую секунду с их временными метками, автроматически удаляет устаревшие (поле limit)
Так же можно сразу инициализировать начальное значение, передав default в kwargs.
Можно придать каждой статистике пояснение передав desc в kwargs.
Метод add добавляет замер value в статистику c ключом key.
Для счетчика можно не передавать значение.
Метод init_and_add работает как метод add но, если сбор ститистики с таким клюсом еще не иничиализирован, то будет вызван метод init_stat
Метод get вернет текущие статистические данные для ключа key, либо None, если нет статистики по ключу.
(aver посчитан не будет)
Метод export вернет dict, в котором будет вся текущая статистика.
(aver будет посчитан)
Если был поднят флаг extension, то значение кажой статистики будет сопровождаться пояснением переданым при регистрации статистики.
Mchunk - Хранение секретов
Логика объекта такова, что он не хранит важные данные, а хранит информацию как их получить.
А именно: имеется маска, сгенерированная случанно, и результат XOR маски и важных данных.
Методы объекта этого класса:
def set(self,data)
def mask(self)
def unmask(self)
def get(self)
set задает данные, которые надо хранить, и генерирует для них маску
mask - маскирует данные.
unmask - получает исходные данные.
get - возвращает данные в текущем состоянии, не меняя их.
Методы set, mask, unmask написаны в стиле ORM - возвращают текущий объект этого класса.
Также класс поддерживает работу с контекстным менеджером
Cache - Временное хранение данных c функцией автоматичексой очисти протухших данных
Функция инициализации принимает параметры:
- auto_clean_in_new_thread - default: False - Если True, то создастся новый тред для проверки тухлости данных
- timeout - default: 1.0 - интервал проверки. Игнорируется, если auto_clean_in_new_thread == False
- save_file - default: "cache.json" - имя файла для временного сохранения данных
- autosave - default: False - если True, автоматически сбрасывает данные в файл. Игнорируется, если auto_clean_in_new_thread == False
Eventer - Регистратор событий
Как кэш, но один тред, и сложность и записи, и чтения - константы
Planner - Запускает задачи по расписанию
Список возможныех параметров плагина:
enable_stats (bool), default = False - флаг агрегации статистики в плагине stats
add_neon_handler (bool), default = False - включить поддержку REST API в Neon
neon_handler_cfg (dict), default = {} - параметры модуля расширения neon
возможные параметры модуля расширения:
-- only_local_hosts (bool), default = True - флаг доступа только с приватных адресов
-- stat_url (str), default = '/%s-admin/planner' % parent.name - начало url, по которому модуль будет идентифицироваться
Каждая задача определяется набором обязательных параметров:
- key (str) - уникальный ключ/имя задачи
- target (function) - функция, которая будет запущена
И набором второстепенных параметров: - enable (bool), default = True - флаг участует ли задача в планировании (и соотв будет выполнена)
- hours (int), default = 0 - интервал между запусками в часах (суммируется с значенями интервала в других единицах измерения)
- min (int), default = 0 - интервал между запусками в минутах (суммируется с значенями интервала в других единицах измерения)
- sec (int), default = 0 - интервал между запусками в секундах (суммируется с значенями интервала в других единицах измерения)
- shedule - список кортежей ('HH:MM:SS', 'HH:MM:SS'), default ('00:00:00','23:59:59') - Расписание запусков (с, по)
- calendar - { 'allowed' or 'disallowed': { month as key [1..12] => set of days [1..31] }}
default {} (всегда разрешено) - выделить дни, когда запускать или не запускать. Можно передать только один из двух ключей {allowed/disallowed} - weekdays - {0,..,6} - список номеров дней недели, когда можно запускать задачу. Понедельник = 0, Воскресенье = 6.
- offset (int), default = 0 - начальнй отступ в секундах
- args (list), , default = [] - параметры функции
- kwargs (dict), default = {} - параметры функции
- threading (bool), default = False . если True или 'thread', запустит функцию в новом потоке, если 'process', запустит в новом процессе.
- after (int), default = None . временная метка, раньше которой функция запущена не будет (None - без ограничения)
- times (int), default = None . кол-во раз, которое будет запущена функция (None - без ограничения)
- max_parallel_copies (int), default = None - максиальное кол-во одновременных исполнений задачи (None - без ограничения)
Интерфейс плагина имеет несколько методов:
def registrate(self, **task)
def update_task(self, key, **task)
def delete_task(self, key):
def get_task(self, key):
def run_task(self, key, set_after=False):
def get_running_tasks(self):
def get_shedule(self):
def get_next_task(self):
registrate создает задачу. Возвращает True в случае успеха;
update_task обновляет существующую задачу или создает новую. Возвращает True в случае успеха;
delete_task удаляет задачу, если такая имелась;
get_task возвращает словарь, содержащий всю информацию о задаче, или None в случае неудачи;
get_running_tasks возвращает список задач, которые данный момент исполняются, и когда они были запущены;
get_shedule возвращает список задач, время, когда они будут запущены, и, сколько секунд осталось до запуска;
get_next_task аналогичнен get_shedule, но возвращает информацию только о первой задачи в очереди на исполнение.
Crypto - Библиотека крипто алгоритмов
Имеет в себе алгоритмы:
- хеширования
- генерации общего секрета
- подписи и проверки подписи
- шифрования
Также имеются второстепенные функции такие, как генерации эфимерной пары ключей или генерации случайного инициализирующего вектора для шифрования.
Art - Язык описания данных экономичнее JSON
Интерфейс модуля:
def marshal(data, mask=None, random=True)
def unmarshal(data=None, fd=None, mask=None)
Метод marshal - маршализирует объект в байтовую строку.
Особенность принципа кодирования такова, что можно неоднозначно маршализировать, но обратное действие всегда будет однозначным.
Флаг random указывает, использовать ли рандов в необднозначностях.
Если передать байтовую строку как аргумент mask, к возвращаемому значению будет применен XOR с этой маской.
Метод unmarshal - создание объект из байтовой строки.
Данные можно передать как строкой, так и потоком данных (файл/сокет).
Если передать байтовую строку как аргумент mask, к возвращаемому значению будет применен XOR с этой маской.
JScheme - Проверка и дополнения объектов.
Содержит всего одну функцию:
def apply(obj, scheme)
obj - имеющийся объект, который необходимо проверить и по возможности дополнить согласно правилам
scheme - правила, по которым проверяется объект.
Принцип описания правил похож на JSON-scheme.
main.py:
#!/usr/bin/env python3
from kframe.base.parent import Parent
from kframe.plugins.neon import Neon
Parent(name='TestApp').add_plugin(target=Neon, kwargs={
'site_directory': '.',
'use_neon_server': True,
'http_port': 8080,
}).init().start()
# open http://127.0.0.1:8080
запуск:
$ ./main.py --stdout --debug
main.py:
#!/usr/bin/env python3
import time
from kframe.base.parent import Parent
from kframe.plugins.planner import Planner
p = Parent(name='TestApp').add_plugin(target=Planner).init()
p.planner.registrate(key='Task-1', target=lambda: print(time.ctime()), sec=5)
p.start() # Here program will stop and wait for end of Planner, who will work untill get SIGINT
p.stop()
запуск:
$ ./main.py --stdout --debug
Установка производится через утилиту pip:
$ pip install git+https://github.com/moff4/kframe.git
или через ручную сборку:
$ python3 setup.py build
$ python3 setup.py install
- pygost - Криптографическая библиотека
- mysql-connector - драйвер MySQL