-
-
Save PhotonQuantum/476ec1be137e4a5a59632ba81650e454 to your computer and use it in GitHub Desktop.
import json | |
class Model: | |
pass | |
class DummyArtist(Model): | |
name: str | |
uri: str | |
def __init__(self, name): | |
self.name = name | |
self.uri = f"fuo://artist/{name}" | |
self.songs = [] | |
if name == "fripSide": | |
self.songs = [ | |
DummySong("Late in autumn", "fripSide_"), | |
DummySong("Only my railgun", "fripSide_") | |
] | |
def __repr__(self): | |
return f"<Artist {self.name}>" | |
class DummySong(Model): | |
title: str | |
artists: list | |
def __init__(self, title, artist): | |
self.title = title | |
self.artists = [DummyArtist(artist)] | |
self.uri = f"fuo://song/{title}" | |
def __repr__(self): | |
return f"<Song {self.title}>" | |
class DummyUser(Model): | |
name: str | |
def __init__(self, name): | |
self.name = name | |
self.uri = f"fuo://user/{name}" | |
def __repr__(self): | |
return f"<User {self.name}>" | |
@property | |
def playlist(self): | |
return [ | |
DummySong("Let it go", "blabla"), | |
DummySong("Only my railgun", "fripSide") | |
] | |
class Response: | |
def __init__(self, model, **options): | |
self.model = model | |
self.options = options | |
def dict_walker(indict, path=None): | |
path = path[:] if path else [] | |
if isinstance(indict, dict): | |
for key, value in indict.items(): | |
if isinstance(value, dict): | |
for d in dict_walker(value, path + [key]): | |
yield d | |
elif isinstance(value, list) or isinstance(value, tuple): | |
for i, v in enumerate(value): | |
for d in dict_walker(v, path + [key] + [i]): | |
yield d | |
else: | |
yield path + [key], value | |
else: | |
yield path, indict | |
def set_item_by_path(indict, path, value): | |
for key in path[:-1]: | |
indict = indict.setdefault(key, {}) | |
indict[path[-1]] = value | |
def render(model, serializer, **options): | |
if isinstance(model, DummyUser): | |
return serializer.render_user(model, **options) | |
if isinstance(model, DummySong): | |
return serializer.render_song(model, **options) | |
if isinstance(model, DummyArtist): | |
return serializer.render_artist(model, **options) | |
def serialize(obj, serializer): | |
is_complete = False | |
while not is_complete: | |
is_complete = True | |
for elem in dict_walker(obj): | |
path, value = elem | |
if isinstance(value, Response): | |
model = value.model | |
options = value.options | |
rendered = render(model, serializer, **options) | |
set_item_by_path(obj, path, rendered) | |
is_complete = False | |
break | |
if isinstance(value, Model): | |
model = value | |
rendered = render(model, serializer) | |
set_item_by_path(obj, path, rendered) | |
is_complete = False | |
break | |
return obj | |
class PlainSerializer: | |
@staticmethod | |
def render_artist(model, brief=True): | |
if brief: | |
return f"{model.uri} \t# {model.name}" | |
else: | |
return { | |
"provider": "blabla", | |
"identifier": "blablabla", | |
"name": model.name, | |
"songs": model.songs | |
} | |
@staticmethod | |
def render_song(model, brief=True): | |
if brief: | |
# artists = ",".join(...) | |
return f"{model.uri}\t# {model.title} - {model.artists[0].name}" | |
else: | |
return { | |
"provider": "blabla", | |
"identifier": "blablabla", | |
"title": model.title, | |
"artists": model.artists | |
# ... | |
} | |
@staticmethod | |
def render_user(model, brief=True): | |
if brief: | |
return f"{model.uri}\t# {model.name}" | |
else: | |
return { | |
"name": model.name, | |
"playlist": model.playlist | |
} | |
def __init__(self, data): | |
self._data = {"root": data} | |
def serialize(self): | |
return serialize(self._data, PlainSerializer)["root"] | |
class JsonSerializer: | |
_data: dict | |
@staticmethod | |
def render_artist(model, brief=True): | |
if brief: | |
return model.uri | |
else: | |
return {"name": model.name, "uri": model.uri, "songs": model.songs} | |
@staticmethod | |
def render_song(model, brief=True): | |
if brief: | |
return model.uri | |
else: | |
return {"name": model.title, "artists": model.artists, "uri": model.uri} | |
@staticmethod | |
def render_user(model, brief=True): | |
if brief: | |
return model.uri | |
else: | |
return {"name": model.name, "playlist": model.playlist, "uri": model.uri} | |
def __init__(self, data): | |
self._data = {"root": data} | |
def serialize(self): | |
return serialize(self._data, JsonSerializer)["root"] | |
class JsonEmitter: | |
def __init__(self, data): | |
self._data = data | |
def emit(self): | |
return json.dumps(self._data, indent=4) | |
class PlainEmitter: | |
def __init__(self, data): | |
self._data = data | |
def _emit(self): | |
for k, v in self._data.items(): | |
if isinstance(v, (str, int)): | |
yield f"{k}:\t{v}" | |
elif isinstance(v, list): | |
yield f"{k}::" | |
for item in v: | |
if isinstance(item, (str, int)): | |
yield f"\t{item}" | |
else: | |
raise TypeError | |
else: | |
raise TypeError | |
def emit(self): | |
return "\n".join(self._emit()) | |
class Dumper: | |
_serializer = None | |
_emitter = None | |
def __init__(self, data): | |
if isinstance(data, Model): | |
self._data = Response(data, brief=False) | |
else: | |
self._data = data | |
def dump(self): | |
serialized = self._serializer(self._data).serialize() | |
return self._emitter(serialized).emit() | |
class JsonDumper(Dumper): | |
_serializer = JsonSerializer | |
_emitter = JsonEmitter | |
class PlainDumper(Dumper): | |
_serializer = PlainSerializer | |
_emitter = PlainEmitter | |
def main(): | |
print("===JSON===") | |
print("---User---") | |
test_dict = DummyUser("lq") | |
print(JsonDumper(test_dict).dump()) | |
print("---Song---") | |
test_dict = DummySong("Only my railgun", "fripSide") | |
print(JsonDumper(test_dict).dump()) | |
print("---Artist---") | |
test_dict = DummyArtist("fripSide") | |
print(JsonDumper(test_dict).dump()) | |
print("===PLAIN===") | |
print("---User---") | |
test_dict = DummyUser("lq") | |
print(PlainDumper(test_dict).dump()) | |
print("---Song---") | |
test_dict = DummySong("Only my railgun", "fripSide") | |
print(PlainDumper(test_dict).dump()) | |
print("---Artist---") | |
test_dict = DummyArtist("fripSide") | |
print(PlainDumper(test_dict).dump()) | |
if __name__ == "__main__": | |
main() |
但是如果默认序列化的时候全部都是 brief 形式的话,想要生成详细信息的时候该怎么办呢
ummm,感觉是不是不会有这种情况。
比如显示单首歌的时候,信息就很多(
emm 其实 brief 这个参数是从老代码那里继承来的
目前 serialize 函数在经过一系列判断,最终会把参数全部传给 show_xxx 函数,所以这些函数的参数看起来基本是会等价的。
但是如果默认序列化的时候全部都是 brief 形式的话,想要生成详细信息的时候该怎么办呢
ummm,感觉是不是不会有这种情况。
比如显示单首歌的时候,信息就很多(
emm 其实 brief 这个参数是从老代码那里继承来的
我理解,如果 show_artist 里面调用其它 show_xxx 时,show_xxx 都会传个 brief=True 进去。
类似的 show_song 里面,在 show_artist,show_artist 也是 brief 的。
我感觉就是第一级的 show_xxx 是 brief=False, 然后二级 show_xxx 都是 brief=True 的。
这边的想法是 handler 扔出来的就是一个 Response 或者 dict[Response] 之类的东西,它最好没有对 show_xxx 的任何调用,而是由 serializer 来负责处理。
比如对于 fuo://<provider>/users/<uid>
,最后 return Response(provider.User.get(uid), brief=False)
(啊 新消息我还没看到 我康一下 ummm
我理解,如果 show_artist 里面调用其它 show_xxx 时,show_xxx 都会传个 brief=True 进去。
按照现在的实现思路,我希望 show_xxx 里面不要有对别的 show_xxx 的调用,如果有引用的需要,就直接扔出来 Response,在下一次 dict_walk 的时候被进一步解析。这样就可以在有需求的时候对一些复杂的引用关系进行处理,也可以解除各个 show_xxx 之间的耦合。
emm,我仔细想了一下,其实可以这样处理:如果 handler 那边 return 了一个裸的 Model,那这个 Model 是默认 brief=False 的,而别的情况下都是 brief=True 这样实现起来觉得好像有点怪(
我理解,如果 show_artist 里面调用其它 show_xxx 时,show_xxx 都会传个 brief=True 进去。
按照现在的实现思路,我希望 show_xxx 里面不要有对别的 show_xxx 的调用,如果有引用的需要,就直接扔出来 Response,在下一次 dict_walk 的时候被进一步解析。这样就可以在有需求的时候对一些复杂的引用关系进行处理,也可以解除各个 show_xxx 之间的耦合。
emm,我仔细想了一下,其实可以这样处理:如果 handler 那边 return 了一个裸的 Model,那这个 Model 是默认 brief=False 的,而别的情况下都是 brief=True这样实现起来觉得好像有点怪(
感觉也不错。
刚新写了一个实现,保留了 Response,然后对 bare Model 做了默认处理
刚新写了一个实现,保留了 Response,然后对 bare Model 做了默认处理
感觉代码又更漂亮了!
ummm,感觉是不是不会有这种情况。