Add _repr_pretty_ for IPython

This commit is contained in:
Lucidiot 2019-09-30 07:17:06 +02:00
parent cdf39c3464
commit e5dcb746ad
Signed by: lucidiot
GPG Key ID: 3358C1CA6906FB8D
4 changed files with 88 additions and 33 deletions

View File

@ -37,6 +37,15 @@ class Namespace(MutableMapping):
raise KeyError(repr(name)) raise KeyError(repr(name))
return namespacify(attr) return namespacify(attr)
def __setitem__(self, name: Any, value: Any) -> None:
setattr(self, name, value)
def __delitem__(self, name: Any) -> None:
try:
delattr(self, name)
except AttributeError:
raise KeyError(repr(name))
def __iter__(self) -> Iterator: def __iter__(self) -> Iterator:
return iter(self.__dict__) return iter(self.__dict__)
@ -49,14 +58,14 @@ class Namespace(MutableMapping):
def __str__(self) -> str: def __str__(self) -> str:
return str(self.__dict__) return str(self.__dict__)
def _repr_pretty_(self, p: Any, cycle: bool) -> None:
# IPython's pretty printing
if cycle:
p.text('{}(...)'.format(self.__class__.__name__))
else:
p.text('{}('.format(self.__class__.__name__))
p.pretty(self.__dict__)
p.text(')')
def copy(self) -> 'Namespace': def copy(self) -> 'Namespace':
return self.__class__(self.__dict__) return self.__class__(self.__dict__)
def __setitem__(self, name: Any, value: Any) -> None:
setattr(self, name, value)
def __delitem__(self, name: Any) -> None:
try:
delattr(self, name)
except AttributeError:
raise KeyError(repr(name))

View File

@ -34,6 +34,15 @@ class ClassRegistry(Dict[str, Callable]):
self.check(key, value) self.check(key, value)
super().__setitem__(key, value) super().__setitem__(key, value)
def _repr_pretty_(self, p: Any, cycle: bool) -> None:
# IPython's pretty printing
if cycle:
p.text('{}(...)'.format(self.__class__.__name__))
else:
p.text('{}('.format(self.__class__.__name__))
p.pretty(dict(self))
p.text(')')
@property @property
def metaclass(self) -> type: def metaclass(self) -> type:
class RegistryMetaclass(type): class RegistryMetaclass(type):

View File

@ -1,6 +1,7 @@
from types import MappingProxyType from types import MappingProxyType
from typing import List, Set, Iterable, Iterator, Any from typing import List, Set, Iterable, Iterator, Any
from unittest import TestCase from unittest import TestCase
from unittest.mock import MagicMock, call
from objtools.collections import namespacify, Namespace from objtools.collections import namespacify, Namespace
@ -69,29 +70,6 @@ class TestNamespace(TestCase):
with self.assertRaises(AttributeError): with self.assertRaises(AttributeError):
ns.b ns.b
def test_iter(self) -> None:
ns: Namespace = Namespace(a=1, b=2)
iterator: Iterator = iter(ns)
self.assertEqual(next(iterator), 'a')
self.assertEqual(next(iterator), 'b')
def test_len(self) -> None:
self.assertEqual(len(Namespace(a=1, b=2)), 2)
self.assertEqual(len(Namespace()), 0)
def test_repr(self) -> None:
self.assertEqual(repr(Namespace()), 'Namespace({})')
self.assertEqual(repr(Namespace(a=1)), "Namespace({'a': 1})")
def test_str(self) -> None:
self.assertEqual(str(Namespace()), '{}')
self.assertEqual(str(Namespace(a=1)), "{'a': 1}")
def test_copy(self) -> None:
ns: Namespace = Namespace(a=1, b=2)
self.assertEqual(ns.copy(), ns)
self.assertIsNot(ns.copy(), ns)
def test_set(self) -> None: def test_set(self) -> None:
ns: Any = Namespace(a=4) ns: Any = Namespace(a=4)
ns.a = 1 ns.a = 1
@ -111,3 +89,44 @@ class TestNamespace(TestCase):
del ns.c del ns.c
with self.assertRaises(KeyError): with self.assertRaises(KeyError):
del ns['c'] del ns['c']
def test_iter(self) -> None:
ns: Namespace = Namespace(a=1, b=2)
iterator: Iterator = iter(ns)
self.assertEqual(next(iterator), 'a')
self.assertEqual(next(iterator), 'b')
def test_len(self) -> None:
self.assertEqual(len(Namespace(a=1, b=2)), 2)
self.assertEqual(len(Namespace()), 0)
def test_repr(self) -> None:
self.assertEqual(repr(Namespace()), 'Namespace({})')
self.assertEqual(repr(Namespace(a=1)), "Namespace({'a': 1})")
def test_str(self) -> None:
self.assertEqual(str(Namespace()), '{}')
self.assertEqual(str(Namespace(a=1)), "{'a': 1}")
def test_repr_pretty(self) -> None:
p = MagicMock(spec_set=['text', 'pretty'])
Namespace(a=1)._repr_pretty_(p, cycle=False)
self.assertEqual(p.text.call_count, 2)
self.assertEqual(p.pretty.call_count, 1)
self.assertListEqual(p.text.call_args_list, [
call('Namespace('),
call(')'),
])
self.assertEqual(p.pretty.call_args, call({'a': 1}))
def test_repr_pretty_cycle(self) -> None:
p = MagicMock(spec_set=['text', 'pretty'])
Namespace(a=1)._repr_pretty_(p, cycle=True)
self.assertEqual(p.text.call_count, 1)
self.assertFalse(p.pretty.called)
self.assertEqual(p.text.call_args, call('Namespace(...)'))
def test_copy(self) -> None:
ns: Namespace = Namespace(a=1, b=2)
self.assertEqual(ns.copy(), ns)
self.assertIsNot(ns.copy(), ns)

View File

@ -1,5 +1,5 @@
from unittest import TestCase from unittest import TestCase
from unittest.mock import patch from unittest.mock import patch, call, MagicMock
from objtools.registry import ClassRegistry from objtools.registry import ClassRegistry
@ -61,3 +61,21 @@ class TestRegistry(TestCase):
pass pass
self.assertDictEqual(registry, {'foo': Thing}) self.assertDictEqual(registry, {'foo': Thing})
def test_repr_pretty(self) -> None:
p = MagicMock(spec_set=['text', 'pretty'])
ClassRegistry(a=object)._repr_pretty_(p, cycle=False)
self.assertEqual(p.text.call_count, 2)
self.assertEqual(p.pretty.call_count, 1)
self.assertListEqual(p.text.call_args_list, [
call('ClassRegistry('),
call(')'),
])
self.assertEqual(p.pretty.call_args, call({'a': object}))
def test_repr_pretty_cycle(self) -> None:
p = MagicMock(spec_set=['text', 'pretty'])
ClassRegistry(a=object)._repr_pretty_(p, cycle=True)
self.assertEqual(p.text.call_count, 1)
self.assertFalse(p.pretty.called)
self.assertEqual(p.text.call_args, call('ClassRegistry(...)'))