You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

154 line
5.1 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import os
from functools import wraps
from abc import ABCMeta, abstractmethod
from inspect import getcallargs
class Cachable:
'''Базовый класс для создания классов, кэширующих вывод своих методов.
Декоратор @Cachable.method_cached() предназначен для указания методов
с кэшируемым выводом.'''
def __init__(self):
self.clear_method_cache()
def clear_method_cache(self):
self._method_cache = {}
@staticmethod
def method_cached(key=lambda *args, **kwargs: hash(args)):
def decorator(function):
function_name = function.__name__
@wraps(function)
def wrapper(self, *args, **kwargs):
keyval = key(*args, **kwargs)
assert isinstance(self, Cachable)
if function_name not in self._method_cache:
self._method_cache[function_name] = {}
cache = self._method_cache[function_name]
if keyval not in cache:
cache[keyval] = function(self, *args, **kwargs)
return cache[keyval]
@wraps(function)
def null_wrapper(self):
assert isinstance(self, Cachable)
if function_name not in self._method_cache:
self._method_cache[function_name] = function(self)
return self._method_cache[function_name]
if len(function.__code__.co_varnames) > 1:
return wrapper
else:
return null_wrapper
return decorator
class Singleton(type):
'''Метакласс для создания синглтонов.'''
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
class SingletonParam(type):
'''Метакласс для создания синглтонов по параметрам.'''
_instances = {}
_init = {}
def __init__(cls, class_name, base_classes, class_dict):
cls._init[cls] = class_dict.get('__init__', None)
def __call__(cls, *args, **kwargs):
init = cls._init[cls]
if init is not None:
key_value = (cls, frozenset(getcallargs(init, None, *args,
**kwargs).items()))
else:
key_value = cls
if key_value not in cls._instances:
cls._instances[key_value] = super().__call__(*args, **kwargs)
return cls._instances[key_value]
class GenericFS(metaclass=ABCMeta):
'''Абстрактный класс для работы с файловыми системами.'''
@abstractmethod
def exists(self, path):
pass
@abstractmethod
def read(self, path):
pass
@abstractmethod
def glob(self, path):
pass
@abstractmethod
def realpath(self, path):
pass
@abstractmethod
def write(self, path, data):
pass
@abstractmethod
def listdir(self, path, fullpath=False):
pass
def get_traceback_caller(exception_type, exception_object,
exception_traceback):
'''Возвращает имя модуля, в котором было сгенерировано исключение,
и соответствующий номер строки.'''
while exception_traceback.tb_next:
exception_traceback = exception_traceback.tb_next
line_number = exception_traceback.tb_lineno
module_path, module_name = os.path.split(exception_traceback.tb_frame.
f_code.co_filename)
if module_name.endswith('.py'):
module_name = module_name[:-3]
full_module_name = [module_name]
while (module_path and module_path != '/' and not
module_path.endswith('site-packages')):
module_path, package_name = os.path.split(module_path)
full_module_name.insert(0, package_name)
if module_path.endswith('site-packages'):
module_name = '.'.join(full_module_name)
return module_name, line_number
def unique(iterable):
'''Возвращает итерируемый объект, содержащий только уникальные элементы
входного объекта, сохраняя их порядок.
'''
output = []
for element in iterable:
if element not in output:
output.append(element)
return output
def flat_iterable(iterable, types=(list, tuple, map, zip, filter)):
'''Распаковывает все вложенные итерируемые объекты во входном объекте.
Например:
[1, 2, [3, 4, [5, 6], 7], [8, 9], 10] -> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
'''
if isinstance(iterable, types):
for it in iterable:
for sub_iterable in flat_iterable(it, types=types):
yield sub_iterable
else:
yield iterable