def test_config_primary(self): config = Config(use_web=False) # Read self.assertEqual(config.get("SelfTest", "test1", "arg1", "3"), "1") self.assertEqual(config.get("SelfTest", "test2", "arg3", "2"), "2") # Add Node self.assertEqual(config.set("SelfTest", "test3", "arg1", "1"), None) self.assertEqual(config.get("SelfTest", "test3", "arg1", "2"), "1") # Save and reload config.save() config = Config(use_web=False) # Check Now node self.assertEqual(config.get("SelfTest", "test3", "arg1", "2"), "1") # Remove self.assertEqual(config.remove("SelfTest", "test3", "arg1"), None) self.assertEqual(config.remove("SelfTest", "test3", ""), None) self.assertEqual(config.get("SelfTest", "test3", "arg1", "2"), "2") config.save()
class Application(Gtk.Application): __gsignals__ = { 'login': (GObject.SIGNAL_RUN_FIRST, None, (int, )), 'logout': (GObject.SIGNAL_RUN_FIRST, None, ()), 'log': (GObject.SIGNAL_RUN_FIRST, None, (str, )) } def __init__(self): Gtk.Application.__init__( self, flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE) self.config = Config(path="./config.xml") self.database = None self.session = None self.windowMain = None self.windowSplash = None self._logging_init() self._check_gui() def _logging_init(self): threading.current_thread().name = 'main' logging.basicConfig( level=int(self.config.get("logging", "console", "level", "10")), stream=sys.stdout, format= '%(asctime)s [%(module)18s] [%(funcName)19s] [%(lineno)4d] [%(levelname)7s] [%(threadName)4s] %(message)s' ) log_handler_gui = IGUI.LogHandler(self) log_handler_gui.setLevel( int(self.config.get("logging", "splash", "level", "20"))) log_handler_console = logging.StreamHandler() log_handler_console.setLevel( int(self.config.get("logging", "console", "level", "10"))) log_handler_console.setFormatter( logging.Formatter( '%(asctime)s [%(module)18s] [%(funcName)19s] [%(lineno)4d] [%(levelname)7s] [%(threadName)4s] %(message)s' )) if bool(int(self.config.get("logging", "", "use_file", "0"))): log_handler_file = logging.handlers.TimedRotatingFileHandler( self.config.get("logging", "file", "path", "4gain.log"), when=self.config.get("logging", "file", "when", "d"), interval=int( self.config.get("logging", "file", "interval", "1")), backupCount=int( self.config.get("logging", "file", "count", "1"))) log_handler_file.setLevel( int(self.config.get("logging", "file", "level", "10"))) log_handler_file.setFormatter( logging.Formatter( '%(asctime)s [%(module)18s] [%(funcName)19s] [%(lineno)4d] [%(levelname)7s] [%(threadName)4s] %(message)s' )) logging.getLogger('').addHandler(log_handler_file) if bool(int(self.config.get("logging", "", "use_syslog", "0"))): log_handler_syslog = logging.handlers.SysLogHandler(address=( self.config.get("logging", "syslog", "address_ip", "127.0.0.1"), int(self.config.get("logging", "syslog", "address_port", "514")))) log_handler_syslog.setLevel( int(self.config.get("logging", "file", "level", "10"))) log_handler_syslog.setFormatter( logging.Formatter( '%(asctime)s [%(module)18s] [%(funcName)19s] [%(lineno)4d] [%(levelname)7s] [%(threadName)4s] %(message)s' )) logging.getLogger('').addHandler(log_handler_syslog) logging.getLogger('').addHandler(log_handler_gui) # logging.getLogger('').addHandler(log_handler_console) def _check_gui(self): if int(self.config.get("gui", "", "gtk_major", "0")) == int(Gtk.get_major_version()) \ and int(self.config.get("gui", "", "gtk_minor", "0")) == int(Gtk.get_minor_version()) \ and int(self.config.get("gui", "", "gtk_micro", "0")) == int(Gtk.get_micro_version()): log.debug("Рабочая версия GTK.") IGUI.ResourceChecker(self) return log.critical("Warning: GTK version mistmatch!") log.critical("Current GTK version is: {0}.{1}.{2}".format( Gtk.get_major_version(), Gtk.get_minor_version(), Gtk.get_micro_version())) log.critical("Need GTK version is: {0}.{1}.{2}".format( self.config.get("gui", "", "gtk_major", "0"), self.config.get("gui", "", "gtk_minor", "0"), self.config.get("gui", "", "gtk_micro", "0"))) self.quit() def _database_init(self): raise Exception("Не реализовано") def _database_close(self): raise Exception("Не реализовано") def _splash_load(self, application_onload=True): if bool(int(self.config.get("gui", "", "use_splash", "0"))): if self.windowSplash is not None: self.add_window(self.windowSplash.window) def _splash_unload(self): if bool(int(self.config.get("gui", "", "use_splash", "0"))): if self.windowSplash is not None: self.windowSplash.window.hide() self.remove_window(self.windowSplash.window) def _session_init(self): raise Exception("Не реализовано") def _session_close(self): raise Exception("Не реализовано") def _synchronizers_init(self): raise Exception("Не реализовано") def _synchronizers_run(self): raise Exception("Не реализовано") def _synchronizers_stop(self): raise Exception("Не реализовано") def _main_prepare(self): self._database_init() self._session_init() self._synchronizers_init() self._synchronizers_run() def _main_load(self): log.info("Загрузка основного окна") if self.windowMain is not None: self.windowMain.window.connect('delete-event', self._main_destroy) self.add_window(self.windowMain.window) self.windowMain.window.present() else: log.critical("Main Window не предопределен") self._main_unload() def _main_destroy(self, widget, args): log.info("Выгрузка основного окна") if self.windowMain is not None: self.windowMain.window.hide() self.remove_window(self.windowMain.window) if bool(int(self.config.get("gui", "", "use_splash", "0"))): self._splash_load(application_onload=False) else: self._main_unload() def _main_unload(self): self._synchronizers_stop() self._session_close() self._database_close() self.quit() def do_startup(self): log.debug("Application do_startup") Gtk.Application.do_startup(self) def do_activate(self): log.debug("Application do_activete") Gtk.Application.do_activate(self) if bool(int(self.config.get("gui", "", "use_splash", "0"))): self._splash_load() else: self._main_prepare() self._main_load() def do_run_mainloop(self): Gtk.Application.do_run_mainloop(self) def do_command_line(self, command_line): options = command_line.get_options_dict() if options.contains("test"): # This is printed on the main instance print("Test argument recieved") self.activate() return 0 def on_about(self, action, param): about_dialog = Gtk.AboutDialog(transient_for=self.window, modal=True) about_dialog.present() def on_quit(self, action, param): log.info("Завершение приложения") self.quit()
class SchemaDisplay: """ Класс визуализации модели базы данных на основе ORM SQLAlchemy. Для работы необходимы: ``pydot``, ``graphviz``. Параметры конфигурационного файла: .. code-block:: xml :linenos: <?xml version="1.0" encoding="UTF-8"?> <configuration> <sphinx> <AutoDocSchemaDisplay path_graph="$path/share/sphinx/static/schema_graph.png" /> </sphinx> </configuration> """ def __init__(self): self.config = Config() self.path_graph = self.config.get("sphinx", "AutoDocSchemaDisplay", "path_graph", "") # Инициализация параметров логирования. logging.basicConfig( level=int(self.config.get("logging", "console", "level", "10")), stream=sys.stdout, format= '%(asctime)s [%(module)15s] [%(funcName)19s] [%(lineno)4d] [%(levelname)7s] [%(threadName)4s] %(message)s' ) def render_schema(self): """ Отрисовка структуры схемы БД. Записывает построенный график в фаил. :return: None """ from sqlalchemy_schemadisplay import create_uml_graph, create_schema_graph from sqlalchemy.orm import class_mapper log.info("Формирование списка таблиц.") mappers = [] for schema_name in dir(Schema): if schema_name[0] == '_': continue if schema_name in ['Base', 'log']: continue schema_class = getattr(Schema, schema_name) if hasattr(schema_class, "__tablename__"): log.debug(" Анализ схемы: {}".format(schema_name)) mappers.append(class_mapper(schema_class)) log.info("Формирование окружения GraphViz.") graph = create_uml_graph(mappers, show_inherited=False, show_operations=False, show_datatypes=False, show_multiplicity_one=False) log.info("Формирование графика.") graph.write_png(self.path_graph) log.info("Формирование графика завершено.")
class Application(Flask): """ При инициализации, загружается конфигурационный фаил на основе данных которого инициализируется Flask. Для инициализации требуются следующий параметры в конфигурационном файле: #. **site_id** - Идентификатор сайта в базе данных, подробнее в описании таблицы :ref:`module-interfaces-mysql-schema-web`. #. **import_name** - Имя импортирования во Flask (оставлять не изменным) #. **static_url_path** - Относительный путь до папки со статическими файлами #. **static_folder** - Полный путь до папки со статическими файлами сайта #. **static_folder_master** - Полный путь до папки со статическими файлами проекта, если будет запрощен статический фаил, и он не будет найден в папке сайта, то будет произведен поиск файла из данной папки #. **template_folder** - Полный путь до папки с Jinja2 шаблонами сайта #. **template_folder_master** - Полный путь до папки с Jinja2 шаблонами проекта, если будет запрощен шаблон, и он не будет найден в папке сайта, то будет произведен поиск файла из данной папки Пример "второго" (подробнее :ref:`module-interfaces-config` и :ref:`example-config-4gw`) конфигурационного файла: .. code-block:: xml :linenos: <?xml version="1.0" encoding="UTF-8"?> <configuration> <web site_id="1"> <application import_name="forGain" static_url_path="/static" static_folder="/home/apache/pro-4gain-www/.4gain.web/static" static_folder_master="/home/apache/pro-4gain-www/.4gain/share/static" template_folder="/home/apache/pro-4gain-www/.4gain.web/templates/default" template_folder_master="/home/apache/pro-4gain-www/.4gain/share/templates/default"/> </web> </configuration> """ _config_file = "./config.xml" def __init__(self): """ """ self._config = Config(path=self._config_file) self._logging_init() self.__register_modules = {} self.__import_name = self._config.get("web", "application", "import_name", "forGain") self.__static_url_path = self._config.get("web", "application", "static_url_path", "/static") self.__static_folder = self._config.get("web", "application", "static_folder", "/static") self.__static_folder_master = self._config.get("web", "application", "static_folder_master", "/static") self.__template_folder = self._config.get("web", "application", "template_folder", "/templates") self.__template_folder_master = self._config.get( "web", "application", "template_folder_master", "/templates") super(Application, self).__init__(import_name=self.__import_name, static_url_path=self.__static_url_path, static_folder=self.__static_folder, template_folder=self.__template_folder) # Config settings for debug self.config['DEBUG'] = True self.config['TEMPLATES_AUTO_RELOAD'] = True # Вынести настройку данных массивов в module_register self.__render_callback_modules = [ 'Session', 'Permissions', 'Account', 'Analytics' ] self.__render_callback_modules_json = ['Permissions', 'Account'] self._database = WebDatabase(self) self._site = WebSite(self) self._register_extensions() self._register_filters() self._register_hooks() self._register_errors() self._register_modules() self._register_pages() # Активируем глобальные дополнения отладки # ExtensionDebugToolbar(self) # Закрываем транзакцию. self._database_unload() @property def site(self): """ Свойство возвращающее текущий сайт. :return: Загруженный обьект :ref:`module-interfaces-web-site` :rtype: :ref:`module-interfaces-web-site` """ return self._site @property def config_xml(self): """ Свойство возвращающее текущий конфиг. :return: Загруженный обьект :ref:`module-interfaces-config` :rtype: :ref:`module-interfaces-config` """ return self._config @property def database(self): """ Свойство возвращающее текущeую базу данных. :return: Загруженный обьект :ref:`module-interfaces-web-database` :rtype: :ref:`module-interfaces-web-database` """ return self._database def _logging_init(self): """ Функция инициализации подсистемы логирования :return: None :rtype: Nothing """ threading.current_thread().name = 'main' logging.basicConfig( level=int(self._config.get("logging", "console", "level", "10")), stream=sys.stdout, format= '%(asctime)s [{}] [%(module)15s] [%(funcName)21s] [%(lineno)4d] [%(levelname)7s] [%(threadName)10s] %(message)s' .format(int(self._config.get("web", "", "site_id", "-1")))) log_handler_console = logging.StreamHandler() log_handler_console.setLevel( int(self._config.get("logging", "console", "level", "10"))) log_handler_console.setFormatter( logging.Formatter( '%(asctime)s [{}] [%(module)15s] [%(funcName)21s] [%(lineno)4d] [%(levelname)7s] [%(threadName)10s] %(message)s' .format(int(self._config.get("web", "", "site_id", "-1"))))) if bool(int(self._config.get("logging", "", "use_file", "0"))): log_handler_file = logging.handlers.TimedRotatingFileHandler( self._config.get("logging", "file", "path", "4gain.log"), when=self._config.get("logging", "file", "when", "d"), interval=int( self._config.get("logging", "file", "interval", "1")), backupCount=int( self._config.get("logging", "file", "count", "1"))) log_handler_file.setLevel( int(self._config.get("logging", "file", "level", "10"))) log_handler_file.setFormatter( logging.Formatter( '%(asctime)s [{}] [%(module)15s] [%(funcName)21s] [%(lineno)4d] [%(levelname)7s] [%(threadName)10s] %(message)s' .format(int(self._config.get("web", "", "site_id", "-1"))))) logging.getLogger('').addHandler(log_handler_file) if bool(int(self._config.get("logging", "", "use_syslog", "0"))): log_handler_syslog = logging.handlers.SysLogHandler( address=(self._config.get("logging", "syslog", "address_ip", "127.0.0.1"), int( self._config.get("logging", "syslog", "address_port", "514")))) log_handler_syslog.setLevel( int(self._config.get("logging", "file", "level", "10"))) log_handler_syslog.setFormatter( logging.Formatter( '%(asctime)s [{}] [%(module)15s] [%(funcName)21s] [%(lineno)4d] [%(levelname)7s] [%(threadName)10s] %(message)s' .format(int(self._config.get("web", "", "site_id", "-1"))))) logging.getLogger('').addHandler(log_handler_syslog) # logging.getLogger('').addHandler(log_handler_console) log.info("App: Logging Enable.") def _register_extensions(self): """ Загружаем дополнения для Jinja2 окружения, такие как парсеры ополнительных тэгов. :return: None :rtype: Nothing """ log.info("App: Register Extensions.") self.jinja_env.add_extension(JinjaExt.MetaInfoExtension) self.jinja_env.add_extension(JinjaExt.WidgetExtension) self.jinja_env.add_extension(JinjaExt.TimeExtension) def _register_filters(self): """ Загружаем фильтров для Jinja2 окружения, такие как рендер MarkDown'a. :return: None :rtype: Nothing """ log.info("App: Register Filters.") self.jinja_env.filters['markdown'] = JinjaFil.filter_markdown self.jinja_env.filters['shuffle'] = JinjaFil.filter_shuffle self.jinja_env.filters['phonenumber'] = JinjaFil.filter_phonenumber self.jinja_env.filters['money'] = JinjaFil.filter_money def _register_hooks(self): """ Загружаем так называемые хуки или костыли. :return: None :rtype: Nothing """ log.info("App: Register Hooks.") WebModules.HookFavicon(self).register() def _register_modules(self): """ Загружаем основные модули приложения, к примеру поддержку сессий, аналитика, поддержка robots.txt и sitemap.xml :return: None :rtype: Nothing """ log.info("App: Register Modules.") # Core WebModules.Session(self).register() WebModules.Permissions(self).register() # Search Robots WebModules.Robots(self).register() WebModules.Sitemap(self).register() # SEO WebModules.Analytics(self).register() def _register_errors(self): """ Загружаем страницы ошибок 400, 401, 404. :return: None :rtype: Nothing """ log.info("App: Register Errors.") WebModules.Error_400(self).register() # Ошибка в данных запроса WebModules.Error_401(self).register() # Ошибка доступа WebModules.Error_404(self).register() # Страница не найдена def _register_pages(self): """ Загружаем страницы приложения, которые описаны в базе данных. :return: None :rtype: Nothing """ log.info("App: Register Pages.") for page in self._site.pages: if page.is_enable: page.module.register() def _database_unload(self): """ Функция закрытия первичного соеденения с БД, используемого для загрузки информации о сайте и подгружаемых модулях. :return: None :rtype: Nothing """ self._database._context_teardown(None) def render(self, template, web_page, web_module, http_code=200): """ Функция отрисовки страницы на основе шаблона, и переданных классов web_module и web_page. :param template: Путь до шаблона :type template: String :param web_page: Экземпляр класса :ref:`module-interfaces-web-page` :type web_page: :ref:`module-interfaces-web-page` :param web_module: Экземпляр класса :ref:`module-interfaces-web-module` :type web_module: :ref:`module-interfaces-web-module` :param http_code: HTTP Return status code :type http_code: Integer :return: Отрисованное содержание страницы :rtype: String """ self.render_callback(template, web_page, web_module) render_html = render_template(template, site=self._site, page=web_page, module=web_module, widgets={}, meta={}, session=self.module_instance("Session")) # Дополнение, по минимализации выходного HTML кода if self._config.get("web", "addon-html-minifer", "enable", "True") == "True": render_html = htmlmin.minify(render_html, remove_comments=True, remove_empty_space=True) # дополнение, по переформатированию HTML кода для лучшего прочнения человеком if self._config.get("web", "addon-html-beautiful", "enable", "False") == "True": render_html_soup = bs4.BeautifulSoup(render_html, 'html.parser') render_html = render_html_soup.prettify() return render_html, http_code def render_json(self, json_object, web_page, web_module, http_code=200): """ Функция серилизации и отправки JSON. :param json_object: JSON Dict :type json_object: Dict :param web_page: Экземпляр класса :ref:`module-interfaces-web-page` :type web_page: :ref:`module-interfaces-web-page` :param web_module: Экземпляр класса :ref:`module-interfaces-web-module` :type web_module: :ref:`module-interfaces-web-module` :param http_code: HTTP Return status code :type http_code: Integer :return: Отрисованное содержание страницы :rtype: String """ self.render_callback_json(web_page, web_module) return jsonify(json_object), http_code def render_callback(self, template, web_page, web_module): """ Функция вызываемая перед каждой отрисовке шаблона, она перебирает список загруженных модулей помеченных для получения этого вызова. :param template: Путь до шаблона :type template: String :param web_page: Экземпляр класса :ref:`module-interfaces-web-page` :type web_page: :ref:`module-interfaces-web-page` :param web_module: Экземпляр класса :ref:`module-interfaces-web-module` :type web_module: :ref:`module-interfaces-web-module` :return: None """ for instance_name in self.__render_callback_modules: instance_module = self.module_instance(instance_name) if instance_module: instance_module.callback_render(template, web_page, web_module) def render_callback_json(self, web_page, web_module): """ Функция вызываемая перед каждой отсылкой JSON, она перебирает список загруженных модулей помеченных для получения этого вызова. :param web_page: Экземпляр класса :ref:`module-interfaces-web-page` :type web_page: :ref:`module-interfaces-web-page` :param web_module: Экземпляр класса :ref:`module-interfaces-web-module` :type web_module: :ref:`module-interfaces-web-module` :return: None """ for instance_name in self.__render_callback_modules_json: instance_module = self.module_instance(instance_name) if instance_module: instance_module.callback_render(None, web_page, web_module) def module_register(self, name, instance): """ Функция регистрации загружаемого модуля в список загруженных модулей приложения, для последующего быстрого доступа к ним из других модулей. :param name: Уникальное имя модуля :type name: String :param instance: Инициализированный класс модуля, откуда идет вызов :return: None """ if name != "" and name != "Module": if name not in self.__register_modules: self.__register_modules[name] = instance def module_instance(self, name): """ Функция возвращает инициализированный класс из списка загруженныйх модулей по точному совпадению имени. :param name: Уникальное имя загруженного модуля. :type name: String :return: Возвращает инициализированный модуль из списка загруженных """ if name in self.__register_modules: return self.__register_modules[name] else: return None @staticmethod def module_request(name, check_only=False): """ Функция возвращает обьект модуля, сохраненный в переменной запроса. :param name: Уникальное имя загруженного модуля. :type name: String :param check_only: Маркер вызова ошибки если данные в обькте запроса не найдены :type check_only: Boolean :return: Экземпляр класса :ref:`module-interfaces-web-module` :rtype: :ref:`module-interfaces-web-module` """ module = getattr(request, '_4g_{}'.format(name.lower()), None) if module is None and check_only is False: abort(500) return module @staticmethod def module_request_save(name, instance): """ Функция возвращает обьект модуля, сохраненный в переменной запроса. :param name: Уникальное имя загруженного модуля. :return: Экземпляр класса :ref:`module-interfaces-web-module` :rtype: :ref:`module-interfaces-web-module` """ setattr(request, '_4g_{}'.format(name.lower()), instance) def module_exist(self, name): """ Функция возвращает метку наличия загруженного модуля в системе по точному совпадению имени. :param name: Уникальное имя загруженного модуля. :type name: String :return: Возвращает метку наличия загруженного модуля в системе :rtype: Boolean """ if name in self.__register_modules: return True else: return False def module_search(self, name, load_first=False): """ Функция поиска загруженного модуля из списка загруженных. :param name: Уникальное имя загруженного модуля. :type name: String :param load_first: Если указано значение True, то функция возвращает инициализированный класс, иначе массив имен. :type load_first: Boolean :return: Возвращает инициализированный модуль из списка загруженных или один инициализированный класс. """ module_names = [] for module_name in self.__register_modules.keys(): if module_name.find(name) == 0: if load_first: return self.module_instance(module_name) module_names.append(module_name) if load_first: return None else: return module_names def send_static_file(self, filename): """ Функция переопределения поиска статического файла изначально в папке сайте, далее в папке проекта. Если фаил и вовсе не был найден - выдаем ошибку 404. :param filename: Путь запрашеваемого статического файла :type filename: String :return: """ if os.path.exists("{}/{}".format(self.__static_folder, filename)): return send_from_directory(self.__static_folder, filename) if os.path.exists("{}/{}".format(self.__static_folder_master, filename)): return send_from_directory(self.__static_folder_master, filename) return '', 404 @locked_cached_property def jinja_loader(self): """ Функция переопределения штатного загрузчика Flask, для поиска шаблона изначально в папке сайта а потом в папке проекта. :return: Экземпляр Jinja2 FileSystemLoader """ return FileSystemLoader( [self.__template_folder, self.__template_folder_master])