nodes.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. # Copyright (c) 2015-2016, 2018, 2020 Claudiu Popa <pcmanticore@gmail.com>
  2. # Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
  3. # Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com>
  4. # Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
  5. # Copyright (c) 2020 hippo91 <guillaume.peillex@gmail.com>
  6. # Copyright (c) 2020 Anthony Sottile <asottile@umich.edu>
  7. # Copyright (c) 2021 Pierre Sassoulas <pierre.sassoulas@gmail.com>
  8. # Copyright (c) 2021 Daniël van Noord <13665637+DanielNoord@users.noreply.github.com>
  9. # Copyright (c) 2021 Marc Mueller <30130371+cdce8p@users.noreply.github.com>
  10. # Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  11. # For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
  12. """Micro reports objects.
  13. A micro report is a tree of layout and content objects.
  14. """
  15. from typing import Any, Iterable, Iterator, List, Optional, Union
  16. from pylint.reporters.ureports.text_writer import TextWriter
  17. class VNode:
  18. def __init__(self) -> None:
  19. self.parent: Optional["BaseLayout"] = None
  20. self.children: List["VNode"] = []
  21. self.visitor_name: str = self.__class__.__name__.lower()
  22. def __iter__(self) -> Iterator["VNode"]:
  23. return iter(self.children)
  24. def accept(self, visitor: TextWriter, *args: Any, **kwargs: Any) -> None:
  25. func = getattr(visitor, f"visit_{self.visitor_name}")
  26. return func(self, *args, **kwargs)
  27. def leave(self, visitor, *args, **kwargs):
  28. func = getattr(visitor, f"leave_{self.visitor_name}")
  29. return func(self, *args, **kwargs)
  30. class BaseLayout(VNode):
  31. """base container node
  32. attributes
  33. * children : components in this table (i.e. the table's cells)
  34. """
  35. def __init__(self, children: Iterable[Union["Text", str]] = ()) -> None:
  36. super().__init__()
  37. for child in children:
  38. if isinstance(child, VNode):
  39. self.append(child)
  40. else:
  41. self.add_text(child)
  42. def append(self, child: VNode) -> None:
  43. """add a node to children"""
  44. assert child not in self.parents()
  45. self.children.append(child)
  46. child.parent = self
  47. def insert(self, index: int, child: VNode) -> None:
  48. """insert a child node"""
  49. self.children.insert(index, child)
  50. child.parent = self
  51. def parents(self) -> List["BaseLayout"]:
  52. """return the ancestor nodes"""
  53. assert self.parent is not self
  54. if self.parent is None:
  55. return []
  56. return [self.parent] + self.parent.parents()
  57. def add_text(self, text: str) -> None:
  58. """shortcut to add text data"""
  59. self.children.append(Text(text))
  60. # non container nodes #########################################################
  61. class Text(VNode):
  62. """a text portion
  63. attributes :
  64. * data : the text value as an encoded or unicode string
  65. """
  66. def __init__(self, data: str, escaped: bool = True) -> None:
  67. super().__init__()
  68. self.escaped = escaped
  69. self.data = data
  70. class VerbatimText(Text):
  71. """a verbatim text, display the raw data
  72. attributes :
  73. * data : the text value as an encoded or unicode string
  74. """
  75. # container nodes #############################################################
  76. class Section(BaseLayout):
  77. """a section
  78. attributes :
  79. * BaseLayout attributes
  80. a title may also be given to the constructor, it'll be added
  81. as a first element
  82. a description may also be given to the constructor, it'll be added
  83. as a first paragraph
  84. """
  85. def __init__(
  86. self,
  87. title: Optional[str] = None,
  88. description: Optional[str] = None,
  89. children: Iterable[Union["Text", str]] = (),
  90. ) -> None:
  91. super().__init__(children=children)
  92. if description:
  93. self.insert(0, Paragraph([Text(description)]))
  94. if title:
  95. self.insert(0, Title(children=(title,)))
  96. self.report_id: str = "" # Used in ReportHandlerMixin.make_reports
  97. class EvaluationSection(Section):
  98. def __init__(
  99. self, message: str, children: Iterable[Union["Text", str]] = ()
  100. ) -> None:
  101. super().__init__(children=children)
  102. title = Paragraph()
  103. title.append(Text("-" * len(message)))
  104. self.append(title)
  105. message_body = Paragraph()
  106. message_body.append(Text(message))
  107. self.append(message_body)
  108. class Title(BaseLayout):
  109. """a title
  110. attributes :
  111. * BaseLayout attributes
  112. A title must not contains a section nor a paragraph!
  113. """
  114. class Paragraph(BaseLayout):
  115. """a simple text paragraph
  116. attributes :
  117. * BaseLayout attributes
  118. A paragraph must not contains a section !
  119. """
  120. class Table(BaseLayout):
  121. """some tabular data
  122. attributes :
  123. * BaseLayout attributes
  124. * cols : the number of columns of the table (REQUIRED)
  125. * rheaders : the first row's elements are table's header
  126. * cheaders : the first col's elements are table's header
  127. * title : the table's optional title
  128. """
  129. def __init__(
  130. self,
  131. cols: int,
  132. title: Optional[str] = None,
  133. rheaders: int = 0,
  134. cheaders: int = 0,
  135. children: Iterable[Union["Text", str]] = (),
  136. ) -> None:
  137. super().__init__(children=children)
  138. assert isinstance(cols, int)
  139. self.cols = cols
  140. self.title = title
  141. self.rheaders = rheaders
  142. self.cheaders = cheaders