@@ -248,6 +248,49 @@ def _pprint_ordered_dict(self, object, stream, indent, allowance, context, level
248248
249249 _dispatch [_collections .OrderedDict .__repr__ ] = _pprint_ordered_dict
250250
251+ def _pprint_dict_view (self , object , stream , indent , allowance , context , level ):
252+ """Pretty print dict views (keys, values, items)."""
253+ if isinstance (object , self ._dict_items_view ):
254+ key = _safe_tuple
255+ else :
256+ key = _safe_key
257+ write = stream .write
258+ write (object .__class__ .__name__ + '([' )
259+ if self ._indent_per_level > 1 :
260+ write ((self ._indent_per_level - 1 ) * ' ' )
261+ length = len (object )
262+ if length :
263+ if self ._sort_dicts :
264+ entries = sorted (object , key = key )
265+ else :
266+ entries = object
267+ self ._format_items (entries , stream , indent , allowance + 1 ,
268+ context , level )
269+ write ('])' )
270+
271+ def _pprint_mapping_abc_view (self , object , stream , indent , allowance , context , level ):
272+ """Pretty print mapping views from collections.abc."""
273+ write = stream .write
274+ write (object .__class__ .__name__ + '(' )
275+ # Dispatch formatting to the view's _mapping
276+ self ._format (object ._mapping , stream , indent , allowance , context , level )
277+ write (')' )
278+
279+ _dict_keys_view = type ({}.keys ())
280+ _dispatch [_dict_keys_view .__repr__ ] = _pprint_dict_view
281+
282+ _dict_values_view = type ({}.values ())
283+ _dispatch [_dict_values_view .__repr__ ] = _pprint_dict_view
284+
285+ _dict_items_view = type ({}.items ())
286+ _dispatch [_dict_items_view .__repr__ ] = _pprint_dict_view
287+
288+ _dispatch [_collections .abc .MappingView .__repr__ ] = _pprint_mapping_abc_view
289+
290+ _view_reprs = {cls .__repr__ for cls in
291+ (_dict_keys_view , _dict_values_view , _dict_items_view ,
292+ _collections .abc .MappingView )}
293+
251294 def _pprint_list (self , object , stream , indent , allowance , context , level ):
252295 stream .write ('[' )
253296 self ._format_items (object , stream , indent , allowance + 1 ,
@@ -610,6 +653,42 @@ def _safe_repr(self, object, context, maxlevels, level):
610653 del context [objid ]
611654 return "{%s}" % ", " .join (components ), readable , recursive
612655
656+ if issubclass (typ , _collections .abc .MappingView ) and r in self ._view_reprs :
657+ objid = id (object )
658+ if maxlevels and level >= maxlevels :
659+ return "{...}" , False , objid in context
660+ if objid in context :
661+ return _recursion (object ), False , True
662+ key = _safe_key
663+ if issubclass (typ , (self ._dict_items_view , _collections .abc .ItemsView )):
664+ key = _safe_tuple
665+ if hasattr (object , "_mapping" ):
666+ # Dispatch formatting to the view's _mapping
667+ mapping_repr , readable , recursive = self .format (
668+ object ._mapping , context , maxlevels , level )
669+ return (typ .__name__ + '(%s)' % mapping_repr ), readable , recursive
670+ elif hasattr (typ , "_mapping" ):
671+ # We have a view that somehow has lost its type's _mapping, raise
672+ # an error by calling repr() instead of failing cryptically later
673+ return repr (object ), True , False
674+ if self ._sort_dicts :
675+ object = sorted (object , key = key )
676+ context [objid ] = 1
677+ readable = True
678+ recursive = False
679+ components = []
680+ append = components .append
681+ level += 1
682+ for val in object :
683+ vrepr , vreadable , vrecur = self .format (
684+ val , context , maxlevels , level )
685+ append (vrepr )
686+ readable = readable and vreadable
687+ if vrecur :
688+ recursive = True
689+ del context [objid ]
690+ return typ .__name__ + '([%s])' % ", " .join (components ), readable , recursive
691+
613692 if (issubclass (typ , list ) and r is list .__repr__ ) or \
614693 (issubclass (typ , tuple ) and r is tuple .__repr__ ):
615694 if issubclass (typ , list ):
0 commit comments