-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Preserving of optimisation results implemented #37
Changes from 4 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import json | ||
import os | ||
from dataclasses import dataclass | ||
from typing import Union, List, Optional | ||
|
||
from gefest.core.serialization.serializer import Serializer | ||
from gefest.core.structure.structure import Structure | ||
|
||
|
||
@dataclass | ||
class Result: | ||
name: str | ||
best_structure: Union[Structure, List[Structure]] | ||
fitness: float | ||
metadata: dict | ||
|
||
def save(self, json_file_path: os.PathLike = None) -> Optional[str]: | ||
if json_file_path is None: | ||
return json.dumps(self, indent=4, cls=Serializer) | ||
with open(json_file_path, mode='w') as json_fp: | ||
json.dump(self, json_fp, indent=4, cls=Serializer) | ||
|
||
@staticmethod | ||
def load(json_str_or_file_path: Union[str, os.PathLike] = None) -> 'Result': | ||
try: | ||
return json.loads(json_str_or_file_path, cls=Serializer) | ||
except json.JSONDecodeError as exc: | ||
with open(json_str_or_file_path, mode='r') as json_fp: | ||
return json.load(json_fp, cls=Serializer) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
from copy import deepcopy | ||
from inspect import signature | ||
from typing import Any, Dict, Type | ||
|
||
from gefest.core.serialization.serializer import Serializer, INSTANCE_OR_CALLABLE | ||
|
||
|
||
def any_to_json(obj: INSTANCE_OR_CALLABLE) -> Dict[str, Any]: | ||
return { | ||
**{k: v for k, v in vars(obj).items() if k != 'log'}, | ||
**Serializer.dump_path_to_obj(obj) | ||
} | ||
|
||
|
||
def any_from_json(cls: Type[INSTANCE_OR_CALLABLE], json_obj: Dict[str, Any]) -> INSTANCE_OR_CALLABLE: | ||
cls_parameters = signature(cls.__init__).parameters | ||
if 'kwargs' not in cls_parameters: | ||
init_data = { | ||
k: v | ||
for k, v in json_obj.items() | ||
if k in cls_parameters | ||
} | ||
obj = cls(**init_data) | ||
vars(obj).update({ | ||
k: json_obj[k] | ||
for k in json_obj.keys() ^ init_data.keys() | ||
}) | ||
else: | ||
init_data = deepcopy(json_obj) | ||
obj = cls(**init_data) | ||
vars(obj).update(json_obj) | ||
return obj |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
from importlib import import_module | ||
from inspect import isclass, isfunction, ismethod, signature | ||
from json import JSONDecoder, JSONEncoder | ||
from typing import Any, Callable, Dict, Type, TypeVar, Union | ||
|
||
MODULE_X_NAME_DELIMITER = '/' | ||
INSTANCE_OR_CALLABLE = TypeVar('INSTANCE_OR_CALLABLE', object, Callable) | ||
CLASS_PATH_KEY = '_class_path' | ||
|
||
|
||
class Serializer(JSONEncoder, JSONDecoder): | ||
_to_json = 'to_json' | ||
_from_json = 'from_json' | ||
|
||
CODERS_BY_TYPE = {} | ||
|
||
def __init__(self, *args, **kwargs): | ||
for base_class, coder_name in [(JSONEncoder, 'default'), (JSONDecoder, 'object_hook')]: | ||
base_kwargs = {k: kwargs[k] for k in kwargs.keys() & signature(base_class.__init__).parameters} | ||
base_kwargs[coder_name] = getattr(self, coder_name) | ||
base_class.__init__(self, **base_kwargs) | ||
|
||
if not Serializer.CODERS_BY_TYPE: | ||
from gefest.core.opt.result import Result | ||
from gefest.core.structure.structure import Structure | ||
from gefest.core.structure.point import Point | ||
from gefest.core.structure.polygon import Polygon | ||
|
||
from .any import ( | ||
any_from_json, | ||
any_to_json, | ||
) | ||
|
||
_to_json = Serializer._to_json | ||
_from_json = Serializer._from_json | ||
basic_serialization = {_to_json: any_to_json, _from_json: any_from_json} | ||
Serializer.CODERS_BY_TYPE = { | ||
Result: basic_serialization, | ||
Structure: basic_serialization, | ||
Polygon: basic_serialization, | ||
Point: basic_serialization | ||
|
||
} | ||
|
||
@staticmethod | ||
def _get_field_checker(obj: Union[INSTANCE_OR_CALLABLE, Type[INSTANCE_OR_CALLABLE]]) -> Callable[..., bool]: | ||
if isclass(obj): | ||
return issubclass | ||
return isinstance | ||
|
||
@staticmethod | ||
def _get_base_type(obj: Union[INSTANCE_OR_CALLABLE, Type[INSTANCE_OR_CALLABLE]]) -> int: | ||
contains = Serializer._get_field_checker(obj) | ||
for k_type in Serializer.CODERS_BY_TYPE: | ||
if contains(obj, k_type): | ||
return k_type | ||
return None | ||
|
||
@staticmethod | ||
def _get_coder_by_type(coder_type: Type, coder_aim: str): | ||
return Serializer.CODERS_BY_TYPE[coder_type][coder_aim] | ||
|
||
@staticmethod | ||
def dump_path_to_obj(obj: INSTANCE_OR_CALLABLE) -> Dict[str, str]: | ||
"""Dumps the full path (module + name) to the input object into the dict | ||
|
||
Args: | ||
obj: object which path should be resolved (class, function or method) | ||
|
||
Returns: | ||
dictionary with path to the object | ||
""" | ||
|
||
if isclass(obj) or isfunction(obj) or ismethod(obj): | ||
obj_name = obj.__qualname__ | ||
else: | ||
obj_name = obj.__class__.__qualname__ | ||
|
||
if getattr(obj, '__module__', None) is not None: | ||
obj_module = obj.__module__ | ||
else: | ||
obj_module = obj.__class__.__module__ | ||
return { | ||
CLASS_PATH_KEY: f'{obj_module}{MODULE_X_NAME_DELIMITER}{obj_name}' | ||
} | ||
|
||
def default(self, obj: INSTANCE_OR_CALLABLE) -> Dict[str, Any]: | ||
"""Tries to encode objects that are not simply json-encodable to JSON-object | ||
|
||
Args: | ||
obj: object to be encoded (class, function or method) | ||
|
||
Returns: | ||
json object | ||
""" | ||
if isfunction(obj) or ismethod(obj): | ||
return Serializer.dump_path_to_obj(obj) | ||
base_type = Serializer._get_base_type(obj) | ||
if base_type is not None: | ||
return Serializer._get_coder_by_type(base_type, Serializer._to_json)(obj) | ||
|
||
return JSONEncoder.default(self, obj) | ||
|
||
@staticmethod | ||
def _get_class(class_path: str) -> Type[INSTANCE_OR_CALLABLE]: | ||
"""Gets the object type from the class_path | ||
|
||
Args: | ||
class_path: full path (module + name) of the class | ||
|
||
Returns: | ||
class, function or method type | ||
""" | ||
|
||
module_name, class_name = class_path.split(MODULE_X_NAME_DELIMITER) | ||
obj_cls = import_module(module_name) | ||
for sub in class_name.split('.'): | ||
obj_cls = getattr(obj_cls, sub) | ||
return obj_cls | ||
|
||
def object_hook(self, json_obj: Dict[str, Any]) -> Union[INSTANCE_OR_CALLABLE, dict]: | ||
"""Decodes every JSON-object to python class/func object or just returns dict | ||
|
||
Args: | ||
json_obj: dict[str, Any] to be decoded into Python class, function or | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. типы объектов принималось обозначать через There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. при этом сами объекты, например Point, Domain и прочее, вот так -> :obj: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Пока просто убрал дублирующую type hint-ы типизацию. |
||
method object only if it has some special fields | ||
|
||
Returns: | ||
Python class, function or method object OR input if it's just a regular dict | ||
""" | ||
|
||
if CLASS_PATH_KEY in json_obj: | ||
obj_cls = Serializer._get_class(json_obj[CLASS_PATH_KEY]) | ||
del json_obj[CLASS_PATH_KEY] | ||
base_type = Serializer._get_base_type(obj_cls) | ||
if isclass(obj_cls) and base_type is not None: | ||
return Serializer._get_coder_by_type(base_type, Serializer._from_json)(obj_cls, json_obj) | ||
elif isfunction(obj_cls) or ismethod(obj_cls): | ||
return obj_cls | ||
raise TypeError(f'Parsed obj_cls={obj_cls} is not serializable, but should be') | ||
return json_obj |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
этот метод скрытый, зачем его документировать докстрингами?
если им можно пользоваться, то почему он не может быть обычным?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Документировать не обязательно, но лишним не будет. В таком виде взял из FEDOT-а.