13 from mgr_module
import CLICommand
17 T
= TypeVar("T", bound
="Parent")
21 def __init__(self
, name
, val
=None):
26 def to_simplified(self
) -> Dict
[str, Any
]:
28 "version": self
.version
,
34 class JSONer(Simpler
):
35 def to_json(self
) -> Dict
[str, Any
]:
36 d
= self
.to_simplified()
41 def from_json(cls
: Type
[T
], data
) -> T
:
42 o
= cls(data
.get("name", ""), data
.get("value"))
43 o
.version
= data
.get("version", 1) + 1
47 class YAMLer(Simpler
):
48 def to_yaml(self
) -> Dict
[str, Any
]:
49 d
= self
.to_simplified()
54 @pytest.mark
.parametrize(
55 "obj, compatible, json_val",
58 ({"name": "foobar"}, False, '{"name": "foobar"}'),
59 ([1, 2, 3], False, "[1, 2, 3]"),
60 (JSONer("bob"), False, '{"name": "bob", "value": {}, "version": 1}'),
64 '{"name": "betty", "value": 77, "version": 1}',
67 ({"name": "foobar"}, True, '{"name": "foobar"}'),
71 '{"_json": true, "name": "bob", "value": {}, "version": 1}',
75 def test_format_json(obj
: Any
, compatible
: bool, json_val
: str):
77 object_format
.ObjectFormatAdapter(
78 obj
, compatible
=compatible
, json_indent
=None
84 @pytest.mark
.parametrize(
85 "obj, compatible, yaml_val",
88 ({"name": "foobar"}, False, "name: foobar\n"),
90 {"stuff": [1, 88, 909, 32]},
92 "stuff:\n- 1\n- 88\n- 909\n- 32\n",
95 JSONer("zebulon", "999"),
97 "name: zebulon\nvalue: '999'\nversion: 1\n",
100 ({"name": "foobar"}, True, "name: foobar\n"),
102 YAMLer("thingy", "404"),
104 "_yaml: true\nname: thingy\nvalue: '404'\nversion: 1\n",
108 def test_format_yaml(obj
: Any
, compatible
: bool, yaml_val
: str):
110 object_format
.ObjectFormatAdapter(
111 obj
, compatible
=compatible
118 def __init__(self
, v
) -> None:
121 def mgr_return_value(self
) -> int:
125 @pytest.mark
.parametrize(
129 ({"fish": "sticks"}, 0),
135 def test_return_value(obj
: Any
, ret
: int):
136 rva
= object_format
.ReturnValueAdapter(obj
)
137 # a ReturnValueAdapter instance meets the ReturnValueProvider protocol.
138 assert object_format
._is
_return
_value
_provider
(rva
)
139 assert rva
.mgr_return_value() == ret
142 def test_valid_formats():
143 ofa
= object_format
.ObjectFormatAdapter({"fred": "wilma"})
144 vf
= ofa
.valid_formats()
151 def test_error_response_exceptions():
152 err
= object_format
.ErrorResponseBase()
153 with pytest
.raises(NotImplementedError):
154 err
.format_response()
156 err
= object_format
.UnsupportedFormat("cheese")
157 assert err
.format_response() == (-22, "", "Unsupported format: cheese")
159 err
= object_format
.UnknownFormat("chocolate")
160 assert err
.format_response() == (-22, "", "Unknown format name: chocolate")
163 @pytest.mark
.parametrize(
164 "value, format, result",
166 ({}, None, (0, "{}", "")),
167 ({"blat": True}, "json", (0, '{\n "blat": true\n}', "")),
168 ({"blat": True}, "yaml", (0, "blat: true\n", "")),
169 ({"blat": True}, "toml", (-22, "", "Unknown format name: toml")),
170 ({"blat": True}, "xml", (-22, "", "Unsupported format: xml")),
172 JSONer("hoop", "303"),
174 (0, "name: hoop\nvalue: '303'\nversion: 1\n", ""),
178 def test_responder_decorator_default(
179 value
: Any
, format
: Optional
[str], result
: Tuple
[int, str, str]
181 @object_format.Responder()
182 def orf_value(format
: Optional
[str] = None):
185 assert orf_value(format
=format
) == result
188 class PhonyMultiYAMLFormatAdapter(object_format
.ObjectFormatAdapter
):
189 """This adapter puts a yaml document/directive separator line
190 before all output. It doesn't actully support multiple documents.
192 def format_yaml(self
):
193 yml
= super().format_yaml()
194 return "---\n{}".format(yml
)
197 @pytest.mark
.parametrize(
198 "value, format, result",
200 ({}, None, (0, "{}", "")),
201 ({"blat": True}, "json", (0, '{\n "blat": true\n}', "")),
202 ({"blat": True}, "yaml", (0, "---\nblat: true\n", "")),
203 ({"blat": True}, "toml", (-22, "", "Unknown format name: toml")),
204 ({"blat": True}, "xml", (-22, "", "Unsupported format: xml")),
206 JSONer("hoop", "303"),
208 (0, "---\nname: hoop\nvalue: '303'\nversion: 1\n", ""),
212 def test_responder_decorator_custom(
213 value
: Any
, format
: Optional
[str], result
: Tuple
[int, str, str]
215 @object_format.Responder(PhonyMultiYAMLFormatAdapter
)
216 def orf_value(format
: Optional
[str] = None):
219 assert orf_value(format
=format
) == result
222 class FancyDemoAdapter(PhonyMultiYAMLFormatAdapter
):
223 """This adapter demonstrates adding formatting for other formats
224 like xml and plain text.
226 def format_xml(self
) -> str:
227 name
= self
.obj
.get("name")
228 size
= self
.obj
.get("size")
229 return f
'<object name="{name}" size="{size}" />'
231 def format_plain(self
) -> str:
232 name
= self
.obj
.get("name")
233 size
= self
.obj
.get("size")
234 es
= 'es' if size
!= 1 else ''
235 return f
"{size} box{es} of {name}"
239 """Class to stand in for a mgr module, used to test CLICommand integration."""
241 @CLICommand("alpha one", perm
="rw")
242 @object_format.Responder()
243 def alpha_one(self
, name
: str = "default") -> Dict
[str, str]:
250 @CLICommand("beta two", perm
="r")
251 @object_format.Responder()
253 self
, name
: str = "default", format
: Optional
[str] = None
261 @CLICommand("gamma three", perm
="rw")
262 @object_format.Responder(FancyDemoAdapter
)
263 def gamma_three(self
, size
: int = 0) -> Dict
[str, Any
]:
264 return {"name": "funnystuff", "size": size
}
267 @pytest.mark
.parametrize(
268 "prefix, args, response",
272 {"name": "moonbase"},
275 '{\n "alpha": "one",\n "name": "moonbase",\n "weight": 300\n}',
282 {"name": "moonbase2", "format": "yaml"},
285 "alpha: one\nname: moonbase2\nweight: 300\n",
292 {"name": "moonbase2", "format": "chocolate"},
296 "Unknown format name: chocolate",
305 '{\n "beta": "two",\n "name": "blocker",\n "weight": 72\n}',
312 {"name": "test", "format": "yaml"},
315 "beta: two\nname: test\nweight: 72\n",
322 {"name": "test", "format": "plain"},
326 "Unsupported format: plain",
335 '{\n "name": "funnystuff",\n "size": 0\n}',
342 {"size": 1, "format": "json"},
345 '{\n "name": "funnystuff",\n "size": 1\n}',
352 {"size": 1, "format": "plain"},
355 "1 box of funnystuff",
362 {"size": 2, "format": "plain"},
365 "2 boxes of funnystuff",
372 {"size": 2, "format": "xml"},
375 '<object name="funnystuff" size="2" />',
382 {"size": 2, "format": "toml"},
386 "Unknown format name: toml",
391 def test_cli_command_responder(prefix
, args
, response
):
393 assert CLICommand
.COMMANDS
[prefix
].call(dd
, args
, None) == response
396 def test_error_response():
397 e1
= object_format
.ErrorResponse("nope")
398 assert e1
.format_response() == (-22, "", "nope")
399 assert e1
.return_value
== -22
400 assert e1
.errno
== 22
401 assert "ErrorResponse" in repr(e1
)
402 assert "nope" in repr(e1
)
403 assert e1
.mgr_return_value() == -22
406 open("/this/is_/extremely_/unlikely/_to/exist.txt")
407 except Exception as e
:
408 e2
= object_format
.ErrorResponse
.wrap(e
)
409 r
= e2
.format_response()
410 assert r
[0] == -errno
.ENOENT
412 assert "No such file or directory" in r
[2]
413 assert "ErrorResponse" in repr(e2
)
414 assert "No such file or directory" in repr(e2
)
415 assert r
[0] == e2
.mgr_return_value()
417 e3
= object_format
.ErrorResponse
.wrap(RuntimeError("blat"))
418 r
= e3
.format_response()
419 assert r
[0] == -errno
.EINVAL
421 assert "blat" in r
[2]
422 assert r
[0] == e3
.mgr_return_value()