diff options
Diffstat (limited to 'pylint/reporters')
-rw-r--r-- | pylint/reporters/__init__.py | 4 | ||||
-rw-r--r-- | pylint/reporters/guireporter.py | 2 | ||||
-rw-r--r-- | pylint/reporters/html.py | 4 | ||||
-rw-r--r-- | pylint/reporters/text.py | 2 | ||||
-rw-r--r-- | pylint/reporters/ureports/__init__.py | 124 | ||||
-rw-r--r-- | pylint/reporters/ureports/html_writer.py | 127 | ||||
-rw-r--r-- | pylint/reporters/ureports/nodes.py | 183 | ||||
-rw-r--r-- | pylint/reporters/ureports/text_writer.py | 143 | ||||
-rw-r--r-- | pylint/reporters/ureports/tree.py | 235 |
9 files changed, 817 insertions, 7 deletions
diff --git a/pylint/reporters/__init__.py b/pylint/reporters/__init__.py index 7103ade..5c4437a 100644 --- a/pylint/reporters/__init__.py +++ b/pylint/reporters/__init__.py @@ -21,9 +21,6 @@ import warnings import six - -from pylint import utils - CMPS = ['=', '-', '+'] # py3k has no more cmp builtin @@ -138,4 +135,5 @@ class CollectingReporter(BaseReporter): def initialize(linter): """initialize linter with reporters in this package """ + from pylint import utils utils.register_plugins(linter, __path__[0]) diff --git a/pylint/reporters/guireporter.py b/pylint/reporters/guireporter.py index 4ad4ebb..32f5d22 100644 --- a/pylint/reporters/guireporter.py +++ b/pylint/reporters/guireporter.py @@ -4,7 +4,7 @@ import sys from pylint.interfaces import IReporter from pylint.reporters import BaseReporter -from logilab.common.ureports import TextWriter +from pylint.reporters.ureports.text_writer import TextWriter class GUIReporter(BaseReporter): diff --git a/pylint/reporters/html.py b/pylint/reporters/html.py index 1e050d3..b2214b1 100644 --- a/pylint/reporters/html.py +++ b/pylint/reporters/html.py @@ -17,10 +17,10 @@ import itertools import string import sys -from logilab.common.ureports import HTMLWriter, Section, Table - from pylint.interfaces import IReporter from pylint.reporters import BaseReporter +from pylint.reporters.ureports.html_writer import HTMLWriter +from pylint.reporters.ureports.nodes import Section, Table class HTMLReporter(BaseReporter): diff --git a/pylint/reporters/text.py b/pylint/reporters/text.py index 22b0609..78b1099 100644 --- a/pylint/reporters/text.py +++ b/pylint/reporters/text.py @@ -22,11 +22,11 @@ import warnings import sys import six -from logilab.common.ureports import TextWriter from pylint.interfaces import IReporter from pylint.reporters import BaseReporter from pylint import utils +from pylint.reporters.ureports.text_writer import TextWriter TITLE_UNDERLINES = ['', '=', '-', '.'] diff --git a/pylint/reporters/ureports/__init__.py b/pylint/reporters/ureports/__init__.py new file mode 100644 index 0000000..0da4051 --- /dev/null +++ b/pylint/reporters/ureports/__init__.py @@ -0,0 +1,124 @@ +# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of pylint. +# +# pylint is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 2.1 of the License, or (at your option) any +# later version. +# +# pylint is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with pylint. If not, see <http://www.gnu.org/licenses/>. +"""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 + +import six + + +# pylint: disable=method-hidden; Weird API in compute_content. + +class BaseWriter(object): + """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.__compute_funcs = [] + 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=u''): + """write a line in the output buffer""" + self.write(string + os.linesep) + + def write(self, string): + """write a string in the output buffer""" + try: + self.out.write(string) + except UnicodeEncodeError: + self.out.write(string.encode(self.encoding)) + + 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(u'') + 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) + """ + # use cells ! + def write(data): + try: + stream.write(data) + except UnicodeEncodeError: + stream.write(data.encode(self.encoding)) + def writeln(data=u''): + try: + stream.write(data + os.linesep) + except UnicodeEncodeError: + stream.write(data.encode(self.encoding) + os.linesep) + self.write = write + self.writeln = writeln + self.__compute_funcs.append((write, writeln)) + for child in layout.children: + stream = six.StringIO() + child.accept(self) + yield stream.getvalue() + self.__compute_funcs.pop() + try: + self.write, self.writeln = self.__compute_funcs[-1] + except IndexError: + del self.write + del self.writeln diff --git a/pylint/reporters/ureports/html_writer.py b/pylint/reporters/ureports/html_writer.py new file mode 100644 index 0000000..005ac62 --- /dev/null +++ b/pylint/reporters/ureports/html_writer.py @@ -0,0 +1,127 @@ +# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of pylint. +# +# pylint is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 2.1 of the License, or (at your option) any +# later version. +# +# pylint is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with pylint. If not, see <http://www.gnu.org/licenses/>. +"""HTML formatting drivers for ureports""" + +from pylint.reporters.ureports import BaseWriter + + +class HTMLWriter(BaseWriter): + """format layouts as HTML""" + + def __init__(self, snippet=None): + super(HTMLWriter, self).__init__() + self.snippet = snippet + + @staticmethod + def handle_attrs(layout): + """get an attribute string from layout member attributes""" + attrs = u'' + klass = getattr(layout, 'klass', None) + if klass: + attrs += u' class="%s"' % klass + nid = getattr(layout, 'id', None) + if nid: + attrs += u' id="%s"' % nid + return attrs + + def begin_format(self): + """begin to format a layout""" + super(HTMLWriter, self).begin_format() + if self.snippet is None: + self.writeln(u'<html>') + self.writeln(u'<body>') + + def end_format(self): + """finished to format a layout""" + if self.snippet is None: + self.writeln(u'</body>') + self.writeln(u'</html>') + + def visit_section(self, layout): + """display a section as html, using div + h[section level]""" + self.section += 1 + self.writeln(u'<div%s>' % self.handle_attrs(layout)) + self.format_children(layout) + self.writeln(u'</div>') + self.section -= 1 + + def visit_title(self, layout): + """display a title using <hX>""" + self.write(u'<h%s%s>' % (self.section, self.handle_attrs(layout))) + self.format_children(layout) + self.writeln(u'</h%s>' % self.section) + + def visit_table(self, layout): + """display a table as html""" + self.writeln(u'<table%s>' % self.handle_attrs(layout)) + table_content = self.get_table_content(layout) + for i, row in enumerate(table_content): + if i == 0 and layout.rheaders: + self.writeln(u'<tr class="header">') + elif i+1 == len(table_content) and layout.rrheaders: + self.writeln(u'<tr class="header">') + else: + self.writeln(u'<tr class="%s">' % (i%2 and 'even' or 'odd')) + for j, cell in enumerate(row): + cell = cell or u' ' + if (layout.rheaders and i == 0) or \ + (layout.cheaders and j == 0) or \ + (layout.rrheaders and i+1 == len(table_content)) or \ + (layout.rcheaders and j+1 == len(row)): + self.writeln(u'<th>%s</th>' % cell) + else: + self.writeln(u'<td>%s</td>' % cell) + self.writeln(u'</tr>') + self.writeln(u'</table>') + + def visit_list(self, layout): + """display a list as html""" + self.writeln(u'<ul%s>' % self.handle_attrs(layout)) + for row in list(self.compute_content(layout)): + self.writeln(u'<li>%s</li>' % row) + self.writeln(u'</ul>') + + def visit_paragraph(self, layout): + """display links (using <p>)""" + self.write(u'<p>') + self.format_children(layout) + self.write(u'</p>') + + def visit_span(self, layout): + """display links (using <p>)""" + self.write(u'<span%s>' % self.handle_attrs(layout)) + self.format_children(layout) + self.write(u'</span>') + + def visit_link(self, layout): + """display links (using <a>)""" + self.write(u' <a href="%s"%s>%s</a>' % (layout.url, + self.handle_attrs(layout), + layout.label)) + def visit_verbatimtext(self, layout): + """display verbatim text (using <pre>)""" + self.write(u'<pre>') + self.write(layout.data.replace(u'&', u'&').replace(u'<', u'<')) + self.write(u'</pre>') + + def visit_text(self, layout): + """add some text""" + data = layout.data + if layout.escaped: + data = data.replace(u'&', u'&').replace(u'<', u'<') + self.write(data) diff --git a/pylint/reporters/ureports/nodes.py b/pylint/reporters/ureports/nodes.py new file mode 100644 index 0000000..01cbcb7 --- /dev/null +++ b/pylint/reporters/ureports/nodes.py @@ -0,0 +1,183 @@ +# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of pylint. +# +# pylint is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 2.1 of the License, or (at your option) any +# later version. +# +# pylint is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with pylint. If not, see <http://www.gnu.org/licenses/>. +"""Micro reports objects. + +A micro report is a tree of layout and content objects. +""" + +from six import string_types + +from pylint.reporters.ureports.tree import VNode + + +class BaseComponent(VNode): + """base report component + + attributes + * id : the component's optional id + * klass : the component's optional klass + """ + def __init__(self, id=None, klass=None): + super(BaseComponent, self).__init__(id) + self.klass = klass + + +class BaseLayout(BaseComponent): + """base container node + + attributes + * BaseComponent 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, BaseComponent): + 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(BaseComponent): + """a text portion + + attributes : + * BaseComponent 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, string_types), data.__class__ + self.escaped = escaped + self.data = data + + +class VerbatimText(Text): + """a verbatim text, display the raw data + + attributes : + * BaseComponent attributes + * data : the text value as an encoded or unicode string + """ + + +class Link(BaseComponent): + """a labelled link + + attributes : + * BaseComponent attributes + * url : the link's target (REQUIRED) + * label : the link's label as a string (use the url by default) + """ + def __init__(self, url, label=None, **kwargs): + super(Link, self).__init__(**kwargs) + assert url + self.url = url + self.label = label or url + + +# 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 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, rrheaders=0, rcheaders=0, + **kwargs): + super(Table, self).__init__(**kwargs) + assert isinstance(cols, int) + self.cols = cols + self.title = title + self.rheaders = rheaders + self.cheaders = cheaders + self.rrheaders = rrheaders + self.rcheaders = rcheaders + + +class List(BaseLayout): + """some list data + + attributes : + * BaseLayout attributes + """ diff --git a/pylint/reporters/ureports/text_writer.py b/pylint/reporters/ureports/text_writer.py new file mode 100644 index 0000000..545f999 --- /dev/null +++ b/pylint/reporters/ureports/text_writer.py @@ -0,0 +1,143 @@ +# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of logilab-common. +# +# pylint is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 2.1 of the License, or (at your option) any +# later version. +# +# pylint is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with pylint. If not, see <http://www.gnu.org/licenses/>. +"""Text formatting drivers for ureports""" + +from __future__ import print_function + +import os + +from six.moves import range + +from pylint.reporters.ureports import BaseWriter + + +TITLE_UNDERLINES = [u'', u'=', u'-', u'`', u'.', u'~', u'^'] +BULLETS = [u'*', u'-'] + +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 + self.pending_urls = [] + + def visit_section(self, layout): + """display a section as text + """ + self.section += 1 + self.writeln() + self.format_children(layout) + if self.pending_urls: + self.writeln() + for label, url in self.pending_urls: + self.writeln(u'.. _`%s`: %s' % (label, url)) + self.pending_urls = [] + self.section -= 1 + self.writeln() + + def visit_title(self, layout): + title = u''.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_span(self, layout): + """enter a span""" + self.format_children(layout) + + 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 in range(len(row)): + col = row[index] + cols_width[index] = max(cols_width[index], len(col)) + if layout.klass == 'field': + self.field_table(layout, table_content, cols_width) + else: + 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 = u' '.join([u'%%-%ss'] * len(cols_width)) + format_strings = format_strings % tuple(cols_width) + format_strings = format_strings.split(' ') + table_linesep = u'\n+' + u'+'.join([u'-'*w for w in cols_width]) + u'+\n' + headsep = u'\n+' + u'+'.join([u'='*w for w in cols_width]) + u'+\n' + # FIXME: layout.cheaders + self.write(table_linesep) + for i in range(len(table_content)): + self.write(u'|') + line = table_content[i] + for j in range(len(line)): + self.write(format_strings[j] % line[j]) + self.write(u'|') + if i == 0 and layout.rheaders: + self.write(headsep) + else: + self.write(table_linesep) + + def field_table(self, layout, table_content, cols_width): + """special case for field table""" + assert layout.cols == 2 + format_string = u'%s%%-%ss: %%s' % (os.linesep, cols_width[0]) + for field, value in table_content: + self.write(format_string % (field, value)) + + def visit_list(self, layout): + """display a list layout as text""" + bullet = BULLETS[self.list_level % len(BULLETS)] + indent = ' ' * self.list_level + self.list_level += 1 + for child in layout.children: + self.write(u'%s%s%s ' % (os.linesep, indent, bullet)) + child.accept(self) + self.list_level -= 1 + + def visit_link(self, layout): + """add a hyperlink""" + if layout.label != layout.url: + self.write(u'`%s`_' % layout.label) + self.pending_urls.append((layout.label, layout.url)) + else: + self.write(layout.url) + + def visit_verbatimtext(self, layout): + """display a verbatim layout as text (so difficult ;) + """ + self.writeln(u'::\n') + for line in layout.data.splitlines(): + self.writeln(u' ' + line) + self.writeln() + + def visit_text(self, layout): + """add some text""" + self.write(u'%s' % layout.data) diff --git a/pylint/reporters/ureports/tree.py b/pylint/reporters/ureports/tree.py new file mode 100644 index 0000000..99965f2 --- /dev/null +++ b/pylint/reporters/ureports/tree.py @@ -0,0 +1,235 @@ +# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of pylint. +# +# logilab-common is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 2.1 of the License, or (at your option) any +# later version. +# +# pylint is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with pylint. If not, see <http://www.gnu.org/licenses/>. + + +class NodeNotFound(Exception): + """raised when a node has not been found""" + +EX_SIBLING_NOT_FOUND = "No such sibling as '%s'" +EX_CHILD_NOT_FOUND = "No such child as '%s'" +EX_NODE_NOT_FOUND = "No such node as '%s'" + + +# Base node ################################################################### + +class Node(object): + """a basic tree node, characterized by an id""" + + def __init__(self, nid=None): + self.id = nid + # navigation + self.parent = None + self.children = [] + + def __iter__(self): + return iter(self.children) + + def __str__(self, indent=0): + s = ['%s%s %s' % (' '*indent, self.__class__.__name__, self.id)] + indent += 2 + for child in self.children: + try: + s.append(child.__str__(indent)) + except TypeError: + s.append(child.__str__()) + return '\n'.join(s) + + def is_leaf(self): + return not self.children + + def append(self, child): + """add a node to children""" + self.children.append(child) + child.parent = self + + def remove(self, child): + """remove a child node""" + self.children.remove(child) + child.parent = None + + def insert(self, index, child): + """insert a child node""" + self.children.insert(index, child) + child.parent = self + + def replace(self, old_child, new_child): + """replace a child node with another""" + i = self.children.index(old_child) + self.children.pop(i) + self.children.insert(i, new_child) + new_child.parent = self + + def get_sibling(self, nid): + """return the sibling node that has given id""" + try: + return self.parent.get_child_by_id(nid) + except NodeNotFound: + raise NodeNotFound(EX_SIBLING_NOT_FOUND % nid) + + def next_sibling(self): + """ + return the next sibling for this node if any + """ + parent = self.parent + if parent is None: + # root node has no sibling + return None + index = parent.children.index(self) + try: + return parent.children[index+1] + except IndexError: + return None + + def previous_sibling(self): + """ + return the previous sibling for this node if any + """ + parent = self.parent + if parent is None: + # root node has no sibling + return None + index = parent.children.index(self) + if index > 0: + return parent.children[index-1] + return None + + def get_node_by_id(self, nid): + """ + return node in whole hierarchy that has given id + """ + root = self.root() + try: + return root.get_child_by_id(nid, 1) + except NodeNotFound: + raise NodeNotFound(EX_NODE_NOT_FOUND % nid) + + def get_child_by_id(self, nid, recurse=None): + """ + return child of given id + """ + if self.id == nid: + return self + for c in self.children: + if recurse: + try: + return c.get_child_by_id(nid, 1) + except NodeNotFound: + continue + if c.id == nid: + return c + raise NodeNotFound(EX_CHILD_NOT_FOUND % nid) + + def get_child_by_path(self, path): + """ + return child of given path (path is a list of ids) + """ + if len(path) > 0 and path[0] == self.id: + if len(path) == 1: + return self + else: + for c in self.children: + try: + return c.get_child_by_path(path[1:]) + except NodeNotFound: + pass + raise NodeNotFound(EX_CHILD_NOT_FOUND % path) + + def depth(self): + """ + return depth of this node in the tree + """ + if self.parent is not None: + return 1 + self.parent.depth() + else: + return 0 + + def depth_down(self): + """ + return depth of the tree from this node + """ + if self.children: + return 1 + max([c.depth_down() for c in self.children]) + return 1 + + def width(self): + """ + return the width of the tree from this node + """ + return len(self.leaves()) + + def root(self): + """ + return the root node of the tree + """ + if self.parent is not None: + return self.parent.root() + return self + + def leaves(self): + """ + return a list with all the leaves nodes descendant from this node + """ + leaves = [] + if self.children: + for child in self.children: + leaves += child.leaves() + return leaves + else: + return [self] + + def flatten(self, _list=None): + """ + return a list with all the nodes descendant from this node + """ + if _list is None: + _list = [] + _list.append(self) + for c in self.children: + c.flatten(_list) + return _list + + def lineage(self): + """ + return list of parents up to root node + """ + lst = [self] + if self.parent is not None: + lst.extend(self.parent.lineage()) + return lst + + +class VNode(Node): + + 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: + return self.TYPE.replace('-', '_') + 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) |