Format: text/x-python
⬇ Download File (14.1 KB)
   1  """Tests for the Formatters."""
   2  
   3  from math import pi
   4  
   5  try:
   6      import numpy
   7  except:
   8      numpy = None
   9  import pytest
  10  
  11  from IPython import get_ipython
  12  from traitlets.config import Config
  13  from IPython.core.formatters import (
  14      PlainTextFormatter, HTMLFormatter, PDFFormatter, _mod_name_key,
  15      DisplayFormatter, JSONFormatter,
  16  )
  17  from IPython.utils.io import capture_output
  18  
  19  class A(object):
  20      def __repr__(self):
  21          return 'A()'
  22  
  23  class B(A):
  24      def __repr__(self):
  25          return 'B()'
  26  
  27  class C:
  28      pass
  29  
  30  class BadRepr(object):
  31      def __repr__(self):
  32          raise ValueError("bad repr")
  33  
  34  class BadPretty(object):
  35      _repr_pretty_ = None
  36  
  37  class GoodPretty(object):
  38      def _repr_pretty_(self, pp, cycle):
  39          pp.text('foo')
  40  
  41      def __repr__(self):
  42          return 'GoodPretty()'
  43  
  44  def foo_printer(obj, pp, cycle):
  45      pp.text('foo')
  46  
  47  def test_pretty():
  48      f = PlainTextFormatter()
  49      f.for_type(A, foo_printer)
  50      assert f(A()) == "foo"
  51      assert f(B()) == "B()"
  52      assert f(GoodPretty()) == "foo"
  53      # Just don't raise an exception for the following:
  54      f(BadPretty())
  55  
  56      f.pprint = False
  57      assert f(A()) == "A()"
  58      assert f(B()) == "B()"
  59      assert f(GoodPretty()) == "GoodPretty()"
  60  
  61  
  62  def test_deferred():
  63      f = PlainTextFormatter()
  64  
  65  def test_precision():
  66      """test various values for float_precision."""
  67      f = PlainTextFormatter()
  68      assert f(pi) == repr(pi)
  69      f.float_precision = 0
  70      if numpy:
  71          po = numpy.get_printoptions()
  72          assert po["precision"] == 0
  73      assert f(pi) == "3"
  74      f.float_precision = 2
  75      if numpy:
  76          po = numpy.get_printoptions()
  77          assert po["precision"] == 2
  78      assert f(pi) == "3.14"
  79      f.float_precision = "%g"
  80      if numpy:
  81          po = numpy.get_printoptions()
  82          assert po["precision"] == 2
  83      assert f(pi) == "3.14159"
  84      f.float_precision = "%e"
  85      assert f(pi) == "3.141593e+00"
  86      f.float_precision = ""
  87      if numpy:
  88          po = numpy.get_printoptions()
  89          assert po["precision"] == 8
  90      assert f(pi) == repr(pi)
  91  
  92  
  93  def test_bad_precision():
  94      """test various invalid values for float_precision."""
  95      f = PlainTextFormatter()
  96      def set_fp(p):
  97          f.float_precision = p
  98  
  99      pytest.raises(ValueError, set_fp, "%")
 100      pytest.raises(ValueError, set_fp, "%.3f%i")
 101      pytest.raises(ValueError, set_fp, "foo")
 102      pytest.raises(ValueError, set_fp, -1)
 103  
 104  def test_for_type():
 105      f = PlainTextFormatter()
 106  
 107      # initial return, None
 108      assert f.for_type(C, foo_printer) is None
 109      # no func queries
 110      assert f.for_type(C) is foo_printer
 111      # shouldn't change anything
 112      assert f.for_type(C) is foo_printer
 113      # None should do the same
 114      assert f.for_type(C, None) is foo_printer
 115      assert f.for_type(C, None) is foo_printer
 116  
 117  def test_for_type_string():
 118      f = PlainTextFormatter()
 119  
 120      type_str = '%s.%s' % (C.__module__, 'C')
 121  
 122      # initial return, None
 123      assert f.for_type(type_str, foo_printer) is None
 124      # no func queries
 125      assert f.for_type(type_str) is foo_printer
 126      assert _mod_name_key(C) in f.deferred_printers
 127      assert f.for_type(C) is foo_printer
 128      assert _mod_name_key(C) not in f.deferred_printers
 129      assert C in f.type_printers
 130  
 131  def test_for_type_by_name():
 132      f = PlainTextFormatter()
 133  
 134      mod = C.__module__
 135  
 136      # initial return, None
 137      assert f.for_type_by_name(mod, "C", foo_printer) is None
 138      # no func queries
 139      assert f.for_type_by_name(mod, "C") is foo_printer
 140      # shouldn't change anything
 141      assert f.for_type_by_name(mod, "C") is foo_printer
 142      # None should do the same
 143      assert f.for_type_by_name(mod, "C", None) is foo_printer
 144      assert f.for_type_by_name(mod, "C", None) is foo_printer
 145  
 146  
 147  def test_lookup():
 148      f = PlainTextFormatter()
 149  
 150      f.for_type(C, foo_printer)
 151      assert f.lookup(C()) is foo_printer
 152      with pytest.raises(KeyError):
 153          f.lookup(A())
 154  
 155  def test_lookup_string():
 156      f = PlainTextFormatter()
 157      type_str = '%s.%s' % (C.__module__, 'C')
 158  
 159      f.for_type(type_str, foo_printer)
 160      assert f.lookup(C()) is foo_printer
 161      # should move from deferred to imported dict
 162      assert _mod_name_key(C) not in f.deferred_printers
 163      assert C in f.type_printers
 164  
 165  def test_lookup_by_type():
 166      f = PlainTextFormatter()
 167      f.for_type(C, foo_printer)
 168      assert f.lookup_by_type(C) is foo_printer
 169      with pytest.raises(KeyError):
 170          f.lookup_by_type(A)
 171  
 172  def test_lookup_by_type_string():
 173      f = PlainTextFormatter()
 174      type_str = '%s.%s' % (C.__module__, 'C')
 175      f.for_type(type_str, foo_printer)
 176  
 177      # verify insertion
 178      assert _mod_name_key(C) in f.deferred_printers
 179      assert C not in f.type_printers
 180  
 181      assert f.lookup_by_type(type_str) is foo_printer
 182      # lookup by string doesn't cause import
 183      assert _mod_name_key(C) in f.deferred_printers
 184      assert C not in f.type_printers
 185  
 186      assert f.lookup_by_type(C) is foo_printer
 187      # should move from deferred to imported dict
 188      assert _mod_name_key(C) not in f.deferred_printers
 189      assert C in f.type_printers
 190  
 191  def test_in_formatter():
 192      f = PlainTextFormatter()
 193      f.for_type(C, foo_printer)
 194      type_str = '%s.%s' % (C.__module__, 'C')
 195      assert C in f
 196      assert type_str in f
 197  
 198  def test_string_in_formatter():
 199      f = PlainTextFormatter()
 200      type_str = '%s.%s' % (C.__module__, 'C')
 201      f.for_type(type_str, foo_printer)
 202      assert type_str in f
 203      assert C in f
 204  
 205  def test_pop():
 206      f = PlainTextFormatter()
 207      f.for_type(C, foo_printer)
 208      assert f.lookup_by_type(C) is foo_printer
 209      assert f.pop(C, None) is foo_printer
 210      f.for_type(C, foo_printer)
 211      assert f.pop(C) is foo_printer
 212      with pytest.raises(KeyError):
 213          f.lookup_by_type(C)
 214      with pytest.raises(KeyError):
 215          f.pop(C)
 216      with pytest.raises(KeyError):
 217          f.pop(A)
 218      assert f.pop(A, None) is None
 219  
 220  def test_pop_string():
 221      f = PlainTextFormatter()
 222      type_str = '%s.%s' % (C.__module__, 'C')
 223  
 224      with pytest.raises(KeyError):
 225          f.pop(type_str)
 226  
 227      f.for_type(type_str, foo_printer)
 228      f.pop(type_str)
 229      with pytest.raises(KeyError):
 230          f.lookup_by_type(C)
 231      with pytest.raises(KeyError):
 232          f.pop(type_str)
 233  
 234      f.for_type(C, foo_printer)
 235      assert f.pop(type_str, None) is foo_printer
 236      with pytest.raises(KeyError):
 237          f.lookup_by_type(C)
 238      with pytest.raises(KeyError):
 239          f.pop(type_str)
 240      assert f.pop(type_str, None) is None
 241  
 242  
 243  def test_error_method():
 244      f = HTMLFormatter()
 245      class BadHTML(object):
 246          def _repr_html_(self):
 247              raise ValueError("Bad HTML")
 248      bad = BadHTML()
 249      with capture_output() as captured:
 250          result = f(bad)
 251      assert result is None
 252      assert "Traceback" in captured.stdout
 253      assert "Bad HTML" in captured.stdout
 254      assert "_repr_html_" in captured.stdout
 255  
 256  def test_nowarn_notimplemented():
 257      f = HTMLFormatter()
 258      class HTMLNotImplemented(object):
 259          def _repr_html_(self):
 260              raise NotImplementedError
 261      h = HTMLNotImplemented()
 262      with capture_output() as captured:
 263          result = f(h)
 264      assert result is None
 265      assert "" == captured.stderr
 266      assert "" == captured.stdout
 267  
 268  
 269  def test_warn_error_for_type():
 270      f = HTMLFormatter()
 271      f.for_type(int, lambda i: name_error)
 272      with capture_output() as captured:
 273          result = f(5)
 274      assert result is None
 275      assert "Traceback" in captured.stdout
 276      assert "NameError" in captured.stdout
 277      assert "name_error" in captured.stdout
 278  
 279  def test_error_pretty_method():
 280      f = PlainTextFormatter()
 281      class BadPretty(object):
 282          def _repr_pretty_(self):
 283              return "hello"
 284      bad = BadPretty()
 285      with capture_output() as captured:
 286          result = f(bad)
 287      assert result is None
 288      assert "Traceback" in captured.stdout
 289      assert "_repr_pretty_" in captured.stdout
 290      assert "given" in captured.stdout
 291      assert "argument" in captured.stdout
 292  
 293  
 294  def test_bad_repr_traceback():
 295      f = PlainTextFormatter()
 296      bad = BadRepr()
 297      with capture_output() as captured:
 298          result = f(bad)
 299      # catches error, returns None
 300      assert result is None
 301      assert "Traceback" in captured.stdout
 302      assert "__repr__" in captured.stdout
 303      assert "ValueError" in captured.stdout
 304  
 305  
 306  class MakePDF(object):
 307      def _repr_pdf_(self):
 308          return 'PDF'
 309  
 310  def test_pdf_formatter():
 311      pdf = MakePDF()
 312      f = PDFFormatter()
 313      assert f(pdf) == "PDF"
 314  
 315  
 316  def test_print_method_bound():
 317      f = HTMLFormatter()
 318      class MyHTML(object):
 319          def _repr_html_(self):
 320              return "hello"
 321      with capture_output() as captured:
 322          result = f(MyHTML)
 323      assert result is None
 324      assert "FormatterWarning" not in captured.stderr
 325  
 326      with capture_output() as captured:
 327          result = f(MyHTML())
 328      assert result == "hello"
 329      assert captured.stderr == ""
 330  
 331  
 332  def test_print_method_weird():
 333  
 334      class TextMagicHat(object):
 335          def __getattr__(self, key):
 336              return key
 337  
 338      f = HTMLFormatter()
 339  
 340      text_hat = TextMagicHat()
 341      assert text_hat._repr_html_ == "_repr_html_"
 342      with capture_output() as captured:
 343          result = f(text_hat)
 344  
 345      assert result is None
 346      assert "FormatterWarning" not in captured.stderr
 347  
 348      class CallableMagicHat(object):
 349          def __getattr__(self, key):
 350              return lambda : key
 351  
 352      call_hat = CallableMagicHat()
 353      with capture_output() as captured:
 354          result = f(call_hat)
 355  
 356      assert result is None
 357  
 358      class BadReprArgs(object):
 359          def _repr_html_(self, extra, args):
 360              return "html"
 361  
 362      bad = BadReprArgs()
 363      with capture_output() as captured:
 364          result = f(bad)
 365  
 366      assert result is None
 367      assert "FormatterWarning" not in captured.stderr
 368  
 369  
 370  def test_format_config():
 371      """config objects don't pretend to support fancy reprs with lazy attrs"""
 372      f = HTMLFormatter()
 373      cfg = Config()
 374      with capture_output() as captured:
 375          result = f(cfg)
 376      assert result is None
 377      assert captured.stderr == ""
 378  
 379      with capture_output() as captured:
 380          result = f(Config)
 381      assert result is None
 382      assert captured.stderr == ""
 383  
 384  
 385  def test_pretty_max_seq_length():
 386      f = PlainTextFormatter(max_seq_length=1)
 387      lis = list(range(3))
 388      text = f(lis)
 389      assert text == "[0, ...]"
 390      f.max_seq_length = 0
 391      text = f(lis)
 392      assert text == "[0, 1, 2]"
 393      text = f(list(range(1024)))
 394      lines = text.splitlines()
 395      assert len(lines) == 1024
 396  
 397  
 398  def test_ipython_display_formatter():
 399      """Objects with _ipython_display_ defined bypass other formatters"""
 400      f = get_ipython().display_formatter
 401      catcher = []
 402      class SelfDisplaying(object):
 403          def _ipython_display_(self):
 404              catcher.append(self)
 405  
 406      class NotSelfDisplaying(object):
 407          def __repr__(self):
 408              return "NotSelfDisplaying"
 409  
 410          def _ipython_display_(self):
 411              raise NotImplementedError
 412  
 413      save_enabled = f.ipython_display_formatter.enabled
 414      f.ipython_display_formatter.enabled = True
 415  
 416      yes = SelfDisplaying()
 417      no = NotSelfDisplaying()
 418  
 419      d, md = f.format(no)
 420      assert d == {"text/plain": repr(no)}
 421      assert md == {}
 422      assert catcher == []
 423  
 424      d, md = f.format(yes)
 425      assert d == {}
 426      assert md == {}
 427      assert catcher == [yes]
 428  
 429      f.ipython_display_formatter.enabled = save_enabled
 430  
 431  
 432  def test_repr_mime():
 433      class HasReprMime(object):
 434          def _repr_mimebundle_(self, include=None, exclude=None):
 435              return {
 436                  'application/json+test.v2': {
 437                      'x': 'y'
 438                  },
 439                  'plain/text' : '<HasReprMime>',
 440                  'image/png' : 'i-overwrite'
 441              }
 442  
 443          def _repr_png_(self):
 444              return 'should-be-overwritten'
 445          def _repr_html_(self):
 446              return '<b>hi!</b>'
 447  
 448      f = get_ipython().display_formatter
 449      html_f = f.formatters['text/html']
 450      save_enabled = html_f.enabled
 451      html_f.enabled = True
 452      obj = HasReprMime()
 453      d, md = f.format(obj)
 454      html_f.enabled = save_enabled
 455  
 456      assert sorted(d) == [
 457          "application/json+test.v2",
 458          "image/png",
 459          "plain/text",
 460          "text/html",
 461          "text/plain",
 462      ]
 463      assert md == {}
 464  
 465      d, md = f.format(obj, include={"image/png"})
 466      assert list(d.keys()) == [
 467          "image/png"
 468      ], "Include should filter out even things from repr_mimebundle"
 469  
 470      assert d["image/png"] == "i-overwrite", "_repr_mimebundle_ take precedence"
 471  
 472  
 473  def test_pass_correct_include_exclude():
 474      class Tester(object):
 475  
 476          def __init__(self, include=None, exclude=None):
 477              self.include = include
 478              self.exclude = exclude
 479  
 480          def _repr_mimebundle_(self, include, exclude, **kwargs):
 481              if include and (include != self.include):
 482                  raise ValueError('include got modified: display() may be broken.')
 483              if exclude and (exclude != self.exclude):
 484                  raise ValueError('exclude got modified: display() may be broken.')
 485  
 486              return None
 487  
 488      include = {'a', 'b', 'c'}
 489      exclude = {'c', 'e' , 'f'}
 490  
 491      f = get_ipython().display_formatter
 492      f.format(Tester(include=include, exclude=exclude), include=include, exclude=exclude)
 493      f.format(Tester(exclude=exclude), exclude=exclude)
 494      f.format(Tester(include=include), include=include)
 495  
 496  
 497  def test_repr_mime_meta():
 498      class HasReprMimeMeta(object):
 499          def _repr_mimebundle_(self, include=None, exclude=None):
 500              data = {
 501                  'image/png': 'base64-image-data',
 502              }
 503              metadata = {
 504                  'image/png': {
 505                      'width': 5,
 506                      'height': 10,
 507                  }
 508              }
 509              return (data, metadata)
 510  
 511      f = get_ipython().display_formatter
 512      obj = HasReprMimeMeta()
 513      d, md = f.format(obj)
 514      assert sorted(d) == ["image/png", "text/plain"]
 515      assert md == {
 516          "image/png": {
 517              "width": 5,
 518              "height": 10,
 519          }
 520      }
 521  
 522  
 523  def test_repr_mime_failure():
 524      class BadReprMime(object):
 525          def _repr_mimebundle_(self, include=None, exclude=None):
 526              raise RuntimeError
 527  
 528      f = get_ipython().display_formatter
 529      obj = BadReprMime()
 530      d, md = f.format(obj)
 531      assert "text/plain" in d
 532  
 533  
 534  def test_custom_repr_namedtuple_partialmethod():
 535      from functools import partialmethod
 536      from typing import NamedTuple
 537  
 538      class Foo(NamedTuple): ...
 539  
 540      Foo.__repr__ = partialmethod(lambda obj: "Hello World")
 541      foo = Foo()
 542  
 543      f = PlainTextFormatter()
 544      assert f.pprint
 545      assert f(foo) == "Hello World"
 546