From 2fc4b7d67010eb7f8718d85a49ab2426621c7d85 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Sat, 9 Mar 2019 11:26:02 +0100 Subject: [PATCH] Refs #273 - Replaced vendored markup lib by dependency --- NOTICE | 6 - requirements.txt | 1 + setup.py | 1 + tablib/compat.py | 2 - tablib/formats/_html.py | 5 +- tablib/packages/markup.py | 482 ------------------------------------ tablib/packages/markup3.py | 484 ------------------------------------- test_tablib.py | 3 +- 8 files changed, 6 insertions(+), 978 deletions(-) delete mode 100644 NOTICE delete mode 100644 tablib/packages/markup.py delete mode 100644 tablib/packages/markup3.py diff --git a/NOTICE b/NOTICE deleted file mode 100644 index 6966f470..00000000 --- a/NOTICE +++ /dev/null @@ -1,6 +0,0 @@ -Tablib includes some vendorized Python libraries: markup. - -Markup License -============== - -Markup is in the public domain. diff --git a/requirements.txt b/requirements.txt index afa868d7..7f99a7e1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,6 +4,7 @@ chardet==3.0.4 et-xmlfile==1.0.1 idna==2.6 jdcal==1.3 +MarkupPy==1.14 numpy==1.13.1 odfpy==1.3.5 openpyxl==2.4.8 diff --git a/setup.py b/setup.py index 4e65dd2e..4553dde4 100755 --- a/setup.py +++ b/setup.py @@ -36,6 +36,7 @@ 'odfpy', 'openpyxl>=2.4.0', 'backports.csv;python_version<"3.0"', + 'markuppy' 'xlrd', 'xlwt', 'pyyaml', diff --git a/tablib/compat.py b/tablib/compat.py index 660697d1..5b8490c2 100644 --- a/tablib/compat.py +++ b/tablib/compat.py @@ -16,7 +16,6 @@ if is_py3: from io import BytesIO from io import StringIO - from tablib.packages import markup3 as markup from statistics import median from itertools import zip_longest as izip_longest import csv @@ -28,7 +27,6 @@ else: from cStringIO import StringIO as BytesIO from StringIO import StringIO - from tablib.packages import markup from tablib.packages.statistics import median from itertools import izip_longest from backports import csv diff --git a/tablib/formats/_html.py b/tablib/formats/_html.py index 655e74c5..33f32908 100644 --- a/tablib/formats/_html.py +++ b/tablib/formats/_html.py @@ -7,14 +7,13 @@ if sys.version_info[0] > 2: from io import BytesIO as StringIO - from tablib.packages import markup3 as markup else: from cStringIO import StringIO - from tablib.packages import markup +import codecs +from MarkupPy import markup import tablib from tablib.compat import unicode -import codecs BOOK_ENDINGS = 'h3' diff --git a/tablib/packages/markup.py b/tablib/packages/markup.py deleted file mode 100644 index e52acae2..00000000 --- a/tablib/packages/markup.py +++ /dev/null @@ -1,482 +0,0 @@ -# This code is in the public domain, it comes -# with absolutely no warranty and you can do -# absolutely whatever you want with it. - -__date__ = '17 May 2007' -__version__ = '1.7' -__doc__= """ -This is markup.py - a Python module that attempts to -make it easier to generate HTML/XML from a Python program -in an intuitive, lightweight, customizable and pythonic way. - -The code is in the public domain. - -Version: %s as of %s. - -Documentation and further info is at http://markup.sourceforge.net/ - -Please send bug reports, feature requests, enhancement -ideas or questions to nogradi at gmail dot com. - -Installation: drop markup.py somewhere into your Python path. -""" % ( __version__, __date__ ) - -import string - -class element: - """This class handles the addition of a new element.""" - - def __init__( self, tag, case='lower', parent=None ): - self.parent = parent - - if case == 'lower': - self.tag = tag.lower( ) - else: - self.tag = tag.upper( ) - - def __call__( self, *args, **kwargs ): - if len( args ) > 1: - raise ArgumentError( self.tag ) - - # if class_ was defined in parent it should be added to every element - if self.parent is not None and self.parent.class_ is not None: - if 'class_' not in kwargs: - kwargs['class_'] = self.parent.class_ - - if self.parent is None and len( args ) == 1: - x = [ self.render( self.tag, False, myarg, mydict ) for myarg, mydict in _argsdicts( args, kwargs ) ] - return '\n'.join( x ) - elif self.parent is None and len( args ) == 0: - x = [ self.render( self.tag, True, myarg, mydict ) for myarg, mydict in _argsdicts( args, kwargs ) ] - return '\n'.join( x ) - - if self.tag in self.parent.twotags: - for myarg, mydict in _argsdicts( args, kwargs ): - self.render( self.tag, False, myarg, mydict ) - elif self.tag in self.parent.onetags: - if len( args ) == 0: - for myarg, mydict in _argsdicts( args, kwargs ): - self.render( self.tag, True, myarg, mydict ) # here myarg is always None, because len( args ) = 0 - else: - raise ClosingError( self.tag ) - elif self.parent.mode == 'strict_html' and self.tag in self.parent.deptags: - raise DeprecationError( self.tag ) - else: - raise InvalidElementError( self.tag, self.parent.mode ) - - def render( self, tag, single, between, kwargs ): - """Append the actual tags to content.""" - - out = u"<%s" % tag - for key, value in kwargs.iteritems( ): - if value is not None: # when value is None that means stuff like <... checked> - key = key.strip('_') # strip this so class_ will mean class, etc. - if key in ['http_equiv', 'accept_charset']: - key.replace('_','-') - out = u"%s %s=\"%s\"" % ( out, key, escape( value ) ) - else: - out = u"%s %s" % ( out, key ) - if between is not None: - out = u"%s>%s" % ( out, between, tag ) - else: - if single: - out = u"%s />" % out - else: - out = u"%s>" % out - if self.parent is not None: - self.parent.content.append( out ) - else: - return out - - def close( self ): - """Append a closing tag unless element has only opening tag.""" - - if self.tag in self.parent.twotags: - self.parent.content.append( "" % self.tag ) - elif self.tag in self.parent.onetags: - raise ClosingError( self.tag ) - elif self.parent.mode == 'strict_html' and self.tag in self.parent.deptags: - raise DeprecationError( self.tag ) - - def open( self, **kwargs ): - """Append an opening tag.""" - - if self.tag in self.parent.twotags or self.tag in self.parent.onetags: - self.render( self.tag, False, None, kwargs ) - elif self.mode == 'strict_html' and self.tag in self.parent.deptags: - raise DeprecationError( self.tag ) - -class page: - """This is our main class representing a document. Elements are added - as attributes of an instance of this class.""" - - def __init__( self, mode='strict_html', case='lower', onetags=None, twotags=None, separator='\n', class_=None ): - """Stuff that effects the whole document. - - mode -- 'strict_html' for HTML 4.01 (default) - 'html' alias for 'strict_html' - 'loose_html' to allow some deprecated elements - 'xml' to allow arbitrary elements - - case -- 'lower' element names will be printed in lower case (default) - 'upper' they will be printed in upper case - - onetags -- list or tuple of valid elements with opening tags only - twotags -- list or tuple of valid elements with both opening and closing tags - these two keyword arguments may be used to select - the set of valid elements in 'xml' mode - invalid elements will raise appropriate exceptions - - separator -- string to place between added elements, defaults to newline - - class_ -- a class that will be added to every element if defined""" - - valid_onetags = [ "AREA", "BASE", "BR", "COL", "FRAME", "HR", "IMG", "INPUT", "LINK", "META", "PARAM" ] - valid_twotags = [ "A", "ABBR", "ACRONYM", "ADDRESS", "B", "BDO", "BIG", "BLOCKQUOTE", "BODY", "BUTTON", - "CAPTION", "CITE", "CODE", "COLGROUP", "DD", "DEL", "DFN", "DIV", "DL", "DT", "EM", "FIELDSET", - "FORM", "FRAMESET", "H1", "H2", "H3", "H4", "H5", "H6", "HEAD", "HTML", "I", "IFRAME", "INS", - "KBD", "LABEL", "LEGEND", "LI", "MAP", "NOFRAMES", "NOSCRIPT", "OBJECT", "OL", "OPTGROUP", - "OPTION", "P", "PRE", "Q", "SAMP", "SCRIPT", "SELECT", "SMALL", "SPAN", "STRONG", "STYLE", - "SUB", "SUP", "TABLE", "TBODY", "TD", "TEXTAREA", "TFOOT", "TH", "THEAD", "TITLE", "TR", - "TT", "UL", "VAR" ] - deprecated_onetags = [ "BASEFONT", "ISINDEX" ] - deprecated_twotags = [ "APPLET", "CENTER", "DIR", "FONT", "MENU", "S", "STRIKE", "U" ] - - self.header = [ ] - self.content = [ ] - self.footer = [ ] - self.case = case - self.separator = separator - - # init( ) sets it to True so we know that has to be printed at the end - self._full = False - self.class_= class_ - - if mode == 'strict_html' or mode == 'html': - self.onetags = valid_onetags - self.onetags += map( string.lower, self.onetags ) - self.twotags = valid_twotags - self.twotags += map( string.lower, self.twotags ) - self.deptags = deprecated_onetags + deprecated_twotags - self.deptags += map( string.lower, self.deptags ) - self.mode = 'strict_html' - elif mode == 'loose_html': - self.onetags = valid_onetags + deprecated_onetags - self.onetags += map( string.lower, self.onetags ) - self.twotags = valid_twotags + deprecated_twotags - self.twotags += map( string.lower, self.twotags ) - self.mode = mode - elif mode == 'xml': - if onetags and twotags: - self.onetags = onetags - self.twotags = twotags - elif ( onetags and not twotags ) or ( twotags and not onetags ): - raise CustomizationError( ) - else: - self.onetags = russell( ) - self.twotags = russell( ) - self.mode = mode - else: - raise ModeError( mode ) - - def __getattr__( self, attr ): - if attr.startswith("__") and attr.endswith("__"): - raise AttributeError(attr) - return element( attr, case=self.case, parent=self ) - - def __str__( self ): - - if self._full and ( self.mode == 'strict_html' or self.mode == 'loose_html' ): - end = [ '', '' ] - else: - end = [ ] - - return self.separator.join( self.header + self.content + self.footer + end ) - - def __call__( self, escape=False ): - """Return the document as a string. - - escape -- False print normally - True replace < and > by < and > - the default escape sequences in most browsers""" - - if escape: - return _escape( self.__str__( ) ) - else: - return self.__str__( ) - - def add( self, text ): - """This is an alias to addcontent.""" - self.addcontent( text ) - - def addfooter( self, text ): - """Add some text to the bottom of the document""" - self.footer.append( text ) - - def addheader( self, text ): - """Add some text to the top of the document""" - self.header.append( text ) - - def addcontent( self, text ): - """Add some text to the main part of the document""" - self.content.append( text ) - - - def init( self, lang='en', css=None, metainfo=None, title=None, header=None, - footer=None, charset=None, encoding=None, doctype=None, bodyattrs=None, script=None ): - """This method is used for complete documents with appropriate - doctype, encoding, title, etc information. For an HTML/XML snippet - omit this method. - - lang -- language, usually a two character string, will appear - as in html mode (ignored in xml mode) - - css -- Cascading Style Sheet filename as a string or a list of - strings for multiple css files (ignored in xml mode) - - metainfo -- a dictionary in the form { 'name':'content' } to be inserted - into meta element(s) as - (ignored in xml mode) - - bodyattrs --a dictionary in the form { 'key':'value', ... } which will be added - as attributes of the element as - (ignored in xml mode) - - script -- dictionary containing src:type pairs, - - title -- the title of the document as a string to be inserted into - a title element as my title (ignored in xml mode) - - header -- some text to be inserted right after the element - (ignored in xml mode) - - footer -- some text to be inserted right before the element - (ignored in xml mode) - - charset -- a string defining the character set, will be inserted into a - - element (ignored in xml mode) - - encoding -- a string defining the encoding, will be put into to first line of - the document as in - xml mode (ignored in html mode) - - doctype -- the document type string, defaults to - - in html mode (ignored in xml mode)""" - - self._full = True - - if self.mode == 'strict_html' or self.mode == 'loose_html': - if doctype is None: - doctype = "" - self.header.append( doctype ) - self.html( lang=lang ) - self.head( ) - if charset is not None: - self.meta( http_equiv='Content-Type', content="text/html; charset=%s" % charset ) - if metainfo is not None: - self.metainfo( metainfo ) - if css is not None: - self.css( css ) - if title is not None: - self.title( title ) - if script is not None: - self.scripts( script ) - self.head.close() - if bodyattrs is not None: - self.body( **bodyattrs ) - else: - self.body( ) - if header is not None: - self.content.append( header ) - if footer is not None: - self.footer.append( footer ) - - elif self.mode == 'xml': - if doctype is None: - if encoding is not None: - doctype = "" % encoding - else: - doctype = "" - self.header.append( doctype ) - - def css( self, filelist ): - """This convenience function is only useful for html. - It adds css stylesheet(s) to the document via the element.""" - - if isinstance( filelist, basestring ): - self.link( href=filelist, rel='stylesheet', type='text/css', media='all' ) - else: - for file in filelist: - self.link( href=file, rel='stylesheet', type='text/css', media='all' ) - - def metainfo( self, mydict ): - """This convenience function is only useful for html. - It adds meta information via the element, the argument is - a dictionary of the form { 'name':'content' }.""" - - if isinstance( mydict, dict ): - for name, content in mydict.iteritems( ): - self.meta( name=name, content=content ) - else: - raise TypeError ("Metainfo should be called with a dictionary argument of name:content pairs.") - - def scripts( self, mydict ): - """Only useful in html, mydict is dictionary of src:type pairs will - be rendered as """ - - if isinstance( mydict, dict ): - for src, type in mydict.iteritems( ): - self.script( '', src=src, type='text/%s' % type ) - else: - raise TypeError ("Script should be given a dictionary of src:type pairs.") - - -class _oneliner: - """An instance of oneliner returns a string corresponding to one element. - This class can be used to write 'oneliners' that return a string - immediately so there is no need to instantiate the page class.""" - - def __init__( self, case='lower' ): - self.case = case - - def __getattr__( self, attr ): - if attr.startswith("__") and attr.endswith("__"): - raise AttributeError(attr) - return element( attr, case=self.case, parent=None ) - -oneliner = _oneliner( case='lower' ) -upper_oneliner = _oneliner( case='upper' ) - -def _argsdicts( args, mydict ): - """A utility generator that pads argument list and dictionary values, will only be called with len( args ) = 0, 1.""" - - if len( args ) == 0: - args = None, - elif len( args ) == 1: - args = _totuple( args[0] ) - else: - raise Exception("We should have never gotten here.") - - mykeys = mydict.keys( ) - myvalues = map( _totuple, mydict.values( ) ) - - maxlength = max( map( len, [ args ] + myvalues ) ) - - for i in xrange( maxlength ): - thisdict = { } - for key, value in zip( mykeys, myvalues ): - try: - thisdict[ key ] = value[i] - except IndexError: - thisdict[ key ] = value[-1] - try: - thisarg = args[i] - except IndexError: - thisarg = args[-1] - - yield thisarg, thisdict - -def _totuple( x ): - """Utility stuff to convert string, int, float, None or anything to a usable tuple.""" - - if isinstance( x, basestring ): - out = x, - elif isinstance( x, ( int, float ) ): - out = str( x ), - elif x is None: - out = None, - else: - out = tuple( x ) - - return out - -def escape( text, newline=False ): - """Escape special html characters.""" - - if isinstance( text, basestring ): - if '&' in text: - text = text.replace( '&', '&' ) - if '>' in text: - text = text.replace( '>', '>' ) - if '<' in text: - text = text.replace( '<', '<' ) - if '\"' in text: - text = text.replace( '\"', '"' ) - if '\'' in text: - text = text.replace( '\'', '"' ) - if newline: - if '\n' in text: - text = text.replace( '\n', '
' ) - - return text - -_escape = escape - -def unescape( text ): - """Inverse of escape.""" - - if isinstance( text, basestring ): - if '&' in text: - text = text.replace( '&', '&' ) - if '>' in text: - text = text.replace( '>', '>' ) - if '<' in text: - text = text.replace( '<', '<' ) - if '"' in text: - text = text.replace( '"', '\"' ) - - return text - -class dummy: - """A dummy class for attaching attributes.""" - pass - -doctype = dummy( ) -doctype.frameset = "" -doctype.strict = "" -doctype.loose = "" - -class russell: - """A dummy class that contains anything.""" - - def __contains__( self, item ): - return True - - -class MarkupError( Exception ): - """All our exceptions subclass this.""" - def __str__( self ): - return self.message - -class ClosingError( MarkupError ): - def __init__( self, tag ): - self.message = "The element '%s' does not accept non-keyword arguments (has no closing tag)." % tag - -class OpeningError( MarkupError ): - def __init__( self, tag ): - self.message = "The element '%s' can not be opened." % tag - -class ArgumentError( MarkupError ): - def __init__( self, tag ): - self.message = "The element '%s' was called with more than one non-keyword argument." % tag - -class InvalidElementError( MarkupError ): - def __init__( self, tag, mode ): - self.message = "The element '%s' is not valid for your mode '%s'." % ( tag, mode ) - -class DeprecationError( MarkupError ): - def __init__( self, tag ): - self.message = "The element '%s' is deprecated, instantiate markup.page with mode='loose_html' to allow it." % tag - -class ModeError( MarkupError ): - def __init__( self, mode ): - self.message = "Mode '%s' is invalid, possible values: strict_html, loose_html, xml." % mode - -class CustomizationError( MarkupError ): - def __init__( self ): - self.message = "If you customize the allowed elements, you must define both types 'onetags' and 'twotags'." - -if __name__ == '__main__': - print (__doc__) diff --git a/tablib/packages/markup3.py b/tablib/packages/markup3.py deleted file mode 100644 index a4fc637c..00000000 --- a/tablib/packages/markup3.py +++ /dev/null @@ -1,484 +0,0 @@ -# This code is in the public domain, it comes -# with absolutely no warranty and you can do -# absolutely whatever you want with it. - -__date__ = '17 May 2007' -__version__ = '1.7' -__doc__= """ -This is markup.py - a Python module that attempts to -make it easier to generate HTML/XML from a Python program -in an intuitive, lightweight, customizable and pythonic way. - -The code is in the public domain. - -Version: %s as of %s. - -Documentation and further info is at http://markup.sourceforge.net/ - -Please send bug reports, feature requests, enhancement -ideas or questions to nogradi at gmail dot com. - -Installation: drop markup.py somewhere into your Python path. -""" % ( __version__, __date__ ) - -import string - -class element: - """This class handles the addition of a new element.""" - - def __init__( self, tag, case='lower', parent=None ): - self.parent = parent - - if case == 'lower': - self.tag = tag.lower( ) - else: - self.tag = tag.upper( ) - - def __call__( self, *args, **kwargs ): - if len( args ) > 1: - raise ArgumentError( self.tag ) - - # if class_ was defined in parent it should be added to every element - if self.parent is not None and self.parent.class_ is not None: - if 'class_' not in kwargs: - kwargs['class_'] = self.parent.class_ - - if self.parent is None and len( args ) == 1: - x = [ self.render( self.tag, False, myarg, mydict ) for myarg, mydict in _argsdicts( args, kwargs ) ] - return '\n'.join( x ) - elif self.parent is None and len( args ) == 0: - x = [ self.render( self.tag, True, myarg, mydict ) for myarg, mydict in _argsdicts( args, kwargs ) ] - return '\n'.join( x ) - - if self.tag in self.parent.twotags: - for myarg, mydict in _argsdicts( args, kwargs ): - self.render( self.tag, False, myarg, mydict ) - elif self.tag in self.parent.onetags: - if len( args ) == 0: - for myarg, mydict in _argsdicts( args, kwargs ): - self.render( self.tag, True, myarg, mydict ) # here myarg is always None, because len( args ) = 0 - else: - raise ClosingError( self.tag ) - elif self.parent.mode == 'strict_html' and self.tag in self.parent.deptags: - raise DeprecationError( self.tag ) - else: - raise InvalidElementError( self.tag, self.parent.mode ) - - def render( self, tag, single, between, kwargs ): - """Append the actual tags to content.""" - - out = "<%s" % tag - for key, value in kwargs.items( ): - if value is not None: # when value is None that means stuff like <... checked> - key = key.strip('_') # strip this so class_ will mean class, etc. - if key == 'http_equiv': # special cases, maybe change _ to - overall? - key = 'http-equiv' - elif key == 'accept_charset': - key = 'accept-charset' - out = "%s %s=\"%s\"" % ( out, key, escape( value ) ) - else: - out = "%s %s" % ( out, key ) - if between is not None: - out = "%s>%s" % ( out, between, tag ) - else: - if single: - out = "%s />" % out - else: - out = "%s>" % out - if self.parent is not None: - self.parent.content.append( out ) - else: - return out - - def close( self ): - """Append a closing tag unless element has only opening tag.""" - - if self.tag in self.parent.twotags: - self.parent.content.append( "" % self.tag ) - elif self.tag in self.parent.onetags: - raise ClosingError( self.tag ) - elif self.parent.mode == 'strict_html' and self.tag in self.parent.deptags: - raise DeprecationError( self.tag ) - - def open( self, **kwargs ): - """Append an opening tag.""" - - if self.tag in self.parent.twotags or self.tag in self.parent.onetags: - self.render( self.tag, False, None, kwargs ) - elif self.mode == 'strict_html' and self.tag in self.parent.deptags: - raise DeprecationError( self.tag ) - -class page: - """This is our main class representing a document. Elements are added - as attributes of an instance of this class.""" - - def __init__( self, mode='strict_html', case='lower', onetags=None, twotags=None, separator='\n', class_=None ): - """Stuff that effects the whole document. - - mode -- 'strict_html' for HTML 4.01 (default) - 'html' alias for 'strict_html' - 'loose_html' to allow some deprecated elements - 'xml' to allow arbitrary elements - - case -- 'lower' element names will be printed in lower case (default) - 'upper' they will be printed in upper case - - onetags -- list or tuple of valid elements with opening tags only - twotags -- list or tuple of valid elements with both opening and closing tags - these two keyword arguments may be used to select - the set of valid elements in 'xml' mode - invalid elements will raise appropriate exceptions - - separator -- string to place between added elements, defaults to newline - - class_ -- a class that will be added to every element if defined""" - - valid_onetags = [ "AREA", "BASE", "BR", "COL", "FRAME", "HR", "IMG", "INPUT", "LINK", "META", "PARAM" ] - valid_twotags = [ "A", "ABBR", "ACRONYM", "ADDRESS", "B", "BDO", "BIG", "BLOCKQUOTE", "BODY", "BUTTON", - "CAPTION", "CITE", "CODE", "COLGROUP", "DD", "DEL", "DFN", "DIV", "DL", "DT", "EM", "FIELDSET", - "FORM", "FRAMESET", "H1", "H2", "H3", "H4", "H5", "H6", "HEAD", "HTML", "I", "IFRAME", "INS", - "KBD", "LABEL", "LEGEND", "LI", "MAP", "NOFRAMES", "NOSCRIPT", "OBJECT", "OL", "OPTGROUP", - "OPTION", "P", "PRE", "Q", "SAMP", "SCRIPT", "SELECT", "SMALL", "SPAN", "STRONG", "STYLE", - "SUB", "SUP", "TABLE", "TBODY", "TD", "TEXTAREA", "TFOOT", "TH", "THEAD", "TITLE", "TR", - "TT", "UL", "VAR" ] - deprecated_onetags = [ "BASEFONT", "ISINDEX" ] - deprecated_twotags = [ "APPLET", "CENTER", "DIR", "FONT", "MENU", "S", "STRIKE", "U" ] - - self.header = [ ] - self.content = [ ] - self.footer = [ ] - self.case = case - self.separator = separator - - # init( ) sets it to True so we know that has to be printed at the end - self._full = False - self.class_= class_ - - if mode == 'strict_html' or mode == 'html': - self.onetags = valid_onetags - self.onetags += list(map( str.lower, self.onetags )) - self.twotags = valid_twotags - self.twotags += list(map( str.lower, self.twotags )) - self.deptags = deprecated_onetags + deprecated_twotags - self.deptags += list(map( str.lower, self.deptags )) - self.mode = 'strict_html' - elif mode == 'loose_html': - self.onetags = valid_onetags + deprecated_onetags - self.onetags += list(map( str.lower, self.onetags )) - self.twotags = valid_twotags + deprecated_twotags - self.twotags += list(map( str.lower, self.twotags )) - self.mode = mode - elif mode == 'xml': - if onetags and twotags: - self.onetags = onetags - self.twotags = twotags - elif ( onetags and not twotags ) or ( twotags and not onetags ): - raise CustomizationError( ) - else: - self.onetags = russell( ) - self.twotags = russell( ) - self.mode = mode - else: - raise ModeError( mode ) - - def __getattr__( self, attr ): - if attr.startswith("__") and attr.endswith("__"): - raise AttributeError(attr) - return element( attr, case=self.case, parent=self ) - - def __str__( self ): - - if self._full and ( self.mode == 'strict_html' or self.mode == 'loose_html' ): - end = [ '', '' ] - else: - end = [ ] - - return self.separator.join( self.header + self.content + self.footer + end ) - - def __call__( self, escape=False ): - """Return the document as a string. - - escape -- False print normally - True replace < and > by < and > - the default escape sequences in most browsers""" - - if escape: - return _escape( self.__str__( ) ) - else: - return self.__str__( ) - - def add( self, text ): - """This is an alias to addcontent.""" - self.addcontent( text ) - - def addfooter( self, text ): - """Add some text to the bottom of the document""" - self.footer.append( text ) - - def addheader( self, text ): - """Add some text to the top of the document""" - self.header.append( text ) - - def addcontent( self, text ): - """Add some text to the main part of the document""" - self.content.append( text ) - - - def init( self, lang='en', css=None, metainfo=None, title=None, header=None, - footer=None, charset=None, encoding=None, doctype=None, bodyattrs=None, script=None ): - """This method is used for complete documents with appropriate - doctype, encoding, title, etc information. For an HTML/XML snippet - omit this method. - - lang -- language, usually a two character string, will appear - as in html mode (ignored in xml mode) - - css -- Cascading Style Sheet filename as a string or a list of - strings for multiple css files (ignored in xml mode) - - metainfo -- a dictionary in the form { 'name':'content' } to be inserted - into meta element(s) as - (ignored in xml mode) - - bodyattrs --a dictionary in the form { 'key':'value', ... } which will be added - as attributes of the element as - (ignored in xml mode) - - script -- dictionary containing src:type pairs, - - title -- the title of the document as a string to be inserted into - a title element as my title (ignored in xml mode) - - header -- some text to be inserted right after the element - (ignored in xml mode) - - footer -- some text to be inserted right before the element - (ignored in xml mode) - - charset -- a string defining the character set, will be inserted into a - - element (ignored in xml mode) - - encoding -- a string defining the encoding, will be put into to first line of - the document as in - xml mode (ignored in html mode) - - doctype -- the document type string, defaults to - - in html mode (ignored in xml mode)""" - - self._full = True - - if self.mode == 'strict_html' or self.mode == 'loose_html': - if doctype is None: - doctype = "" - self.header.append( doctype ) - self.html( lang=lang ) - self.head( ) - if charset is not None: - self.meta( http_equiv='Content-Type', content="text/html; charset=%s" % charset ) - if metainfo is not None: - self.metainfo( metainfo ) - if css is not None: - self.css( css ) - if title is not None: - self.title( title ) - if script is not None: - self.scripts( script ) - self.head.close() - if bodyattrs is not None: - self.body( **bodyattrs ) - else: - self.body( ) - if header is not None: - self.content.append( header ) - if footer is not None: - self.footer.append( footer ) - - elif self.mode == 'xml': - if doctype is None: - if encoding is not None: - doctype = "" % encoding - else: - doctype = "" - self.header.append( doctype ) - - def css( self, filelist ): - """This convenience function is only useful for html. - It adds css stylesheet(s) to the document via the element.""" - - if isinstance( filelist, str ): - self.link( href=filelist, rel='stylesheet', type='text/css', media='all' ) - else: - for file in filelist: - self.link( href=file, rel='stylesheet', type='text/css', media='all' ) - - def metainfo( self, mydict ): - """This convenience function is only useful for html. - It adds meta information via the element, the argument is - a dictionary of the form { 'name':'content' }.""" - - if isinstance( mydict, dict ): - for name, content in mydict.items( ): - self.meta( name=name, content=content ) - else: - raise TypeError("Metainfo should be called with a dictionary argument of name:content pairs.") - - def scripts( self, mydict ): - """Only useful in html, mydict is dictionary of src:type pairs will - be rendered as """ - - if isinstance( mydict, dict ): - for src, type in mydict.items( ): - self.script( '', src=src, type='text/%s' % type ) - else: - raise TypeError("Script should be given a dictionary of src:type pairs.") - - -class _oneliner: - """An instance of oneliner returns a string corresponding to one element. - This class can be used to write 'oneliners' that return a string - immediately so there is no need to instantiate the page class.""" - - def __init__( self, case='lower' ): - self.case = case - - def __getattr__( self, attr ): - if attr.startswith("__") and attr.endswith("__"): - raise AttributeError(attr) - return element( attr, case=self.case, parent=None ) - -oneliner = _oneliner( case='lower' ) -upper_oneliner = _oneliner( case='upper' ) - -def _argsdicts( args, mydict ): - """A utility generator that pads argument list and dictionary values, will only be called with len( args ) = 0, 1.""" - - if len( args ) == 0: - args = None, - elif len( args ) == 1: - args = _totuple( args[0] ) - else: - raise Exception("We should have never gotten here.") - - mykeys = list(mydict.keys( )) - myvalues = list(map( _totuple, list(mydict.values( )) )) - - maxlength = max( list(map( len, [ args ] + myvalues )) ) - - for i in range( maxlength ): - thisdict = { } - for key, value in zip( mykeys, myvalues ): - try: - thisdict[ key ] = value[i] - except IndexError: - thisdict[ key ] = value[-1] - try: - thisarg = args[i] - except IndexError: - thisarg = args[-1] - - yield thisarg, thisdict - -def _totuple( x ): - """Utility stuff to convert string, int, float, None or anything to a usable tuple.""" - - if isinstance( x, str ): - out = x, - elif isinstance( x, ( int, float ) ): - out = str( x ), - elif x is None: - out = None, - else: - out = tuple( x ) - - return out - -def escape( text, newline=False ): - """Escape special html characters.""" - - if isinstance( text, str ): - if '&' in text: - text = text.replace( '&', '&' ) - if '>' in text: - text = text.replace( '>', '>' ) - if '<' in text: - text = text.replace( '<', '<' ) - if '\"' in text: - text = text.replace( '\"', '"' ) - if '\'' in text: - text = text.replace( '\'', '"' ) - if newline: - if '\n' in text: - text = text.replace( '\n', '
' ) - - return text - -_escape = escape - -def unescape( text ): - """Inverse of escape.""" - - if isinstance( text, str ): - if '&' in text: - text = text.replace( '&', '&' ) - if '>' in text: - text = text.replace( '>', '>' ) - if '<' in text: - text = text.replace( '<', '<' ) - if '"' in text: - text = text.replace( '"', '\"' ) - - return text - -class dummy: - """A dummy class for attaching attributes.""" - pass - -doctype = dummy( ) -doctype.frameset = "" -doctype.strict = "" -doctype.loose = "" - -class russell: - """A dummy class that contains anything.""" - - def __contains__( self, item ): - return True - - -class MarkupError( Exception ): - """All our exceptions subclass this.""" - def __str__( self ): - return self.message - -class ClosingError( MarkupError ): - def __init__( self, tag ): - self.message = "The element '%s' does not accept non-keyword arguments (has no closing tag)." % tag - -class OpeningError( MarkupError ): - def __init__( self, tag ): - self.message = "The element '%s' can not be opened." % tag - -class ArgumentError( MarkupError ): - def __init__( self, tag ): - self.message = "The element '%s' was called with more than one non-keyword argument." % tag - -class InvalidElementError( MarkupError ): - def __init__( self, tag, mode ): - self.message = "The element '%s' is not valid for your mode '%s'." % ( tag, mode ) - -class DeprecationError( MarkupError ): - def __init__( self, tag ): - self.message = "The element '%s' is deprecated, instantiate markup.page with mode='loose_html' to allow it." % tag - -class ModeError( MarkupError ): - def __init__( self, mode ): - self.message = "Mode '%s' is invalid, possible values: strict_html, loose_html, xml." % mode - -class CustomizationError( MarkupError ): - def __init__( self ): - self.message = "If you customize the allowed elements, you must define both types 'onetags' and 'twotags'." - -if __name__ == '__main__': - print(__doc__) diff --git a/test_tablib.py b/test_tablib.py index e7b7233c..0c842a64 100755 --- a/test_tablib.py +++ b/test_tablib.py @@ -10,8 +10,9 @@ import unittest from uuid import uuid4 +from MarkupPy import markup import tablib -from tablib.compat import markup, unicode, is_py3 +from tablib.compat import unicode, is_py3 from tablib.core import Row from tablib.formats import csv as csv_format