-
Notifications
You must be signed in to change notification settings - Fork 479
/
Copy pathmaps.py
109 lines (87 loc) · 3.61 KB
/
maps.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
from inspect import signature
from .logger import logger
from .markdown import markdown_to_notion, notion_to_markdown
class mapper(property):
def __init__(self, path, python_to_api, api_to_python, *args, **kwargs):
self.python_to_api = python_to_api
self.api_to_python = api_to_python
self.path = (
".".join(map(str, path))
if isinstance(path, list) or isinstance(path, tuple)
else path
)
super().__init__(*args, **kwargs)
def field_map(path, python_to_api=lambda x: x, api_to_python=lambda x: x):
"""
Returns a property that maps a Block attribute onto a field in the API data structures.
- `path` can either be a top-level field-name, a list that specifies the key names to traverse,
or a dot-delimited string representing the same traversal.
- `python_to_api` is a function that converts values as given in the Python layer into the
internal representation to be sent along in the API request.
- `api_to_python` is a function that converts what is received from the API into an internal
representation to be returned to the Python layer.
"""
if isinstance(path, str):
path = path.split(".")
def fget(self):
kwargs = {}
if (
"client" in signature(api_to_python).parameters
and "id" in signature(api_to_python).parameters
):
kwargs["client"] = self._client
kwargs["id"] = self.id
return api_to_python(self.get(path), **kwargs)
def fset(self, value):
kwargs = {}
if "client" in signature(python_to_api).parameters:
kwargs["client"] = self._client
self.set(path, python_to_api(value, **kwargs))
return mapper(
fget=fget,
fset=fset,
path=path,
python_to_api=python_to_api,
api_to_python=api_to_python,
)
def property_map(
name, python_to_api=lambda x: x, api_to_python=lambda x: x, markdown=True
):
"""
Similar to `field_map`, except it works specifically with the data under the "properties" field
in the API's block table, and just takes a single name to specify which subkey to reference.
Also, these properties all seem to use a special "embedded list" format that breaks the text
up into a sequence of chunks and associated format metadata. If `markdown` is True, we convert
this representation into commonmark-compatible markdown, and back again when saving.
"""
def py2api(x, client=None):
kwargs = {}
if "client" in signature(python_to_api).parameters:
kwargs["client"] = client
x = python_to_api(x, **kwargs)
if markdown:
x = markdown_to_notion(x)
return x
def api2py(x, client=None, id=""):
x = x or [[""]]
if markdown:
x = notion_to_markdown(x)
kwargs = {}
params = signature(api_to_python).parameters
if "client" in params:
kwargs["client"] = client
if "id" in params:
kwargs["id"] = id
return api_to_python(x, **kwargs)
return field_map(["properties", name], python_to_api=py2api, api_to_python=api2py)
def joint_map(*mappings):
"""
Combine multiple `field_map` and `property_map` instances together to map an attribute to multiple API fields.
Note: when "getting", the first one will be used. When "setting", they will all be set in parallel.
"""
def fget(self):
return mappings[0].fget(self)
def fset(self, value):
for m in mappings:
m.fset(self, value)
return property(fget=fget, fset=fset)