Skip to content

Commit e9a9e2e

Browse files
committed
* Filled yaml_info docstring.
* fixed class decoding so that it is robust to errors happening with faulty classes. This fixes #2 * improved error message when decoding failed.
1 parent acbe2e6 commit e9a9e2e

2 files changed

Lines changed: 88 additions & 10 deletions

File tree

yamlable/main.py

Lines changed: 70 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,37 @@ def is_yaml_tag_supported(cls, yaml_tag: str) -> bool:
8686
def yaml_info(yaml_tag: str = None, yaml_tag_ns: str = None) \
8787
-> Callable[['Type[YA]', Optional[str], Optional[str]], 'Type[YA]']:
8888
"""
89-
A simple class decorator to tag a class with a global yaml tag - that way no need to call YamlAble super constructor
89+
A simple class decorator to tag a class with a global yaml tag - that way you do not have to call `YamlAble` super
90+
constructor.
9091
91-
:param yaml_tag:
92-
:param yaml_tag_ns:
92+
You can either provide a full yaml tag suffix:
93+
94+
```python
95+
@yaml_info("com.example.MyFoo")
96+
class Foo(YamlAble):
97+
pass
98+
99+
print(Foo.__yaml_tag_suffix__) # yields "com.example.MyFoo"
100+
```
101+
102+
or simply provide a namespace, that will be appended with '.<class name>' :
103+
104+
```python
105+
@yaml_info(yaml_tag_ns="com.example")
106+
class Foo(YamlAble):
107+
pass
108+
109+
print(MyFoo.__yaml_tag_suffix__) # yields "com.example.Foo"
110+
```
111+
112+
In both cases, the suffix is appended at the end of the common yamlable prefix:
113+
114+
```python
115+
print(Foo().dumps_yaml()) # yields "!yamlable/com.example.Foo {}"
116+
```
117+
118+
:param yaml_tag: the complete yaml suffix.
119+
:param yaml_tag_ns: the yaml namespace. It will be appended with '.<cls.__name__>'
93120
:return:
94121
"""
95122
def f(cls):
@@ -99,11 +126,38 @@ def f(cls):
99126

100127
def yaml_info_decorate(cls: 'Type[YA]', yaml_tag: str = None, yaml_tag_ns: str = None) -> 'Type[YA]':
101128
"""
102-
A simple class decorator to tag a class with yaml tag - that way no need to call YamlAble super constructor
129+
A simple class decorator to tag a class with a global yaml tag - that way you do not have to call `YamlAble` super
130+
constructor.
131+
132+
You can either provide a full yaml tag suffix:
133+
134+
```python
135+
@yaml_info("com.example.MyFoo")
136+
class Foo(YamlAble):
137+
pass
138+
139+
print(Foo.__yaml_tag_suffix__) # yields "com.example.MyFoo"
140+
```
141+
142+
or simply provide a namespace, that will be appended with '.<class name>' :
143+
144+
```python
145+
@yaml_info(yaml_tag_ns="com.example")
146+
class Foo(YamlAble):
147+
pass
148+
149+
print(MyFoo.__yaml_tag_suffix__) # yields "com.example.Foo"
150+
```
151+
152+
In both cases, the suffix is appended at the end of the common yamlable prefix:
153+
154+
```python
155+
print(Foo().dumps_yaml()) # yields "!yamlable/com.example.Foo {}"
156+
```
103157
104158
:param cls:
105-
:param yaml_tag:
106-
:param yaml_tag_ns:
159+
:param yaml_tag: the complete yaml suffix.
160+
:param yaml_tag_ns: the yaml namespace. It will be appended with '.<cls.__name__>'
107161
:return:
108162
"""
109163
if yaml_tag_ns is not None:
@@ -141,13 +195,19 @@ def decode_yamlable(loader, yaml_tag, node, **kwargs):
141195
:return:
142196
"""
143197
candidates = _get_all_subclasses(YamlAble)
198+
errors = dict()
144199
for clazz in candidates:
145-
if clazz.is_yaml_tag_supported(yaml_tag):
146-
constructor_args = read_yaml_node_as_dict(loader, node)
147-
return clazz.from_yaml_dict(constructor_args, yaml_tag=yaml_tag)
200+
try:
201+
if clazz.is_yaml_tag_supported(yaml_tag):
202+
constructor_args = read_yaml_node_as_dict(loader, node)
203+
return clazz.from_yaml_dict(constructor_args, yaml_tag=yaml_tag)
204+
except Exception as e:
205+
errors[clazz.__name__] = e
148206

149207
raise TypeError("No YamlAble subclass found able to decode object !yamlable/" + yaml_tag + ". Tried classes: "
150-
+ str(candidates))
208+
+ str(candidates) + ". Caught errors: " + str(errors) + ". "
209+
"Please check the value of <cls>.__yaml_tag_suffix__ on these classes. Note that this value may be "
210+
"set using @yaml_info() so help(yaml_info) might help too.")
151211

152212

153213
def encode_yamlable(dumper, obj, without_custom_tag: bool = False, **kwargs):

yamlable/tests/test_yamlable.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,3 +112,21 @@ def __init__(self, a, b):
112112
assert dump(f) == s
113113

114114
assert dump(load(dump(load(s)))) == s
115+
116+
117+
def test_help_yaml_info():
118+
119+
@yaml_info("com.example.MyFoo")
120+
class Foo(YamlAble):
121+
pass
122+
123+
assert Foo.__yaml_tag_suffix__ == "com.example.MyFoo"
124+
125+
@yaml_info(yaml_tag_ns="com.example")
126+
class Foo(YamlAble):
127+
pass
128+
129+
assert Foo.__yaml_tag_suffix__ == "com.example.Foo"
130+
131+
assert Foo().dumps_yaml() == """!yamlable/com.example.Foo {}
132+
"""

0 commit comments

Comments
 (0)