from abc import ABC, abstractmethod, abstractclassmethod
from pathlib import Path
import os, sys
sys.path.append(os.path.abspath(os.path.join(__file__, '..')))
from utils import PathType, save_as_json, load_json, auto_type_check, get_info_dict_from_callable, find_callable_by_name
import shutil, os, json
[docs]class Base(ABC):
PATH_TYPE = PathType.BOTH
def __init__(self):
self.load_path = None
[docs] @abstractclassmethod
def check_data(cls, data):
pass
[docs] def validate_data(cls, *args, **kwargs):
try:
cls.check_data(*args, **kwargs)
return True
except:
return False
[docs] def check(self):
self.check_data(self)
@property
def valid(self):
try:
self.check()
return True
except:
return False
[docs] @classmethod
def _check_file_name(cls, file_name):
pass
[docs] @classmethod
def _check_path_to_load(cls, load_path):
path = Path(load_path).resolve()
if not path.exists():
raise FileNotFoundError(f"'{path}' doesn't exists for loading")
elif cls.PATH_TYPE == PathType.FILE and not path.is_file():
raise FileNotFoundError(f'There is no such a file to load: {path}')
elif cls.PATH_TYPE == PathType.DIRECTORY and not path.is_dir():
raise FileNotFoundError(f'There is no such a directory to load: {path}')
cls._check_file_name(path.name)
[docs] @classmethod
def _check_before_load(cls, load_path):
cls._check_path_to_load(load_path)
[docs] @abstractclassmethod
def _load_raw_data(cls, load_path):
pass
[docs] @classmethod
def _check_after_load(cls, raw_data):
pass
[docs] @classmethod
def is_path_loadable(cls, load_path):
try:
cls._check_before_load(load_path)
cls._check_after_load(cls._load_raw_data(load_path))
return True
except:
return False
[docs] @classmethod
def _create_obj_by_raw_data(cls, raw_data):
return cls(raw_data)
[docs] def _update(self, other_obj):
other_t, self_t = type(other_obj), type(self)
if not issubclass(other_t, self_t):
raise TypeError(f"The class '{other_t.__name__}' of the other object is not the subclass of '{self_t.__name__}'.")
for attr_name, attr_value in vars(self).items():
setattr(self, attr_name, getattr(other_obj, attr_name))
[docs] @classmethod
def load_from_disk(cls, load_path):
cls._check_before_load(load_path)
raw_data = cls._load_raw_data(load_path)
cls._check_after_load(raw_data)
new_obj = cls._create_obj_by_raw_data(raw_data)
new_obj.load_path = load_path
return new_obj
[docs] @classmethod
def valid_path_exists(cls, path): # should not validate the internal data to load
try:
cls._check_path_to_load(path)
return True
except:
return False
[docs] def _check_before_save(self, save_path):
path = Path(save_path).resolve()
if not path.parent.is_dir():
raise FileNotFoundError(f"The parent directory to save doesn't exist: {path.parent}")
elif self.valid_path_exists(str(path)):
raise FileExistsError(f'{path} has already existed and cannot be overwritten.')
self._check_file_name(path.name)
[docs] @abstractmethod
def _save(self, save_path):
pass
# if
[docs] def _after_save(self, save_path):
self.load_path = save_path
[docs] @classmethod
def _exist_handler(cls, path):
path = Path(path)
os.remove(str(path)) if path.is_file() else shutil.rmtree(path)
[docs] def save_to_disk(self, save_path, all_overwrite=True):
try:
self._check_before_save(save_path)
except FileExistsError as e:
if all_overwrite:
self._exist_handler(save_path)
else:
raise e
self._save(save_path)
self._after_save(save_path)
[docs] def to_dict(self):
return self.__dict__
[docs] def to_info_dict(self):
result = self.to_dict()
result['valid'] = self.valid
return result
[docs] def __str__(self) -> str:
return json.dumps(self.to_info_dict(), indent=2)
# meta_path = 'D:/documents/AcademicDocuments/customed_python_pkgs/unit-run/tests/test/.meta.json'
# new_meta_path = 'D:/documents/AcademicDocuments/customed_python_pkgs/unit-run/tests/.meta.json'
# meta = Meta.load_from_disk(meta_path)
# print(meta.to_dict())
# meta.save_to_disk(new_meta_path)
# print(meta.to_dict())
# path = Path('tzffaaf.json')
# print(Path(path).suffix)
[docs]class ParameterGroup(Base):
FILE_SUFFIX = '.param_group.json'
PATH_TYPE = PathType.FILE
def __init__(self, data):
self.data = data
super().__init__()
[docs] @classmethod
def check_data(cls, data):
json.dumps(data)
[docs] def check(self):
self.check_data(self.data)
[docs] @classmethod
def get_file_name(cls, name):
return name+cls.FILE_SUFFIX
[docs] @classmethod
def get_path_by_dir_and_name(cls, dir_path, name):
return os.path.join(dir_path, cls.get_file_name(name))
[docs] @classmethod
def get_name_from_path(cls, path):
return Path(path).name.replace(cls.FILE_SUFFIX, '')
[docs] @classmethod
def _check_file_name(cls, file_name):
splits = os.path.basename(file_name).split('.')
err_msg = f"The file suffix of parameter group should be '{cls.FILE_SUFFIX}'"
if len(splits) <= 1:
err_msg += ', instead empty.'
raise NameError(err_msg)
else:
suffix = '.' + '.'.join(splits[-2:])
if suffix != cls.FILE_SUFFIX:
err_msg += f", instead '{suffix}'."
raise NameError(err_msg)
[docs] @classmethod
def _load_raw_data(cls, load_path):
return load_json(load_path)
[docs] @classmethod
def load_by_dir_and_name(cls, dir_path, name):
return cls.load_from_disk(cls.get_path_by_dir_and_name(dir_path, name))
[docs] def _save(self, save_path):
save_as_json(save_path, self.data)
[docs] def save_by_dir_and_name(self, dir_path, name):
self.save_to_disk(self.get_path_by_dir_and_name(dir_path, name))
# dir_path = 'D:/documents/AcademicDocuments/customed_python_pkgs/unit-run/tests'
# group = ParameterGroup(None)
# print(group)
# group.save_by_dir_and_name(dir_path, 'test')
# print(group)
[docs]class Unit(Base):
PATH_TYPE = PathType.DIRECTORY
@Meta.property
def src_path(self):
pass
@Meta.property
def src_name(self):
pass
def __init__(self, src_path, src_name):
self.meta = Meta(src_path, src_name)
self.param_group_map = dict()
super().__init__()
[docs] @classmethod
def check_data(cls, unit):
unit.meta.check()
for group in unit.param_group_map.values():
group.check()
unit.check_src_obj()
[docs] def check_src_obj(self):
if not Path(self.meta.src_path).exists():
raise FileNotFoundError(f'Source not found: {self.meta.src_path}')
if self.src_obj is None:
raise LookupError(f"Cannot find the unit source '{self.meta.src_name}' from '{self.meta.src_path}'")
@property
def src_obj_valid(self):
try:
self.check_src_obj()
return True
except:
return False
@property
def src_obj(self):
return find_callable_by_name(str(Path(self.meta.src_path).resolve()), self.meta.src_name)
[docs] @classmethod
def _check_path_to_load(cls, load_path):
super()._check_path_to_load(load_path)
sub_file_paths = Path(load_path).glob('*')
meta_paths = list(filter(lambda path: Meta.valid_path_exists(path), sub_file_paths))
if len(meta_paths) != 1:
raise LookupError(f'There is no valid meta file to load.')
[docs] @classmethod
def _check_after_load(cls, raw_data):
super()._check_after_load(raw_data)
Meta.check_data(raw_data['meta'])
for key, group in raw_data['param_group_pairs']:
group.check()
[docs] @classmethod
def get_param_group_pairs_from_dir(cls, dir_path):
return [(ParameterGroup.get_name_from_path(str(path)), ParameterGroup.load_from_disk(str(path))) for path in Path(dir_path).glob('*') if ParameterGroup.valid_path_exists(path)]
[docs] @classmethod
def _load_raw_data(cls, load_dir):
meta_load_path = Meta.get_path_by_dir(load_dir)
meta_raw_data = Meta._load_raw_data(meta_load_path)
param_group_pairs = cls.get_param_group_pairs_from_dir(load_dir)
return {
'meta': meta_raw_data,
'param_group_pairs': param_group_pairs
}
[docs] @classmethod
def _create_obj_by_raw_data(cls, raw_data):
obj = cls(raw_data['meta']['src_path'], raw_data['meta']['src_name'])
for key, group in raw_data['param_group_pairs']:
obj.set_param_group(key, group, with_check=False)
return obj
[docs] def _check_before_save(self, save_path):
super()._check_before_save(save_path)
self.check()
[docs] def _save(self, save_dir):
if not Path(save_dir).is_dir():
os.mkdir(save_dir)
else:
pairs = self.get_param_group_pairs_from_dir(save_dir)
for name, path in pairs:
os.remove(path)
self.meta._save(Meta.get_path_by_dir(save_dir))
for name, group in self.param_group_map.items():
group._save(ParameterGroup.get_path_by_dir_and_name(save_dir, name))
[docs] def set_param_group(self, name, group, with_check=True):
if not isinstance(group, ParameterGroup):
group = ParameterGroup(group)
if with_check:
group.check()
self.param_group_map[name] = group
[docs] def set_param_group_from_disk(self, path, new_name=None):
# print(ParameterGroup._load_raw_data(path))
group = ParameterGroup(ParameterGroup._load_raw_data(path))
if new_name is None:
new_name = ParameterGroup.get_name_from_path(path)
# print(new_name)
self.set_param_group(new_name, group, with_check=False)
[docs] def rename_param_group(self, old_name, new_name, overwrite=False):
if new_name in self and not overwrite:
raise NameError(f'New name for paramter group exists: {new_name}')
if old_name not in self:
raise NameError(f"Old name for paramter group doesn't exists: {old_name}")
self.param_group_map[new_name] = self.param_group_map[old_name]
del self.param_group_map[old_name]
[docs] def run(self, group_name):
group = self.param_group_map[group_name]
group.check()
params = group.data
self.meta.check()
self.check_src_obj()
src_obj = self.src_obj
return src_obj(**params) if type(params) == dict else src_obj(*params)
[docs] def _get_param_group_map_dict(self):
r = dict()
for k, v in self.param_group_map.items():
r[k] = v.to_info_dict()
return r
[docs] def to_info_dict(self):
"""The information `dict` for the source unit object
:example:
```python
print(unit)
```
```{code-block} json
{
"meta": {
"src_path": "test.py",
"src_name": "y",
"load_path": null
},
"meta_valid": true,
"src_valid": false,
"src_obj_info": null,
"param_group_map": {
"name1": {
"data": [1, 2],
"load_path": "test/name1.param_group.json",
"valid": true
},
"name2": {
"data": [10, 1],
"load_path": "test/name3.param_group.json",
"valid": true
}
}
}
```
"""
src_obj = self.src_obj
src_valid = src_obj is not None
return {
'meta': self.meta.to_dict(),
'meta_valid': self.meta.valid,
'src_valid': src_valid,
'src_obj_info': None if not src_valid else get_info_dict_from_callable(src_obj),
'param_group_map': self._get_param_group_map_dict()
}
@property
def src_obj_info(self):
self.check_src_obj()
return get_info_dict_from_callable(self.src_obj)
# @property
# def summary(self):
# new_meta = self.unit_info
# new_meta['path'] = self.src_path
# return {
# 'meta': self.unit_info,
# 'param_group_map': self.param_group_map
# }
# """Whether `group_name` `in` param_group_map of Unit
# Args:
# group_name (string): Target parameter group name
# Returns:
# ``bool``: Whether ``group_name`` ``in`` param_group_map of Unit
# Example:
# ``python
# unit = Unit()
# print('name1' in unit)
# ``
# """
[docs] def __contains__(self, group_name):
"""Whether *group_name* `in` param_group_map of Unit
:param group_name: Target parameter group name
:return: Whether *group_name* `in` param_group_map of Unit
:Example:
```python
unit = Unit(src_path, src_name)
print('name1' in unit)
```
"""
return group_name in self.param_group_map
# unit_dir = 'D:/documents/AcademicDocuments/customed_python_pkgs/unit-run/examples/tests/test'
# unit = Unit.load_from_disk(unit_dir)
# print(unit)
# unit.save_to_disk(unit_dir)
# print(unit.run('name3'))
# src_path = 'D:/documents/AcademicDocuments/customed_python_pkgs/unit-run/examples/tests/test.py'
# src_name = 'y'
# @auto_type_check
# def func(a, b:int):
# pass
# print(inspect.getframeinfo(inspect.currentframe()))
# func(1, 'fdaaa')
# print(Path('test.param_group.json').stem)
# unit = Unit(src_path, src_name)
# param_path = 'D:/documents/AcademicDocuments/customed_python_pkgs/unit-run/examples/tests/manual_p_group1.json'
# unit.set_param_group_from_disk(param_path)
# unit.rename_param_group('manual_p_group1', 'example_1')
# print(unit)
# unit.save_to_disk(Path(src_path).parent.joinpath('test').resolve())
# print(unit.src_path, unit.src_name)
# unit_dir = 'D:/documents/AcademicDocuments/customed_python_pkgs/unit-run/tests/test.jioni.fda'
# meta = Meta('steswt', 'tes')
# print(Path(unit_dir).stem)
# print(Meta.check == meta.check_valid)
# os.path.join(load_dir, '*.param_group.json')
# print(list(Path(unit_dir).glob('*.param_group.json')))
# unit.check()
# print(unit.src_path)
# print(dir(Meta))
# meta = Meta('test.py', 'y')
# print(meta.to_dict())
# p = Path('D:/documents/AcademicDocuments/customed_python_pkgs/unit-run/unit_run/dfafa/tils.py')
# print(list(p.parents))
# print(p)
# p2 = Path('D:/documents/AcademicDocuments/customed_python_pkgs/unit-run/tests')
# print(p2.exists())
# print(p. == p2.resolve())
# x = [1,2,3]
# print(len(list(filter(lambda a: a>1, x))))