8th day of python challenges 111-117
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
# Copyright (c) 2015-2016 Claudiu Popa <pcmanticore@gmail.com>
|
||||
# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
|
||||
# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com>
|
||||
# Copyright (c) 2018 Anthony Sottile <asottile@umich.edu>
|
||||
|
||||
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
|
||||
|
||||
"""Universal report objects and some formatting drivers.
|
||||
|
||||
A way to create simple reports using python objects, primarily designed to be
|
||||
formatted as text and html.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
from io import StringIO
|
||||
|
||||
|
||||
class BaseWriter:
|
||||
"""base class for ureport writers"""
|
||||
|
||||
def format(self, layout, stream=None, encoding=None):
|
||||
"""format and write the given layout into the stream object
|
||||
|
||||
unicode policy: unicode strings may be found in the layout;
|
||||
try to call stream.write with it, but give it back encoded using
|
||||
the given encoding if it fails
|
||||
"""
|
||||
if stream is None:
|
||||
stream = sys.stdout
|
||||
if not encoding:
|
||||
encoding = getattr(stream, "encoding", "UTF-8")
|
||||
self.encoding = encoding or "UTF-8"
|
||||
self.out = stream
|
||||
self.begin_format()
|
||||
layout.accept(self)
|
||||
self.end_format()
|
||||
|
||||
def format_children(self, layout):
|
||||
"""recurse on the layout children and call their accept method
|
||||
(see the Visitor pattern)
|
||||
"""
|
||||
for child in getattr(layout, "children", ()):
|
||||
child.accept(self)
|
||||
|
||||
def writeln(self, string=""):
|
||||
"""write a line in the output buffer"""
|
||||
self.write(string + os.linesep)
|
||||
|
||||
def write(self, string):
|
||||
"""write a string in the output buffer"""
|
||||
self.out.write(string)
|
||||
|
||||
def begin_format(self):
|
||||
"""begin to format a layout"""
|
||||
self.section = 0
|
||||
|
||||
def end_format(self):
|
||||
"""finished to format a layout"""
|
||||
|
||||
def get_table_content(self, table):
|
||||
"""trick to get table content without actually writing it
|
||||
|
||||
return an aligned list of lists containing table cells values as string
|
||||
"""
|
||||
result = [[]]
|
||||
cols = table.cols
|
||||
for cell in self.compute_content(table):
|
||||
if cols == 0:
|
||||
result.append([])
|
||||
cols = table.cols
|
||||
cols -= 1
|
||||
result[-1].append(cell)
|
||||
# fill missing cells
|
||||
while len(result[-1]) < cols:
|
||||
result[-1].append("")
|
||||
return result
|
||||
|
||||
def compute_content(self, layout):
|
||||
"""trick to compute the formatting of children layout before actually
|
||||
writing it
|
||||
|
||||
return an iterator on strings (one for each child element)
|
||||
"""
|
||||
# Patch the underlying output stream with a fresh-generated stream,
|
||||
# which is used to store a temporary representation of a child
|
||||
# node.
|
||||
out = self.out
|
||||
try:
|
||||
for child in layout.children:
|
||||
stream = StringIO()
|
||||
self.out = stream
|
||||
child.accept(self)
|
||||
yield stream.getvalue()
|
||||
finally:
|
||||
self.out = out
|
@@ -0,0 +1,188 @@
|
||||
# Copyright (c) 2015-2016 Claudiu Popa <pcmanticore@gmail.com>
|
||||
# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
|
||||
# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com>
|
||||
# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
|
||||
|
||||
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
|
||||
|
||||
"""Micro reports objects.
|
||||
|
||||
A micro report is a tree of layout and content objects.
|
||||
"""
|
||||
|
||||
|
||||
class VNode:
|
||||
def __init__(self, nid=None):
|
||||
self.id = nid
|
||||
# navigation
|
||||
self.parent = None
|
||||
self.children = []
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.children)
|
||||
|
||||
def append(self, child):
|
||||
"""add a node to children"""
|
||||
self.children.append(child)
|
||||
child.parent = self
|
||||
|
||||
def insert(self, index, child):
|
||||
"""insert a child node"""
|
||||
self.children.insert(index, child)
|
||||
child.parent = self
|
||||
|
||||
def _get_visit_name(self):
|
||||
"""
|
||||
return the visit name for the mixed class. When calling 'accept', the
|
||||
method <'visit_' + name returned by this method> will be called on the
|
||||
visitor
|
||||
"""
|
||||
try:
|
||||
# pylint: disable=no-member
|
||||
return self.TYPE.replace("-", "_")
|
||||
# pylint: disable=broad-except
|
||||
except Exception:
|
||||
return self.__class__.__name__.lower()
|
||||
|
||||
def accept(self, visitor, *args, **kwargs):
|
||||
func = getattr(visitor, "visit_%s" % self._get_visit_name())
|
||||
return func(self, *args, **kwargs)
|
||||
|
||||
def leave(self, visitor, *args, **kwargs):
|
||||
func = getattr(visitor, "leave_%s" % self._get_visit_name())
|
||||
return func(self, *args, **kwargs)
|
||||
|
||||
|
||||
class BaseLayout(VNode):
|
||||
"""base container node
|
||||
|
||||
attributes
|
||||
* children : components in this table (i.e. the table's cells)
|
||||
"""
|
||||
|
||||
def __init__(self, children=(), **kwargs):
|
||||
super(BaseLayout, self).__init__(**kwargs)
|
||||
for child in children:
|
||||
if isinstance(child, VNode):
|
||||
self.append(child)
|
||||
else:
|
||||
self.add_text(child)
|
||||
|
||||
def append(self, child):
|
||||
"""overridden to detect problems easily"""
|
||||
assert child not in self.parents()
|
||||
VNode.append(self, child)
|
||||
|
||||
def parents(self):
|
||||
"""return the ancestor nodes"""
|
||||
assert self.parent is not self
|
||||
if self.parent is None:
|
||||
return []
|
||||
return [self.parent] + self.parent.parents()
|
||||
|
||||
def add_text(self, text):
|
||||
"""shortcut to add text data"""
|
||||
self.children.append(Text(text))
|
||||
|
||||
|
||||
# non container nodes #########################################################
|
||||
|
||||
|
||||
class Text(VNode):
|
||||
"""a text portion
|
||||
|
||||
attributes :
|
||||
* data : the text value as an encoded or unicode string
|
||||
"""
|
||||
|
||||
def __init__(self, data, escaped=True, **kwargs):
|
||||
super(Text, self).__init__(**kwargs)
|
||||
# if isinstance(data, unicode):
|
||||
# data = data.encode('ascii')
|
||||
assert isinstance(data, str), data.__class__
|
||||
self.escaped = escaped
|
||||
self.data = data
|
||||
|
||||
|
||||
class VerbatimText(Text):
|
||||
"""a verbatim text, display the raw data
|
||||
|
||||
attributes :
|
||||
* data : the text value as an encoded or unicode string
|
||||
"""
|
||||
|
||||
|
||||
# container nodes #############################################################
|
||||
|
||||
|
||||
class Section(BaseLayout):
|
||||
"""a section
|
||||
|
||||
attributes :
|
||||
* BaseLayout attributes
|
||||
|
||||
a title may also be given to the constructor, it'll be added
|
||||
as a first element
|
||||
a description may also be given to the constructor, it'll be added
|
||||
as a first paragraph
|
||||
"""
|
||||
|
||||
def __init__(self, title=None, description=None, **kwargs):
|
||||
super(Section, self).__init__(**kwargs)
|
||||
if description:
|
||||
self.insert(0, Paragraph([Text(description)]))
|
||||
if title:
|
||||
self.insert(0, Title(children=(title,)))
|
||||
|
||||
|
||||
class EvaluationSection(Section):
|
||||
def __init__(self, message, **kwargs):
|
||||
super(EvaluationSection, self).__init__(**kwargs)
|
||||
title = Paragraph()
|
||||
title.append(Text("-" * len(message)))
|
||||
self.append(title)
|
||||
|
||||
message_body = Paragraph()
|
||||
message_body.append(Text(message))
|
||||
self.append(message_body)
|
||||
|
||||
|
||||
class Title(BaseLayout):
|
||||
"""a title
|
||||
|
||||
attributes :
|
||||
* BaseLayout attributes
|
||||
|
||||
A title must not contains a section nor a paragraph!
|
||||
"""
|
||||
|
||||
|
||||
class Paragraph(BaseLayout):
|
||||
"""a simple text paragraph
|
||||
|
||||
attributes :
|
||||
* BaseLayout attributes
|
||||
|
||||
A paragraph must not contains a section !
|
||||
"""
|
||||
|
||||
|
||||
class Table(BaseLayout):
|
||||
"""some tabular data
|
||||
|
||||
attributes :
|
||||
* BaseLayout attributes
|
||||
* cols : the number of columns of the table (REQUIRED)
|
||||
* rheaders : the first row's elements are table's header
|
||||
* cheaders : the first col's elements are table's header
|
||||
* title : the table's optional title
|
||||
"""
|
||||
|
||||
def __init__(self, cols, title=None, rheaders=0, cheaders=0, **kwargs):
|
||||
super(Table, self).__init__(**kwargs)
|
||||
assert isinstance(cols, int)
|
||||
self.cols = cols
|
||||
self.title = title
|
||||
self.rheaders = rheaders
|
||||
self.cheaders = cheaders
|
@@ -0,0 +1,97 @@
|
||||
# Copyright (c) 2015-2016 Claudiu Popa <pcmanticore@gmail.com>
|
||||
# Copyright (c) 2018 Anthony Sottile <asottile@umich.edu>
|
||||
|
||||
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
|
||||
|
||||
"""Text formatting drivers for ureports"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
from pylint.reporters.ureports import BaseWriter
|
||||
|
||||
|
||||
TITLE_UNDERLINES = ["", "=", "-", "`", ".", "~", "^"]
|
||||
BULLETS = ["*", "-"]
|
||||
|
||||
|
||||
class TextWriter(BaseWriter):
|
||||
"""format layouts as text
|
||||
(ReStructured inspiration but not totally handled yet)
|
||||
"""
|
||||
|
||||
def begin_format(self):
|
||||
super(TextWriter, self).begin_format()
|
||||
self.list_level = 0
|
||||
|
||||
def visit_section(self, layout):
|
||||
"""display a section as text
|
||||
"""
|
||||
self.section += 1
|
||||
self.writeln()
|
||||
self.format_children(layout)
|
||||
self.section -= 1
|
||||
self.writeln()
|
||||
|
||||
def visit_evaluationsection(self, layout):
|
||||
"""Display an evaluation section as a text."""
|
||||
self.section += 1
|
||||
self.format_children(layout)
|
||||
self.section -= 1
|
||||
self.writeln()
|
||||
|
||||
def visit_title(self, layout):
|
||||
title = "".join(list(self.compute_content(layout)))
|
||||
self.writeln(title)
|
||||
try:
|
||||
self.writeln(TITLE_UNDERLINES[self.section] * len(title))
|
||||
except IndexError:
|
||||
print("FIXME TITLE TOO DEEP. TURNING TITLE INTO TEXT")
|
||||
|
||||
def visit_paragraph(self, layout):
|
||||
"""enter a paragraph"""
|
||||
self.format_children(layout)
|
||||
self.writeln()
|
||||
|
||||
def visit_table(self, layout):
|
||||
"""display a table as text"""
|
||||
table_content = self.get_table_content(layout)
|
||||
# get columns width
|
||||
cols_width = [0] * len(table_content[0])
|
||||
for row in table_content:
|
||||
for index, col in enumerate(row):
|
||||
cols_width[index] = max(cols_width[index], len(col))
|
||||
self.default_table(layout, table_content, cols_width)
|
||||
self.writeln()
|
||||
|
||||
def default_table(self, layout, table_content, cols_width):
|
||||
"""format a table"""
|
||||
cols_width = [size + 1 for size in cols_width]
|
||||
format_strings = " ".join(["%%-%ss"] * len(cols_width))
|
||||
format_strings = format_strings % tuple(cols_width)
|
||||
format_strings = format_strings.split(" ")
|
||||
table_linesep = "\n+" + "+".join(["-" * w for w in cols_width]) + "+\n"
|
||||
headsep = "\n+" + "+".join(["=" * w for w in cols_width]) + "+\n"
|
||||
# FIXME: layout.cheaders
|
||||
self.write(table_linesep)
|
||||
for index, line in enumerate(table_content):
|
||||
self.write("|")
|
||||
for line_index, at_index in enumerate(line):
|
||||
self.write(format_strings[line_index] % at_index)
|
||||
self.write("|")
|
||||
if index == 0 and layout.rheaders:
|
||||
self.write(headsep)
|
||||
else:
|
||||
self.write(table_linesep)
|
||||
|
||||
def visit_verbatimtext(self, layout):
|
||||
"""display a verbatim layout as text (so difficult ;)
|
||||
"""
|
||||
self.writeln("::\n")
|
||||
for line in layout.data.splitlines():
|
||||
self.writeln(" " + line)
|
||||
self.writeln()
|
||||
|
||||
def visit_text(self, layout):
|
||||
"""add some text"""
|
||||
self.write("%s" % layout.data)
|
Reference in New Issue
Block a user