py.lib
34:84b54a8a6d4c Browse Files
+ Возможность обработки параметров конфигурации перед добавлением в класс конфигурации . Переформатирование части кода по PEP
config_parse_helper.py dataclass_utils.py db/ldap.py db/sqlite.py error.py webapp/jwt_util.py webapp/url.py webapp/win.py
1.1 --- a/config_parse_helper.py Sat Aug 13 20:06:47 2022 +0300 1.2 +++ b/config_parse_helper.py Sun Aug 14 12:19:08 2022 +0300 1.3 @@ -1,7 +1,7 @@ 1.4 # coding: utf-8 1.5 from configparser import ConfigParser 1.6 from dataclasses import is_dataclass, fields, Field, MISSING 1.7 -from typing import Iterable, Optional, Dict, Any, List 1.8 +from typing import Iterable, Optional, Dict, Any, List, Callable 1.9 from threading import RLock 1.10 from os import getenv 1.11 1.12 @@ -99,11 +99,14 @@ 1.13 self.ph = parser_helper_object 1.14 self.params = {} 1.15 1.16 - def _add_param(self, param_name: str, param_val: Any): 1.17 + def _add_param(self, param_name: str, param_val: Any, parser: Optional[Callable[[Any], Any]] = None): 1.18 """\ 1.19 Непосредственное добавление полученного параметра со всеми проверками. 1.20 """ 1.21 1.22 + if parser is not None and param_val is not None: 1.23 + param_val = parser(param_val) 1.24 + 1.25 fld = self.ph.fields.get(param_name) 1.26 if not isinstance(fld, Field): 1.27 raise ConfigParseHelperError(f'В классе данных отсутствует свойство "{param_name}", ' 1.28 @@ -138,7 +141,7 @@ 1.29 if exc_type is None: 1.30 self.ph.add_params(self.params) 1.31 1.32 - def get(self, config_prop_name: str, dc_prop_name: str): 1.33 + def get(self, config_prop_name: str, dc_prop_name: str, parser: Optional[Callable[[Any], Any]] = None): 1.34 raise NoSectionNotification() 1.35 1.36 1.37 @@ -152,13 +155,14 @@ 1.38 self.section_name = section 1.39 self.section = parser_helper_object.conf_parser[section] 1.40 1.41 - def get(self, config_prop_name: str, dc_prop_name: str): 1.42 + def get(self, config_prop_name: str, dc_prop_name: str, parser: Optional[Callable[[Any], Any]] = None): 1.43 """\ 1.44 :param config_prop_name: Имя опции в файле конфигурации 1.45 :param dc_prop_name: Имя свойства в классе данных, хранящем конфигурацию 1.46 + :param parser: Исполнимый обработчик значения, перед его помещением в конфигурацию 1.47 """ 1.48 try: 1.49 - self._add_param(dc_prop_name, self.section.get(config_prop_name)) 1.50 + self._add_param(dc_prop_name, self.section.get(config_prop_name), parser) 1.51 1.52 except ConfigParseHelperError as e: 1.53 raise ConfigParseHelperError(f'Ошибка при разборе параметра "{config_prop_name}" ' 1.54 @@ -170,14 +174,15 @@ 1.55 Класс для разбора переменных окружения в том же ключе, что и файла конфигурации 1.56 """ 1.57 1.58 - def get(self, env_name: str, dc_prop_name: str): 1.59 + def get(self, env_name: str, dc_prop_name: str, parser: Optional[Callable[[Any], Any]] = None): 1.60 """\ 1.61 :param env_name: Имя переменной окружения, хранящей опцию 1.62 :param dc_prop_name: Имя свойства в классе данных, хранящем конфигурацию 1.63 + :param parser: Исполнимый обработчик значения, перед его помещением в конфигурацию 1.64 """ 1.65 1.66 try: 1.67 - self._add_param(dc_prop_name, getenv(env_name)) 1.68 + self._add_param(dc_prop_name, getenv(env_name), parser) 1.69 1.70 except ConfigParseHelperError as e: 1.71 raise ConfigParseHelperError(f'Ошибка при получении значения из переменной окружения "{env_name}": {e}') 1.72 @@ -208,23 +213,24 @@ 1.73 raise ConfigParseHelperError(f'Ошибка создания объекта объекта конфигурации, ' 1.74 f'списка объектов конфигурации: {e}') 1.75 1.76 - def get(self, config_prop_name: str, dc_prop_name: str): 1.77 + def get(self, config_prop_name: str, dc_prop_name: str, parser: Optional[Callable[[Any], Any]] = None): 1.78 """\ 1.79 Подготавливаем список соответствия названий параметров в секции конкретным свойствам данного 1.80 в помощник класса 1.81 1.82 :param config_prop_name: Имя опции в файле конфигурации 1.83 :param dc_prop_name: Имя свойства в классе данных, хранящем конфигурацию 1.84 + :param parser: Исполнимый обработчик значения, перед его помещением в конфигурацию 1.85 """ 1.86 1.87 - self.ident_list.append((config_prop_name, dc_prop_name)) 1.88 + self.ident_list.append((config_prop_name, dc_prop_name, parser)) 1.89 1.90 def get_config_objects(self) -> List[object]: 1.91 for section in self.sections: 1.92 try: 1.93 with CPHSection(self, section) as section_helper: 1.94 - for conf_prop, dc_prop in self.ident_list: 1.95 - section_helper.get(conf_prop, dc_prop) 1.96 + for conf_prop, dc_prop, parser in self.ident_list: 1.97 + section_helper.get(conf_prop, dc_prop, parser) 1.98 1.99 except ConfigParseHelperError as e: 1.100 raise ConfigParseHelperError(f'Ошибка при разборе секции "{section}": {e}')
2.1 --- a/dataclass_utils.py Sat Aug 13 20:06:47 2022 +0300 2.2 +++ b/dataclass_utils.py Sun Aug 14 12:19:08 2022 +0300 2.3 @@ -66,7 +66,7 @@ 2.4 raise ValueError('Не удалось привести значение к типу') 2.5 2.6 2.7 -def dataobj_extract(obj: Union[object, Dict[str, Any]], dataclass_type: type) -> dataclass: 2.8 +def dataobj_extract(obj: Union[object, Dict[str, Any]], dataclass_type: type) -> object: 2.9 """\ 2.10 Извлекает объект данных из предоставленного объекта, путём получения из него 2.11 указанных в классе данных аттрибутов и поиска их в данном объекте.
3.1 --- a/db/ldap.py Sat Aug 13 20:06:47 2022 +0300 3.2 +++ b/db/ldap.py Sun Aug 14 12:19:08 2022 +0300 3.3 @@ -2,83 +2,83 @@ 3.4 3.5 from ldap3 import Server, Connection, SIMPLE, SUBTREE 3.6 3.7 -LDAP_PAGE=1000 3.8 +LDAP_PAGE = 1000 3.9 + 3.10 3.11 class LdapError(Exception): pass 3.12 3.13 -class LdapRes(): 3.14 - def __init__(self, dn, attrib): 3.15 - self.dn = dn 3.16 - self.attr = attrib 3.17 - 3.18 - def __getitem__(self, item): 3.19 - return self.attr[item] 3.20 - 3.21 - def __iter__(self): 3.22 - return iter(self.attr) 3.23 - 3.24 - def __repr__(self): 3.25 - return '<LdapRes: dn: %s>' % self.dn 3.26 - 3.27 - @classmethod 3.28 - def fromLdapQuery(cls, q): 3.29 - if not isinstance(q, dict): 3.30 - raise LdapError('LdapRes: Parsing Error, not ldap response item') 3.31 - if not (('dn' in q) and ('attributes' in q)): 3.32 - raise LdapError('LdapRes: Parsing Error, format mismatch') 3.33 - 3.34 - return cls(q['dn'], q['attributes']) 3.35 + 3.36 +class LdapRes: 3.37 + def __init__(self, dn, attrib): 3.38 + self.dn = dn 3.39 + self.attr = attrib 3.40 + 3.41 + def __getitem__(self, item): 3.42 + return self.attr[item] 3.43 + 3.44 + def __iter__(self): 3.45 + return iter(self.attr) 3.46 + 3.47 + def __repr__(self): 3.48 + return '<LdapRes: dn: %s>' % self.dn 3.49 + 3.50 + @classmethod 3.51 + def fromLdapQuery(cls, q): 3.52 + if not isinstance(q, dict): 3.53 + raise LdapError('LdapRes: Parsing Error, not ldap response item') 3.54 + if not (('dn' in q) and ('attributes' in q)): 3.55 + raise LdapError('LdapRes: Parsing Error, format mismatch') 3.56 + 3.57 + return cls(q['dn'], q['attributes']) 3.58 + 3.59 3.60 -class Ldap(): 3.61 - def __init__(self, host, user, passwd, timeout=60, queryTimeout=300, **kwa): 3.62 - if 'baseDN' in kwa: 3.63 - self._baseDN = kwa['baseDN'] 3.64 - del kwa['baseDN'] 3.65 - else: 3.66 - self._baseDN = None 3.67 - ldapSrv = Server(host, connect_timeout=timeout, **kwa) 3.68 - self._conn = self._makeConnFabric(ldapSrv, authentication=SIMPLE, 3.69 - user=user, password=passwd, 3.70 - check_names=True, lazy=True, 3.71 - auto_referrals=False, raise_exceptions=True, auto_range=True 3.72 - ) 3.73 - self.queryTimeout = queryTimeout 3.74 - 3.75 - def __call__(self, filter, attrib, queryTimeout=None, baseDN = None): 3.76 - if baseDN is None: 3.77 - if self._baseDN is None: 3.78 - raise LdapError('No base dn on query execution') 3.79 - baseDN = self._baseDN 3.80 - if queryTimeout is None: 3.81 - queryTimeout = self.queryTimeout 3.82 - try: 3.83 - conn = self._conn() 3.84 - with conn: 3.85 - conn.open() 3.86 - conn.bind() 3.87 - 3.88 - res = conn.extend.standard.paged_search(baseDN, 3.89 - filter, attributes=attrib, paged_size=LDAP_PAGE, generator=False, 3.90 - search_scope=SUBTREE, time_limit=queryTimeout 3.91 - ) 3.92 +class Ldap: 3.93 + def __init__(self, host, user, passwd, timeout=60, queryTimeout=300, **kwa): 3.94 + if 'baseDN' in kwa: 3.95 + self._baseDN = kwa['baseDN'] 3.96 + del kwa['baseDN'] 3.97 + else: 3.98 + self._baseDN = None 3.99 + ldapSrv = Server(host, connect_timeout=timeout, **kwa) 3.100 + self._conn = self._makeConnFabric(ldapSrv, authentication=SIMPLE, 3.101 + user=user, password=passwd, 3.102 + check_names=True, lazy=True, 3.103 + auto_referrals=False, raise_exceptions=True, auto_range=True 3.104 + ) 3.105 + self.queryTimeout = queryTimeout 3.106 3.107 - for i in res: 3.108 - if i['type'] == 'searchResEntry': 3.109 - yield LdapRes.fromLdapQuery(i) 3.110 - 3.111 - except Exception as e: 3.112 - raise LdapError("Error on get data (%s): %s" % (type(e), str(e)), *e.args[1:]) 3.113 - 3.114 - def getList(self, *a, **kwa): 3.115 - return [ i for i in self(*a, **kwa) ] 3.116 - 3.117 - @staticmethod 3.118 - def _makeConnFabric(*a, **kwa): 3.119 - def _func(): 3.120 - return Connection(*a, **kwa) 3.121 - 3.122 - return _func 3.123 - 3.124 - 3.125 - 3.126 - 3.127 \ No newline at end of file 3.128 + def __call__(self, filter, attrib, queryTimeout=None, baseDN=None): 3.129 + if baseDN is None: 3.130 + if self._baseDN is None: 3.131 + raise LdapError('No base dn on query execution') 3.132 + baseDN = self._baseDN 3.133 + if queryTimeout is None: 3.134 + queryTimeout = self.queryTimeout 3.135 + try: 3.136 + conn = self._conn() 3.137 + with conn: 3.138 + conn.open() 3.139 + conn.bind() 3.140 + 3.141 + res = conn.extend.standard.paged_search(baseDN, 3.142 + filter, attributes=attrib, paged_size=LDAP_PAGE, 3.143 + generator=False, 3.144 + search_scope=SUBTREE, time_limit=queryTimeout 3.145 + ) 3.146 + 3.147 + for i in res: 3.148 + if i['type'] == 'searchResEntry': 3.149 + yield LdapRes.fromLdapQuery(i) 3.150 + 3.151 + except Exception as e: 3.152 + raise LdapError("Error on get data (%s): %s" % (type(e), str(e)), *e.args[1:]) 3.153 + 3.154 + def getList(self, *a, **kwa): 3.155 + return [i for i in self(*a, **kwa)] 3.156 + 3.157 + @staticmethod 3.158 + def _makeConnFabric(*a, **kwa): 3.159 + def _func(): 3.160 + return Connection(*a, **kwa) 3.161 + 3.162 + return _func
4.1 --- a/db/sqlite.py Sat Aug 13 20:06:47 2022 +0300 4.2 +++ b/db/sqlite.py Sun Aug 14 12:19:08 2022 +0300 4.3 @@ -2,35 +2,36 @@ 4.4 import sqlite3 4.5 from sqlite3 import Error, IntegrityError 4.6 4.7 + 4.8 class DB: 4.9 - def __init__(self, db_file): 4.10 - self._conn = sqlite3.connect(db_file) 4.11 - self._ex = self._conn.execute 4.12 - self.commit = self._conn.commit 4.13 - self.rollback = self._conn.rollback 4.14 - 4.15 - # DB PREP 4.16 - self._ex("PRAGMA journal=WAL") 4.17 - self._ex("PRAGMA foreign_keys=ON") 4.18 - self.commit() 4.19 - 4.20 - def __del__(self): 4.21 - try: 4.22 - self.rollback() 4.23 - self._conn.close() 4.24 - except: 4.25 - pass 4.26 - 4.27 - def __call__(self, *a, **kwa): 4.28 - cur = self._conn.cursor() 4.29 - cur.execute(*a, **kwa) 4.30 - return cur 4.31 - 4.32 - def cq(self, *a, **wa): 4.33 - try: 4.34 - res = self(*a, **wa) 4.35 - self.commit() 4.36 - return res 4.37 - except Error as e: 4.38 - self.rollback() 4.39 - raise e 4.40 + def __init__(self, db_file): 4.41 + self._conn = sqlite3.connect(db_file) 4.42 + self._ex = self._conn.execute 4.43 + self.commit = self._conn.commit 4.44 + self.rollback = self._conn.rollback 4.45 + 4.46 + # DB PREP 4.47 + self._ex("PRAGMA journal=WAL") 4.48 + self._ex("PRAGMA foreign_keys=ON") 4.49 + self.commit() 4.50 + 4.51 + def __del__(self): 4.52 + try: 4.53 + self.rollback() 4.54 + self._conn.close() 4.55 + except: 4.56 + pass 4.57 + 4.58 + def __call__(self, *a, **kwa): 4.59 + cur = self._conn.cursor() 4.60 + cur.execute(*a, **kwa) 4.61 + return cur 4.62 + 4.63 + def cq(self, *a, **wa): 4.64 + try: 4.65 + res = self(*a, **wa) 4.66 + self.commit() 4.67 + return res 4.68 + except Error as e: 4.69 + self.rollback() 4.70 + raise e
5.1 --- a/error.py Sat Aug 13 20:06:47 2022 +0300 5.2 +++ b/error.py Sun Aug 14 12:19:08 2022 +0300 5.3 @@ -11,4 +11,4 @@ 5.4 return f'{type(err).__name__}({err})' 5.5 5.6 def describe(self): 5.7 - return self.get_describe(self) 5.8 \ No newline at end of file 5.9 + return self.get_describe(self)
6.1 --- a/webapp/jwt_util.py Sat Aug 13 20:06:47 2022 +0300 6.2 +++ b/webapp/jwt_util.py Sun Aug 14 12:19:08 2022 +0300 6.3 @@ -4,7 +4,6 @@ 6.4 from datetime import datetime, timedelta 6.5 from typing import Optional 6.6 6.7 - 6.8 JWT_HASH_ALGO = 'HS512' 6.9 6.10 6.11 @@ -55,4 +54,4 @@ 6.12 def f(): 6.13 return cls(key=key) 6.14 6.15 - return f 6.16 \ No newline at end of file 6.17 + return f
7.1 --- a/webapp/url.py Sat Aug 13 20:06:47 2022 +0300 7.2 +++ b/webapp/url.py Sun Aug 14 12:19:08 2022 +0300 7.3 @@ -5,8 +5,10 @@ 7.4 from base64 import urlsafe_b64encode as _b64e, urlsafe_b64decode as _b64d 7.5 from urllib.parse import quote as urlquote 7.6 7.7 + 7.8 def encode(buf): 7.9 - return _b64e(buf.encode('UTF-8')).decode('UTF-8') 7.10 - 7.11 + return _b64e(buf.encode('UTF-8')).decode('UTF-8') 7.12 + 7.13 + 7.14 def decode(buf): 7.15 - return _b64d(buf.encode('UTF-8')).decode('UTF-8') 7.16 + return _b64d(buf.encode('UTF-8')).decode('UTF-8')
8.1 --- a/webapp/win.py Sat Aug 13 20:06:47 2022 +0300 8.2 +++ b/webapp/win.py Sun Aug 14 12:19:08 2022 +0300 8.3 @@ -6,67 +6,70 @@ 8.4 from bottle import Bottle as App, request as Req, response as Ans, SimpleTemplate as BottleTemplate, abort as Abort 8.5 from os.path import dirname, abspath as pAbs, split as pSplit, join as pJoin 8.6 8.7 + 8.8 # --- CLASSES --- 8.9 class ReqEnv: 8.10 - def __init__(self): 8.11 - self.urlBase = Req.environ.get('SCRIPT_NAME', '') 8.12 - 8.13 - def getUrl(self, url): 8.14 - url = str(url) 8.15 - buf = self.urlBase 8.16 - buf += url if url.startswith('/') else '/' + url 8.17 - return buf 8.18 - 8.19 - def __getitem__(self, key): 8.20 - return Req.environ[key] 8.21 - 8.22 - def __iter__(self): 8.23 - for key in Req.environ.keys(): 8.24 - yield key 8.25 - 8.26 - @staticmethod 8.27 - def abort(*a, **ka): 8.28 - Abort(*a, **ka) 8.29 - 8.30 - @staticmethod 8.31 - def get(self): 8.32 - return Req.GET 8.33 - 8.34 - @staticmethod 8.35 - def post(): 8.36 - return Req.POST 8.37 - 8.38 + def __init__(self): 8.39 + self.urlBase = Req.environ.get('SCRIPT_NAME', '') 8.40 + 8.41 + def getUrl(self, url): 8.42 + url = str(url) 8.43 + buf = self.urlBase 8.44 + buf += url if url.startswith('/') else '/' + url 8.45 + return buf 8.46 + 8.47 + def __getitem__(self, key): 8.48 + return Req.environ[key] 8.49 + 8.50 + def __iter__(self): 8.51 + for key in Req.environ.keys(): 8.52 + yield key 8.53 + 8.54 + @staticmethod 8.55 + def abort(*a, **ka): 8.56 + Abort(*a, **ka) 8.57 + 8.58 + @staticmethod 8.59 + def get(self): 8.60 + return Req.GET 8.61 + 8.62 + @staticmethod 8.63 + def post(): 8.64 + return Req.POST 8.65 + 8.66 + 8.67 class Env: 8.68 - def __init__(self, scFile): 8.69 - self.wd = pSplit(dirname(pAbs(scFile)))[0] 8.70 - self.static = pJoin(self.wd, 'static') 8.71 - 8.72 - def http(self, tplName): 8.73 - return BottleTemplate(name=pJoin(self.wd, 'http', '%s.html' % tplName)).render 8.74 - 8.75 - def db(self): 8.76 - """ 8.77 + def __init__(self, scFile): 8.78 + self.wd = pSplit(dirname(pAbs(scFile)))[0] 8.79 + self.static = pJoin(self.wd, 'static') 8.80 + 8.81 + def http(self, tplName): 8.82 + return BottleTemplate(name=pJoin(self.wd, 'http', '%s.html' % tplName)).render 8.83 + 8.84 + def db(self): 8.85 + """ 8.86 Путь к базе SQLite, иные типы БД дожны иметь более 8.87 серьёзные средства авторизации 8.88 """ 8.89 - return pJoin(self.wd, 'db.sqlite') 8.90 - 8.91 - @staticmethod 8.92 - def httpTpl(buf): 8.93 - return BottleTemplate(buf).render 8.94 - 8.95 - @staticmethod 8.96 - def app(): 8.97 - return App() 8.98 - 8.99 - @staticmethod 8.100 - def appRun(app): 8.101 - app.run(server='cgi') 8.102 - 8.103 + return pJoin(self.wd, 'db.sqlite') 8.104 + 8.105 + @staticmethod 8.106 + def httpTpl(buf): 8.107 + return BottleTemplate(buf).render 8.108 + 8.109 + @staticmethod 8.110 + def app(): 8.111 + return App() 8.112 + 8.113 + @staticmethod 8.114 + def appRun(app): 8.115 + app.run(server='cgi') 8.116 + 8.117 + 8.118 # --- PROCS --- 8.119 def runApp(app): 8.120 - Env.appRun(app) 8.121 - 8.122 + Env.appRun(app) 8.123 + 8.124 + 8.125 def Template(buf): 8.126 - return Env.httpTpl(buf) 8.127 - 8.128 \ No newline at end of file 8.129 + return Env.httpTpl(buf)