home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2010 November / maximum-cd-2010-11.iso / DiscContents / calibre-0.7.13.msi / file_707 (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2010-08-06  |  53.1 KB  |  1,653 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. from __future__ import generators
  5. __author__ = 'Leonard Richardson (leonardr@segfault.org)'
  6. __version__ = '3.1.0.1'
  7. __copyright__ = 'Copyright (c) 2004-2009 Leonard Richardson'
  8. __license__ = 'New-style BSD'
  9. import codecs
  10. import markupbase
  11. import types
  12. import re
  13. from HTMLParser import HTMLParser, HTMLParseError
  14.  
  15. try:
  16.     from htmlentitydefs import name2codepoint
  17. except ImportError:
  18.     name2codepoint = { }
  19.  
  20.  
  21. try:
  22.     set
  23. except NameError:
  24.     from sets import Set as set
  25.  
  26. markupbase._declname_match = re.compile('[a-zA-Z][-_.:a-zA-Z0-9]*\\s*').match
  27. DEFAULT_OUTPUT_ENCODING = 'utf-8'
  28.  
  29. def sob(unicode, encoding):
  30.     if encoding is None:
  31.         return unicode
  32.     return unicode.encode(encoding)
  33.  
  34.  
  35. class PageElement:
  36.     
  37.     def setup(self, parent = None, previous = None):
  38.         self.parent = parent
  39.         self.previous = previous
  40.         self.next = None
  41.         self.previousSibling = None
  42.         self.nextSibling = None
  43.         if self.parent and self.parent.contents:
  44.             self.previousSibling = self.parent.contents[-1]
  45.             self.previousSibling.nextSibling = self
  46.         
  47.  
  48.     
  49.     def replaceWith(self, replaceWith):
  50.         oldParent = self.parent
  51.         myIndex = self.parent.contents.index(self)
  52.         if hasattr(replaceWith, 'parent') and replaceWith.parent == self.parent:
  53.             index = self.parent.contents.index(replaceWith)
  54.             if index and index < myIndex:
  55.                 myIndex = myIndex - 1
  56.             
  57.         
  58.         self.extract()
  59.         oldParent.insert(myIndex, replaceWith)
  60.  
  61.     
  62.     def extract(self):
  63.         if self.parent:
  64.             
  65.             try:
  66.                 self.parent.contents.remove(self)
  67.             except ValueError:
  68.                 pass
  69.             except:
  70.                 None<EXCEPTION MATCH>ValueError
  71.             
  72.  
  73.         None<EXCEPTION MATCH>ValueError
  74.         lastChild = self._lastRecursiveChild()
  75.         nextElement = lastChild.next
  76.         if self.previous:
  77.             self.previous.next = nextElement
  78.         
  79.         if nextElement:
  80.             nextElement.previous = self.previous
  81.         
  82.         self.previous = None
  83.         lastChild.next = None
  84.         self.parent = None
  85.         if self.previousSibling:
  86.             self.previousSibling.nextSibling = self.nextSibling
  87.         
  88.         if self.nextSibling:
  89.             self.nextSibling.previousSibling = self.previousSibling
  90.         
  91.         self.previousSibling = None
  92.         self.nextSibling = None
  93.         return self
  94.  
  95.     
  96.     def _lastRecursiveChild(self):
  97.         lastChild = self
  98.         while hasattr(lastChild, 'contents') and lastChild.contents:
  99.             lastChild = lastChild.contents[-1]
  100.         return lastChild
  101.  
  102.     
  103.     def insert(self, position, newChild):
  104.         if (isinstance(newChild, basestring) or isinstance(newChild, unicode)) and not isinstance(newChild, NavigableString):
  105.             newChild = NavigableString(newChild)
  106.         
  107.         position = min(position, len(self.contents))
  108.         if hasattr(newChild, 'parent') and newChild.parent != None:
  109.             if newChild.parent == self:
  110.                 index = self.find(newChild)
  111.                 if index and index < position:
  112.                     position = position - 1
  113.                 
  114.             
  115.             newChild.extract()
  116.         
  117.         newChild.parent = self
  118.         previousChild = None
  119.         if position == 0:
  120.             newChild.previousSibling = None
  121.             newChild.previous = self
  122.         else:
  123.             previousChild = self.contents[position - 1]
  124.             newChild.previousSibling = previousChild
  125.             newChild.previousSibling.nextSibling = newChild
  126.             newChild.previous = previousChild._lastRecursiveChild()
  127.         if newChild.previous:
  128.             newChild.previous.next = newChild
  129.         
  130.         newChildsLastElement = newChild._lastRecursiveChild()
  131.         if position >= len(self.contents):
  132.             newChild.nextSibling = None
  133.             parent = self
  134.             parentsNextSibling = None
  135.             while not parentsNextSibling:
  136.                 parentsNextSibling = parent.nextSibling
  137.                 parent = parent.parent
  138.                 if not parent:
  139.                     break
  140.                     continue
  141.             if parentsNextSibling:
  142.                 newChildsLastElement.next = parentsNextSibling
  143.             else:
  144.                 newChildsLastElement.next = None
  145.         else:
  146.             nextChild = self.contents[position]
  147.             newChild.nextSibling = nextChild
  148.             if newChild.nextSibling:
  149.                 newChild.nextSibling.previousSibling = newChild
  150.             
  151.             newChildsLastElement.next = nextChild
  152.         if newChildsLastElement.next:
  153.             newChildsLastElement.next.previous = newChildsLastElement
  154.         
  155.         self.contents.insert(position, newChild)
  156.  
  157.     
  158.     def append(self, tag):
  159.         self.insert(len(self.contents), tag)
  160.  
  161.     
  162.     def findNext(self, name = None, attrs = { }, text = None, **kwargs):
  163.         return self._findOne(self.findAllNext, name, attrs, text, **kwargs)
  164.  
  165.     
  166.     def findAllNext(self, name = None, attrs = { }, text = None, limit = None, **kwargs):
  167.         return self._findAll(name, attrs, text, limit, self.nextGenerator, **kwargs)
  168.  
  169.     
  170.     def findNextSibling(self, name = None, attrs = { }, text = None, **kwargs):
  171.         return self._findOne(self.findNextSiblings, name, attrs, text, **kwargs)
  172.  
  173.     
  174.     def findNextSiblings(self, name = None, attrs = { }, text = None, limit = None, **kwargs):
  175.         return self._findAll(name, attrs, text, limit, self.nextSiblingGenerator, **kwargs)
  176.  
  177.     fetchNextSiblings = findNextSiblings
  178.     
  179.     def findPrevious(self, name = None, attrs = { }, text = None, **kwargs):
  180.         return self._findOne(self.findAllPrevious, name, attrs, text, **kwargs)
  181.  
  182.     
  183.     def findAllPrevious(self, name = None, attrs = { }, text = None, limit = None, **kwargs):
  184.         return self._findAll(name, attrs, text, limit, self.previousGenerator, **kwargs)
  185.  
  186.     fetchPrevious = findAllPrevious
  187.     
  188.     def findPreviousSibling(self, name = None, attrs = { }, text = None, **kwargs):
  189.         return self._findOne(self.findPreviousSiblings, name, attrs, text, **kwargs)
  190.  
  191.     
  192.     def findPreviousSiblings(self, name = None, attrs = { }, text = None, limit = None, **kwargs):
  193.         return self._findAll(name, attrs, text, limit, self.previousSiblingGenerator, **kwargs)
  194.  
  195.     fetchPreviousSiblings = findPreviousSiblings
  196.     
  197.     def findParent(self, name = None, attrs = { }, **kwargs):
  198.         r = None
  199.         l = self.findParents(name, attrs, 1)
  200.         if l:
  201.             r = l[0]
  202.         
  203.         return r
  204.  
  205.     
  206.     def findParents(self, name = None, attrs = { }, limit = None, **kwargs):
  207.         return self._findAll(name, attrs, None, limit, self.parentGenerator, **kwargs)
  208.  
  209.     fetchParents = findParents
  210.     
  211.     def _findOne(self, method, name, attrs, text, **kwargs):
  212.         r = None
  213.         l = method(name, attrs, text, 1, **kwargs)
  214.         if l:
  215.             r = l[0]
  216.         
  217.         return r
  218.  
  219.     
  220.     def _findAll(self, name, attrs, text, limit, generator, **kwargs):
  221.         if isinstance(name, SoupStrainer):
  222.             strainer = name
  223.         else:
  224.             strainer = SoupStrainer(name, attrs, text, **kwargs)
  225.         results = ResultSet(strainer)
  226.         g = generator()
  227.         while True:
  228.             
  229.             try:
  230.                 i = g.next()
  231.             except StopIteration:
  232.                 break
  233.  
  234.             if i:
  235.                 found = strainer.search(i)
  236.                 if found:
  237.                     results.append(found)
  238.                     if limit and len(results) >= limit:
  239.                         break
  240.                     
  241.                 
  242.             found
  243.         return results
  244.  
  245.     
  246.     def nextGenerator(self):
  247.         i = self
  248.         while i:
  249.             i = i.next
  250.             yield i
  251.  
  252.     
  253.     def nextSiblingGenerator(self):
  254.         i = self
  255.         while i:
  256.             i = i.nextSibling
  257.             yield i
  258.  
  259.     
  260.     def previousGenerator(self):
  261.         i = self
  262.         while i:
  263.             i = i.previous
  264.             yield i
  265.  
  266.     
  267.     def previousSiblingGenerator(self):
  268.         i = self
  269.         while i:
  270.             i = i.previousSibling
  271.             yield i
  272.  
  273.     
  274.     def parentGenerator(self):
  275.         i = self
  276.         while i:
  277.             i = i.parent
  278.             yield i
  279.  
  280.     
  281.     def substituteEncoding(self, str, encoding = None):
  282.         if not encoding:
  283.             pass
  284.         encoding = 'utf-8'
  285.         return str.replace('%SOUP-ENCODING%', encoding)
  286.  
  287.     
  288.     def toEncoding(self, s, encoding = None):
  289.         if isinstance(s, unicode):
  290.             if encoding:
  291.                 s = s.encode(encoding)
  292.             
  293.         elif isinstance(s, str):
  294.             if encoding:
  295.                 s = s.encode(encoding)
  296.             else:
  297.                 s = unicode(s)
  298.         elif encoding:
  299.             s = self.toEncoding(str(s), encoding)
  300.         else:
  301.             s = unicode(s)
  302.         return s
  303.  
  304.  
  305.  
  306. class NavigableString(unicode, PageElement):
  307.     
  308.     def __new__(cls, value):
  309.         if isinstance(value, unicode):
  310.             return unicode.__new__(cls, value)
  311.         return unicode.__new__(cls, value, DEFAULT_OUTPUT_ENCODING)
  312.  
  313.     
  314.     def __getnewargs__(self):
  315.         return (unicode(self),)
  316.  
  317.     
  318.     def __getattr__(self, attr):
  319.         if attr == 'string':
  320.             return self
  321.         raise AttributeError, "'%s' object has no attribute '%s'" % (self.__class__.__name__, attr)
  322.  
  323.     
  324.     def encode(self, encoding = DEFAULT_OUTPUT_ENCODING):
  325.         return self.decode().encode(encoding)
  326.  
  327.     
  328.     def decodeGivenEventualEncoding(self, eventualEncoding):
  329.         return self
  330.  
  331.  
  332.  
  333. class CData(NavigableString):
  334.     
  335.     def decodeGivenEventualEncoding(self, eventualEncoding):
  336.         return u'<![CDATA[' + self + u']]>'
  337.  
  338.  
  339.  
  340. class ProcessingInstruction(NavigableString):
  341.     
  342.     def decodeGivenEventualEncoding(self, eventualEncoding):
  343.         output = self
  344.         if u'%SOUP-ENCODING%' in output:
  345.             output = self.substituteEncoding(output, eventualEncoding)
  346.         
  347.         return u'<?' + output + u'?>'
  348.  
  349.  
  350.  
  351. class Comment(NavigableString):
  352.     
  353.     def decodeGivenEventualEncoding(self, eventualEncoding):
  354.         return u'<!--' + self + u'-->'
  355.  
  356.  
  357.  
  358. class Declaration(NavigableString):
  359.     
  360.     def decodeGivenEventualEncoding(self, eventualEncoding):
  361.         return u'<!' + self + u'>'
  362.  
  363.  
  364.  
  365. class Tag(PageElement):
  366.     
  367.     def _invert(h):
  368.         i = { }
  369.         for k, v in h.items():
  370.             i[v] = k
  371.         
  372.         return i
  373.  
  374.     XML_ENTITIES_TO_SPECIAL_CHARS = {
  375.         'apos': "'",
  376.         'quot': '"',
  377.         'amp': '&',
  378.         'lt': '<',
  379.         'gt': '>' }
  380.     XML_SPECIAL_CHARS_TO_ENTITIES = _invert(XML_ENTITIES_TO_SPECIAL_CHARS)
  381.     
  382.     def _convertEntities(self, match):
  383.         x = match.group(1)
  384.         if self.convertHTMLEntities and x in name2codepoint:
  385.             return unichr(name2codepoint[x])
  386.         if x in self.XML_ENTITIES_TO_SPECIAL_CHARS:
  387.             if self.convertXMLEntities:
  388.                 return self.XML_ENTITIES_TO_SPECIAL_CHARS[x]
  389.             return u'&%s;' % x
  390.         x in self.XML_ENTITIES_TO_SPECIAL_CHARS
  391.         if len(x) > 0 and x[0] == '#':
  392.             if len(x) > 1 and x[1] == 'x':
  393.                 return unichr(int(x[2:], 16))
  394.             return unichr(int(x[1:]))
  395.         x[0] == '#'
  396.         if self.escapeUnrecognizedEntities:
  397.             return u'&%s;' % x
  398.         return u'&%s;' % x
  399.  
  400.     
  401.     def __init__(self, parser, name, attrs = None, parent = None, previous = None):
  402.         self.parserClass = parser.__class__
  403.         self.isSelfClosing = parser.isSelfClosingTag(name)
  404.         self.name = name
  405.         if attrs == None:
  406.             attrs = []
  407.         
  408.         self.attrs = attrs
  409.         self.contents = []
  410.         self.setup(parent, previous)
  411.         self.hidden = False
  412.         self.containsSubstitutions = False
  413.         self.convertHTMLEntities = parser.convertHTMLEntities
  414.         self.convertXMLEntities = parser.convertXMLEntities
  415.         self.escapeUnrecognizedEntities = parser.escapeUnrecognizedEntities
  416.         
  417.         def convert(kval):
  418.             (k, val) = kval
  419.             if val is None:
  420.                 return kval
  421.             return (k, re.sub('&(#\\d+|#x[0-9a-fA-F]+|\\w+);', self._convertEntities, val))
  422.  
  423.         self.attrs = map(convert, self.attrs)
  424.  
  425.     
  426.     def get(self, key, default = None):
  427.         return self._getAttrMap().get(key, default)
  428.  
  429.     
  430.     def has_key(self, key):
  431.         return self._getAttrMap().has_key(key)
  432.  
  433.     
  434.     def __getitem__(self, key):
  435.         return self._getAttrMap()[key]
  436.  
  437.     
  438.     def __iter__(self):
  439.         return iter(self.contents)
  440.  
  441.     
  442.     def __len__(self):
  443.         return len(self.contents)
  444.  
  445.     
  446.     def __contains__(self, x):
  447.         return x in self.contents
  448.  
  449.     
  450.     def __nonzero__(self):
  451.         return True
  452.  
  453.     
  454.     def __setitem__(self, key, value):
  455.         self._getAttrMap()
  456.         self.attrMap[key] = value
  457.         found = False
  458.         for i in range(0, len(self.attrs)):
  459.             if self.attrs[i][0] == key:
  460.                 self.attrs[i] = (key, value)
  461.                 found = True
  462.                 continue
  463.         
  464.         if not found:
  465.             self.attrs.append((key, value))
  466.         
  467.         self._getAttrMap()[key] = value
  468.  
  469.     
  470.     def __delitem__(self, key):
  471.         for item in self.attrs:
  472.             if item[0] == key:
  473.                 self.attrs.remove(item)
  474.             
  475.             self._getAttrMap()
  476.             if self.attrMap.has_key(key):
  477.                 del self.attrMap[key]
  478.                 continue
  479.         
  480.  
  481.     
  482.     def __call__(self, *args, **kwargs):
  483.         return apply(self.findAll, args, kwargs)
  484.  
  485.     
  486.     def __getattr__(self, tag):
  487.         if len(tag) > 3 and tag.rfind('Tag') == len(tag) - 3:
  488.             return self.find(tag[:-3])
  489.         if tag.find('__') != 0:
  490.             return self.find(tag)
  491.         raise AttributeError, "'%s' object has no attribute '%s'" % (self.__class__, tag)
  492.  
  493.     
  494.     def __eq__(self, other):
  495.         if not hasattr(other, 'name') and not hasattr(other, 'attrs') and not hasattr(other, 'contents') and self.name != other.name and self.attrs != other.attrs or len(self) != len(other):
  496.             return False
  497.         for i in range(0, len(self.contents)):
  498.             if self.contents[i] != other.contents[i]:
  499.                 return False
  500.         
  501.         return True
  502.  
  503.     
  504.     def __ne__(self, other):
  505.         return not (self == other)
  506.  
  507.     
  508.     def __repr__(self, encoding = DEFAULT_OUTPUT_ENCODING):
  509.         return self.decode(eventualEncoding = encoding)
  510.  
  511.     BARE_AMPERSAND_OR_BRACKET = re.compile('([<>]|' + '&(?!#\\d+;|#x[0-9a-fA-F]+;|\\w+;)' + ')')
  512.     
  513.     def _sub_entity(self, x):
  514.         return '&' + self.XML_SPECIAL_CHARS_TO_ENTITIES[x.group(0)[0]] + ';'
  515.  
  516.     
  517.     def __unicode__(self):
  518.         return self.decode()
  519.  
  520.     
  521.     def __str__(self):
  522.         return self.encode()
  523.  
  524.     
  525.     def encode(self, encoding = DEFAULT_OUTPUT_ENCODING, prettyPrint = False, indentLevel = 0):
  526.         return self.decode(prettyPrint, indentLevel, encoding).encode(encoding)
  527.  
  528.     
  529.     def decode(self, prettyPrint = False, indentLevel = 0, eventualEncoding = DEFAULT_OUTPUT_ENCODING):
  530.         attrs = []
  531.         if self.attrs:
  532.             for key, val in self.attrs:
  533.                 fmt = '%s="%s"'
  534.                 if isString(val):
  535.                     if self.containsSubstitutions and eventualEncoding is not None and '%SOUP-ENCODING%' in val:
  536.                         val = self.substituteEncoding(val, eventualEncoding)
  537.                     
  538.                     if '"' in val:
  539.                         fmt = "%s='%s'"
  540.                         if "'" in val:
  541.                             val = val.replace("'", '&squot;')
  542.                         
  543.                     
  544.                     val = self.BARE_AMPERSAND_OR_BRACKET.sub(self._sub_entity, val)
  545.                 
  546.                 if val is None:
  547.                     decoded = key
  548.                 else:
  549.                     decoded = fmt % (key, val)
  550.                 attrs.append(decoded)
  551.             
  552.         
  553.         close = ''
  554.         closeTag = ''
  555.         if self.isSelfClosing:
  556.             close = ' /'
  557.         else:
  558.             closeTag = '</%s>' % self.name
  559.         (indentTag, indentContents) = (0, 0)
  560.         if prettyPrint:
  561.             indentTag = indentLevel
  562.             space = ' ' * (indentTag - 1)
  563.             indentContents = indentTag + 1
  564.         
  565.         contents = self.decodeContents(prettyPrint, indentContents, eventualEncoding)
  566.         if self.hidden:
  567.             s = contents
  568.         else:
  569.             s = []
  570.             attributeString = ''
  571.             if attrs:
  572.                 attributeString = ' ' + ' '.join(attrs)
  573.             
  574.             if prettyPrint:
  575.                 s.append(space)
  576.             
  577.             s.append('<%s%s%s>' % (self.name, attributeString, close))
  578.             if prettyPrint:
  579.                 s.append('\n')
  580.             
  581.             s.append(contents)
  582.             if prettyPrint and contents and contents[-1] != '\n':
  583.                 s.append('\n')
  584.             
  585.             if prettyPrint and closeTag:
  586.                 s.append(space)
  587.             
  588.             s.append(closeTag)
  589.             if prettyPrint and closeTag and self.nextSibling:
  590.                 s.append('\n')
  591.             
  592.             s = ''.join(s)
  593.         return s
  594.  
  595.     
  596.     def decompose(self):
  597.         contents = [ i for i in self.contents ]
  598.         for i in contents:
  599.             if isinstance(i, Tag):
  600.                 i.decompose()
  601.                 continue
  602.             []
  603.             i.extract()
  604.         
  605.         self.extract()
  606.  
  607.     
  608.     def prettify(self, encoding = DEFAULT_OUTPUT_ENCODING):
  609.         return self.encode(encoding, True)
  610.  
  611.     
  612.     def encodeContents(self, encoding = DEFAULT_OUTPUT_ENCODING, prettyPrint = False, indentLevel = 0):
  613.         return self.decodeContents(prettyPrint, indentLevel).encode(encoding)
  614.  
  615.     
  616.     def decodeContents(self, prettyPrint = False, indentLevel = 0, eventualEncoding = DEFAULT_OUTPUT_ENCODING):
  617.         s = []
  618.         for c in self:
  619.             text = None
  620.             if isinstance(c, NavigableString):
  621.                 text = c.decodeGivenEventualEncoding(eventualEncoding)
  622.             elif isinstance(c, Tag):
  623.                 s.append(c.decode(prettyPrint, indentLevel, eventualEncoding))
  624.             
  625.             if text and prettyPrint:
  626.                 text = text.strip()
  627.             
  628.             if text:
  629.                 if prettyPrint:
  630.                     s.append(' ' * (indentLevel - 1))
  631.                 
  632.                 s.append(text)
  633.                 if prettyPrint:
  634.                     s.append('\n')
  635.                 
  636.             prettyPrint
  637.         
  638.         return ''.join(s)
  639.  
  640.     
  641.     def find(self, name = None, attrs = { }, recursive = True, text = None, **kwargs):
  642.         r = None
  643.         l = self.findAll(name, attrs, recursive, text, 1, **kwargs)
  644.         if l:
  645.             r = l[0]
  646.         
  647.         return r
  648.  
  649.     findChild = find
  650.     
  651.     def findAll(self, name = None, attrs = { }, recursive = True, text = None, limit = None, **kwargs):
  652.         generator = self.recursiveChildGenerator
  653.         if not recursive:
  654.             generator = self.childGenerator
  655.         
  656.         return self._findAll(name, attrs, text, limit, generator, **kwargs)
  657.  
  658.     findChildren = findAll
  659.     first = find
  660.     fetch = findAll
  661.     
  662.     def fetchText(self, text = None, recursive = True, limit = None):
  663.         return self.findAll(text = text, recursive = recursive, limit = limit)
  664.  
  665.     
  666.     def firstText(self, text = None, recursive = True):
  667.         return self.find(text = text, recursive = recursive)
  668.  
  669.     
  670.     def renderContents(self, encoding = DEFAULT_OUTPUT_ENCODING, prettyPrint = False, indentLevel = 0):
  671.         if encoding is None:
  672.             return self.decodeContents(prettyPrint, indentLevel, encoding)
  673.         return self.encodeContents(encoding, prettyPrint, indentLevel)
  674.  
  675.     
  676.     def _getAttrMap(self):
  677.         if not getattr(self, 'attrMap'):
  678.             self.attrMap = { }
  679.             for key, value in self.attrs:
  680.                 self.attrMap[key] = value
  681.             
  682.         
  683.         return self.attrMap
  684.  
  685.     
  686.     def recursiveChildGenerator(self):
  687.         if not len(self.contents):
  688.             raise StopIteration
  689.         len(self.contents)
  690.         stopNode = self._lastRecursiveChild().next
  691.         current = self.contents[0]
  692.         while current is not stopNode:
  693.             yield current
  694.             current = current.next
  695.  
  696.     
  697.     def childGenerator(self):
  698.         if not len(self.contents):
  699.             raise StopIteration
  700.         len(self.contents)
  701.         current = self.contents[0]
  702.         while current:
  703.             yield current
  704.             current = current.nextSibling
  705.         raise StopIteration
  706.  
  707.  
  708.  
  709. class SoupStrainer:
  710.     
  711.     def __init__(self, name = None, attrs = { }, text = None, **kwargs):
  712.         self.name = name
  713.         if isString(attrs):
  714.             kwargs['class'] = attrs
  715.             attrs = None
  716.         
  717.         if kwargs:
  718.             if attrs:
  719.                 attrs = attrs.copy()
  720.                 attrs.update(kwargs)
  721.             else:
  722.                 attrs = kwargs
  723.         
  724.         self.attrs = attrs
  725.         self.text = text
  726.  
  727.     
  728.     def __str__(self):
  729.         if self.text:
  730.             return self.text
  731.         return '%s|%s' % (self.name, self.attrs)
  732.  
  733.     
  734.     def searchTag(self, markupName = None, markupAttrs = { }):
  735.         found = None
  736.         markup = None
  737.         if isinstance(markupName, Tag):
  738.             markup = markupName
  739.             markupAttrs = markup
  740.         
  741.         if callable(self.name):
  742.             pass
  743.         callFunctionWithTagData = not isinstance(markupName, Tag)
  744.         if not not (self.name) and callFunctionWithTagData:
  745.             if (markup or self._matches(markup, self.name) or not markup) and self._matches(markupName, self.name):
  746.                 if callFunctionWithTagData:
  747.                     match = self.name(markupName, markupAttrs)
  748.                 else:
  749.                     match = True
  750.                     markupAttrMap = None
  751.                     for attr, matchAgainst in self.attrs.items():
  752.                         if not markupAttrMap:
  753.                             if hasattr(markupAttrs, 'get'):
  754.                                 markupAttrMap = markupAttrs
  755.                             else:
  756.                                 markupAttrMap = { }
  757.                                 for k, v in markupAttrs:
  758.                                     markupAttrMap[k] = v
  759.                                 
  760.                         
  761.                         attrValue = markupAttrMap.get(attr)
  762.                         if not self._matches(attrValue, matchAgainst):
  763.                             match = False
  764.                             break
  765.                             continue
  766.                     
  767.                 if match:
  768.                     if markup:
  769.                         found = markup
  770.                     else:
  771.                         found = markupName
  772.                 
  773.             
  774.         return found
  775.  
  776.     
  777.     def search(self, markup):
  778.         found = None
  779.         if isList(markup) and not isinstance(markup, Tag):
  780.             for element in markup:
  781.                 if isinstance(element, NavigableString) and self.search(element):
  782.                     found = element
  783.                     break
  784.                     continue
  785.             
  786.         elif isinstance(markup, Tag):
  787.             if not self.text:
  788.                 found = self.searchTag(markup)
  789.             
  790.         elif isinstance(markup, NavigableString) or isString(markup):
  791.             if self._matches(markup, self.text):
  792.                 found = markup
  793.             
  794.         else:
  795.             raise Exception, "I don't know how to match against a %s" % markup.__class__
  796.         return isString(markup)
  797.  
  798.     
  799.     def _matches(self, markup, matchAgainst):
  800.         result = False
  801.         if matchAgainst == True and type(matchAgainst) == types.BooleanType:
  802.             result = markup != None
  803.         elif callable(matchAgainst):
  804.             result = matchAgainst(markup)
  805.         elif isinstance(markup, Tag):
  806.             markup = markup.name
  807.         
  808.         if markup is not None and not isString(markup):
  809.             markup = unicode(markup)
  810.         
  811.         if hasattr(matchAgainst, 'match'):
  812.             if markup:
  813.                 pass
  814.             result = matchAgainst.search(markup)
  815.         elif isList(matchAgainst):
  816.             if markup is not None or not isString(matchAgainst):
  817.                 result = markup in matchAgainst
  818.             elif hasattr(matchAgainst, 'items'):
  819.                 result = markup.has_key(matchAgainst)
  820.             elif matchAgainst and isString(markup):
  821.                 if isinstance(markup, unicode):
  822.                     matchAgainst = unicode(matchAgainst)
  823.                 else:
  824.                     matchAgainst = str(matchAgainst)
  825.             
  826.         if not result:
  827.             result = matchAgainst == markup
  828.         
  829.         return result
  830.  
  831.  
  832.  
  833. class ResultSet(list):
  834.     
  835.     def __init__(self, source):
  836.         list.__init__([])
  837.         self.source = source
  838.  
  839.  
  840.  
  841. def isList(l):
  842.     if not hasattr(l, '__iter__') or not isString(l):
  843.         pass
  844.     return type(l) in (types.ListType, types.TupleType)
  845.  
  846.  
  847. def isString(s):
  848.     
  849.     try:
  850.         if not isinstance(s, unicode):
  851.             pass
  852.         return isinstance(s, basestring)
  853.     except NameError:
  854.         return isinstance(s, str)
  855.  
  856.  
  857.  
  858. def buildTagMap(default, *args):
  859.     built = { }
  860.     for portion in args:
  861.         if hasattr(portion, 'items'):
  862.             for k, v in portion.items():
  863.                 built[k] = v
  864.             
  865.         if isList(portion) and not isString(portion):
  866.             for k in portion:
  867.                 built[k] = default
  868.             
  869.         built[portion] = default
  870.     
  871.     return built
  872.  
  873.  
  874. class HTMLParserBuilder(HTMLParser):
  875.     
  876.     def __init__(self, soup):
  877.         HTMLParser.__init__(self)
  878.         self.soup = soup
  879.  
  880.     
  881.     def handle_starttag(self, name, attrs):
  882.         if name == 'meta':
  883.             self.soup.extractCharsetFromMeta(attrs)
  884.         else:
  885.             self.soup.unknown_starttag(name, attrs)
  886.  
  887.     
  888.     def handle_endtag(self, name):
  889.         self.soup.unknown_endtag(name)
  890.  
  891.     
  892.     def handle_data(self, content):
  893.         self.soup.handle_data(content)
  894.  
  895.     
  896.     def _toStringSubclass(self, text, subclass):
  897.         self.soup.endData()
  898.         self.handle_data(text)
  899.         self.soup.endData(subclass)
  900.  
  901.     
  902.     def handle_pi(self, text):
  903.         if text[:3] == 'xml':
  904.             text = u"xml version='1.0' encoding='%SOUP-ENCODING%'"
  905.         
  906.         self._toStringSubclass(text, ProcessingInstruction)
  907.  
  908.     
  909.     def handle_comment(self, text):
  910.         self._toStringSubclass(text, Comment)
  911.  
  912.     
  913.     def handle_charref(self, ref):
  914.         if self.soup.convertEntities:
  915.             data = unichr(int(ref))
  916.         else:
  917.             data = '&#%s;' % ref
  918.         self.handle_data(data)
  919.  
  920.     
  921.     def handle_entityref(self, ref):
  922.         data = None
  923.         if self.soup.convertHTMLEntities:
  924.             
  925.             try:
  926.                 data = unichr(name2codepoint[ref])
  927.             except KeyError:
  928.                 pass
  929.             except:
  930.                 None<EXCEPTION MATCH>KeyError
  931.             
  932.  
  933.         None<EXCEPTION MATCH>KeyError
  934.         if not data and self.soup.convertXMLEntities:
  935.             data = self.soup.XML_ENTITIES_TO_SPECIAL_CHARS.get(ref)
  936.         
  937.         if not data and self.soup.convertHTMLEntities and not self.soup.XML_ENTITIES_TO_SPECIAL_CHARS.get(ref):
  938.             data = '&%s' % ref
  939.         
  940.         if not data:
  941.             data = '&%s;' % ref
  942.         
  943.         self.handle_data(data)
  944.  
  945.     
  946.     def handle_decl(self, data):
  947.         self._toStringSubclass(data, Declaration)
  948.  
  949.     
  950.     def parse_declaration(self, i):
  951.         j = None
  952.         if self.rawdata[i:i + 9] == '<![CDATA[':
  953.             k = self.rawdata.find(']]>', i)
  954.             if k == -1:
  955.                 k = len(self.rawdata)
  956.             
  957.             data = self.rawdata[i + 9:k]
  958.             j = k + 3
  959.             self._toStringSubclass(data, CData)
  960.         else:
  961.             
  962.             try:
  963.                 j = HTMLParser.parse_declaration(self, i)
  964.             except HTMLParseError:
  965.                 toHandle = self.rawdata[i:]
  966.                 self.handle_data(toHandle)
  967.                 j = i + len(toHandle)
  968.  
  969.         return j
  970.  
  971.  
  972.  
  973. class BeautifulStoneSoup(Tag):
  974.     SELF_CLOSING_TAGS = { }
  975.     NESTABLE_TAGS = { }
  976.     RESET_NESTING_TAGS = { }
  977.     QUOTE_TAGS = { }
  978.     PRESERVE_WHITESPACE_TAGS = []
  979.     MARKUP_MASSAGE = [
  980.         (re.compile('(<[^<>]*)/>'), (lambda x: x.group(1) + ' />')),
  981.         (re.compile('<!\\s+([^<>]*)>'), (lambda x: '<!' + x.group(1) + '>'))]
  982.     ROOT_TAG_NAME = u'[document]'
  983.     HTML_ENTITIES = 'html'
  984.     XML_ENTITIES = 'xml'
  985.     XHTML_ENTITIES = 'xhtml'
  986.     ALL_ENTITIES = XHTML_ENTITIES
  987.     STRIP_ASCII_SPACES = {
  988.         9: None,
  989.         10: None,
  990.         12: None,
  991.         13: None,
  992.         32: None }
  993.     
  994.     def __init__(self, markup = '', parseOnlyThese = None, fromEncoding = None, markupMassage = True, smartQuotesTo = XML_ENTITIES, convertEntities = None, selfClosingTags = None, isHTML = False, builder = HTMLParserBuilder):
  995.         self.parseOnlyThese = parseOnlyThese
  996.         self.fromEncoding = fromEncoding
  997.         self.smartQuotesTo = smartQuotesTo
  998.         self.convertEntities = convertEntities
  999.         if self.convertEntities:
  1000.             self.smartQuotesTo = None
  1001.             if convertEntities == self.HTML_ENTITIES:
  1002.                 self.convertXMLEntities = False
  1003.                 self.convertHTMLEntities = True
  1004.                 self.escapeUnrecognizedEntities = True
  1005.             elif convertEntities == self.XHTML_ENTITIES:
  1006.                 self.convertXMLEntities = True
  1007.                 self.convertHTMLEntities = True
  1008.                 self.escapeUnrecognizedEntities = False
  1009.             elif convertEntities == self.XML_ENTITIES:
  1010.                 self.convertXMLEntities = True
  1011.                 self.convertHTMLEntities = False
  1012.                 self.escapeUnrecognizedEntities = False
  1013.             
  1014.         else:
  1015.             self.convertXMLEntities = False
  1016.             self.convertHTMLEntities = False
  1017.             self.escapeUnrecognizedEntities = False
  1018.         self.instanceSelfClosingTags = buildTagMap(None, selfClosingTags)
  1019.         self.builder = builder(self)
  1020.         self.reset()
  1021.         if hasattr(markup, 'read'):
  1022.             markup = markup.read()
  1023.         
  1024.         self.markup = markup
  1025.         self.markupMassage = markupMassage
  1026.         
  1027.         try:
  1028.             self._feed(isHTML = isHTML)
  1029.         except StopParsing:
  1030.             pass
  1031.  
  1032.         self.markup = None
  1033.         self.builder = None
  1034.  
  1035.     
  1036.     def _feed(self, inDocumentEncoding = None, isHTML = False):
  1037.         markup = self.markup
  1038.         if isinstance(markup, unicode):
  1039.             if not hasattr(self, 'originalEncoding'):
  1040.                 self.originalEncoding = None
  1041.             
  1042.         else:
  1043.             dammit = UnicodeDammit(markup, [
  1044.                 self.fromEncoding,
  1045.                 inDocumentEncoding], smartQuotesTo = self.smartQuotesTo, isHTML = isHTML)
  1046.             markup = dammit.unicode
  1047.             self.originalEncoding = dammit.originalEncoding
  1048.             self.declaredHTMLEncoding = dammit.declaredHTMLEncoding
  1049.         if markup:
  1050.             if self.markupMassage:
  1051.                 if not isList(self.markupMassage):
  1052.                     self.markupMassage = self.MARKUP_MASSAGE
  1053.                 
  1054.                 for fix, m in self.markupMassage:
  1055.                     markup = fix.sub(m, markup)
  1056.                 
  1057.                 del self.markupMassage
  1058.             
  1059.         
  1060.         self.builder.reset()
  1061.         self.builder.feed(markup)
  1062.         self.endData()
  1063.         while self.currentTag.name != self.ROOT_TAG_NAME:
  1064.             self.popTag()
  1065.  
  1066.     
  1067.     def isSelfClosingTag(self, name):
  1068.         if not self.SELF_CLOSING_TAGS.has_key(name):
  1069.             pass
  1070.         return self.instanceSelfClosingTags.has_key(name)
  1071.  
  1072.     
  1073.     def reset(self):
  1074.         Tag.__init__(self, self, self.ROOT_TAG_NAME)
  1075.         self.hidden = 1
  1076.         self.builder.reset()
  1077.         self.currentData = []
  1078.         self.currentTag = None
  1079.         self.tagStack = []
  1080.         self.quoteStack = []
  1081.         self.pushTag(self)
  1082.  
  1083.     
  1084.     def popTag(self):
  1085.         tag = self.tagStack.pop()
  1086.         if len(self.currentTag.contents) == 1 and isinstance(self.currentTag.contents[0], NavigableString):
  1087.             self.currentTag.string = self.currentTag.contents[0]
  1088.         
  1089.         if self.tagStack:
  1090.             self.currentTag = self.tagStack[-1]
  1091.         
  1092.         return self.currentTag
  1093.  
  1094.     
  1095.     def pushTag(self, tag):
  1096.         if self.currentTag:
  1097.             self.currentTag.contents.append(tag)
  1098.         
  1099.         self.tagStack.append(tag)
  1100.         self.currentTag = self.tagStack[-1]
  1101.  
  1102.     
  1103.     def endData(self, containerClass = NavigableString):
  1104.         if self.currentData:
  1105.             currentData = u''.join(self.currentData)
  1106.             self.currentData = []
  1107.             if self.parseOnlyThese and len(self.tagStack) <= 1:
  1108.                 if not (self.parseOnlyThese.text) or not self.parseOnlyThese.search(currentData):
  1109.                     return None
  1110.                 o = containerClass(currentData)
  1111.                 o.setup(self.currentTag, self.previous)
  1112.             self.previous = o
  1113.             self.currentTag.contents.append(o)
  1114.         
  1115.  
  1116.     
  1117.     def _popToTag(self, name, inclusivePop = True):
  1118.         if name == self.ROOT_TAG_NAME:
  1119.             return None
  1120.         numPops = 0
  1121.         mostRecentTag = None
  1122.         for i in range(len(self.tagStack) - 1, 0, -1):
  1123.             if name == self.tagStack[i].name:
  1124.                 numPops = len(self.tagStack) - i
  1125.                 break
  1126.                 continue
  1127.             name == self.ROOT_TAG_NAME
  1128.         
  1129.         if not inclusivePop:
  1130.             numPops = numPops - 1
  1131.         
  1132.         for i in range(0, numPops):
  1133.             mostRecentTag = self.popTag()
  1134.         
  1135.         return mostRecentTag
  1136.  
  1137.     
  1138.     def _smartPop(self, name):
  1139.         nestingResetTriggers = self.NESTABLE_TAGS.get(name)
  1140.         isNestable = nestingResetTriggers != None
  1141.         isResetNesting = self.RESET_NESTING_TAGS.has_key(name)
  1142.         popTo = None
  1143.         inclusive = True
  1144.         for i in range(len(self.tagStack) - 1, 0, -1):
  1145.             p = self.tagStack[i]
  1146.             if (not p or p.name == name) and not isNestable:
  1147.                 popTo = name
  1148.                 break
  1149.             
  1150.             if (nestingResetTriggers != None or p.name in nestingResetTriggers or nestingResetTriggers == None) and isResetNesting and self.RESET_NESTING_TAGS.has_key(p.name):
  1151.                 popTo = p.name
  1152.                 inclusive = False
  1153.                 break
  1154.             
  1155.             p = p.parent
  1156.         
  1157.         if popTo:
  1158.             self._popToTag(popTo, inclusive)
  1159.         
  1160.  
  1161.     
  1162.     def unknown_starttag(self, name, attrs, selfClosing = 0):
  1163.         if self.quoteStack:
  1164.             attrs = ''.join(map((lambda .0: (x, y) = .0' %s="%s"' % (x, y)), attrs))
  1165.             self.handle_data('<%s%s>' % (name, attrs))
  1166.             return None
  1167.         self.endData()
  1168.         if not self.isSelfClosingTag(name) and not selfClosing:
  1169.             self._smartPop(name)
  1170.         
  1171.         if self.parseOnlyThese and len(self.tagStack) <= 1:
  1172.             if self.parseOnlyThese.text or not self.parseOnlyThese.searchTag(name, attrs):
  1173.                 return None
  1174.             tag = Tag(self, name, attrs, self.currentTag, self.previous)
  1175.             if self.previous:
  1176.                 self.previous.next = tag
  1177.             
  1178.         self.previous = tag
  1179.         self.pushTag(tag)
  1180.         if selfClosing or self.isSelfClosingTag(name):
  1181.             self.popTag()
  1182.         
  1183.         if name in self.QUOTE_TAGS:
  1184.             self.quoteStack.append(name)
  1185.             self.literal = 1
  1186.         
  1187.         return tag
  1188.  
  1189.     
  1190.     def unknown_endtag(self, name):
  1191.         if self.quoteStack and self.quoteStack[-1] != name:
  1192.             self.handle_data('</%s>' % name)
  1193.             return None
  1194.         self.endData()
  1195.         self._popToTag(name)
  1196.         if self.quoteStack and self.quoteStack[-1] == name:
  1197.             self.quoteStack.pop()
  1198.             self.literal = len(self.quoteStack) > 0
  1199.         
  1200.  
  1201.     
  1202.     def handle_data(self, data):
  1203.         self.currentData.append(data)
  1204.  
  1205.     
  1206.     def extractCharsetFromMeta(self, attrs):
  1207.         self.unknown_starttag('meta', attrs)
  1208.  
  1209.  
  1210.  
  1211. class BeautifulSoup(BeautifulStoneSoup):
  1212.     
  1213.     def __init__(self, *args, **kwargs):
  1214.         if not kwargs.has_key('smartQuotesTo'):
  1215.             kwargs['smartQuotesTo'] = self.HTML_ENTITIES
  1216.         
  1217.         kwargs['isHTML'] = True
  1218.         BeautifulStoneSoup.__init__(self, *args, **kwargs)
  1219.  
  1220.     SELF_CLOSING_TAGS = buildTagMap(None, [
  1221.         'br',
  1222.         'hr',
  1223.         'input',
  1224.         'img',
  1225.         'meta',
  1226.         'spacer',
  1227.         'link',
  1228.         'frame',
  1229.         'base'])
  1230.     PRESERVE_WHITESPACE_TAGS = set([
  1231.         'pre',
  1232.         'textarea'])
  1233.     QUOTE_TAGS = {
  1234.         'script': None,
  1235.         'textarea': None }
  1236.     NESTABLE_INLINE_TAGS = [
  1237.         'span',
  1238.         'font',
  1239.         'q',
  1240.         'object',
  1241.         'bdo',
  1242.         'sub',
  1243.         'sup',
  1244.         'center']
  1245.     NESTABLE_BLOCK_TAGS = [
  1246.         'blockquote',
  1247.         'div',
  1248.         'fieldset',
  1249.         'ins',
  1250.         'del']
  1251.     NESTABLE_LIST_TAGS = {
  1252.         'ol': [],
  1253.         'ul': [],
  1254.         'li': [
  1255.             'ul',
  1256.             'ol'],
  1257.         'dl': [],
  1258.         'dd': [
  1259.             'dl'],
  1260.         'dt': [
  1261.             'dl'] }
  1262.     NESTABLE_TABLE_TAGS = {
  1263.         'table': [],
  1264.         'tr': [
  1265.             'table',
  1266.             'tbody',
  1267.             'tfoot',
  1268.             'thead'],
  1269.         'td': [
  1270.             'tr'],
  1271.         'th': [
  1272.             'tr'],
  1273.         'thead': [
  1274.             'table'],
  1275.         'tbody': [
  1276.             'table'],
  1277.         'tfoot': [
  1278.             'table'] }
  1279.     NON_NESTABLE_BLOCK_TAGS = [
  1280.         'address',
  1281.         'form',
  1282.         'p',
  1283.         'pre']
  1284.     RESET_NESTING_TAGS = buildTagMap(None, NESTABLE_BLOCK_TAGS, 'noscript', NON_NESTABLE_BLOCK_TAGS, NESTABLE_LIST_TAGS, NESTABLE_TABLE_TAGS)
  1285.     NESTABLE_TAGS = buildTagMap([], NESTABLE_INLINE_TAGS, NESTABLE_BLOCK_TAGS, NESTABLE_LIST_TAGS, NESTABLE_TABLE_TAGS)
  1286.     CHARSET_RE = re.compile('((^|;)\\s*charset=)([^;]*)', re.M)
  1287.     
  1288.     def extractCharsetFromMeta(self, attrs):
  1289.         httpEquiv = None
  1290.         contentType = None
  1291.         contentTypeIndex = None
  1292.         tagNeedsEncodingSubstitution = False
  1293.         for i in range(0, len(attrs)):
  1294.             (key, value) = attrs[i]
  1295.             key = key.lower()
  1296.             if key == 'http-equiv':
  1297.                 httpEquiv = value
  1298.                 continue
  1299.             if key == 'content':
  1300.                 contentType = value
  1301.                 contentTypeIndex = i
  1302.                 continue
  1303.         
  1304.         if httpEquiv and contentType:
  1305.             match = self.CHARSET_RE.search(contentType)
  1306.             if match:
  1307.                 if self.declaredHTMLEncoding is not None or self.originalEncoding == self.fromEncoding:
  1308.                     
  1309.                     def rewrite(match):
  1310.                         return match.group(1) + '%SOUP-ENCODING%'
  1311.  
  1312.                     newAttr = self.CHARSET_RE.sub(rewrite, contentType)
  1313.                     attrs[contentTypeIndex] = (attrs[contentTypeIndex][0], newAttr)
  1314.                     tagNeedsEncodingSubstitution = True
  1315.                 else:
  1316.                     newCharset = match.group(3)
  1317.                     if newCharset and newCharset != self.originalEncoding:
  1318.                         self.declaredHTMLEncoding = newCharset
  1319.                         self._feed(self.declaredHTMLEncoding)
  1320.                         raise StopParsing
  1321.                     newCharset != self.originalEncoding
  1322.             
  1323.         
  1324.         tag = self.unknown_starttag('meta', attrs)
  1325.         if tag and tagNeedsEncodingSubstitution:
  1326.             tag.containsSubstitutions = True
  1327.         
  1328.  
  1329.  
  1330.  
  1331. class StopParsing(Exception):
  1332.     pass
  1333.  
  1334.  
  1335. class ICantBelieveItsBeautifulSoup(BeautifulSoup):
  1336.     I_CANT_BELIEVE_THEYRE_NESTABLE_INLINE_TAGS = [
  1337.         'em',
  1338.         'big',
  1339.         'i',
  1340.         'small',
  1341.         'tt',
  1342.         'abbr',
  1343.         'acronym',
  1344.         'strong',
  1345.         'cite',
  1346.         'code',
  1347.         'dfn',
  1348.         'kbd',
  1349.         'samp',
  1350.         'strong',
  1351.         'var',
  1352.         'b',
  1353.         'big']
  1354.     I_CANT_BELIEVE_THEYRE_NESTABLE_BLOCK_TAGS = [
  1355.         'noscript']
  1356.     NESTABLE_TAGS = buildTagMap([], BeautifulSoup.NESTABLE_TAGS, I_CANT_BELIEVE_THEYRE_NESTABLE_BLOCK_TAGS, I_CANT_BELIEVE_THEYRE_NESTABLE_INLINE_TAGS)
  1357.  
  1358.  
  1359. class MinimalSoup(BeautifulSoup):
  1360.     RESET_NESTING_TAGS = buildTagMap('noscript')
  1361.     NESTABLE_TAGS = { }
  1362.  
  1363.  
  1364. class BeautifulSOAP(BeautifulStoneSoup):
  1365.     
  1366.     def popTag(self):
  1367.         if len(self.tagStack) > 1:
  1368.             tag = self.tagStack[-1]
  1369.             parent = self.tagStack[-2]
  1370.             parent._getAttrMap()
  1371.             if isinstance(tag, Tag) and len(tag.contents) == 1 and isinstance(tag.contents[0], NavigableString) and not parent.attrMap.has_key(tag.name):
  1372.                 parent[tag.name] = tag.contents[0]
  1373.             
  1374.         
  1375.         BeautifulStoneSoup.popTag(self)
  1376.  
  1377.  
  1378.  
  1379. class RobustXMLParser(BeautifulStoneSoup):
  1380.     pass
  1381.  
  1382.  
  1383. class RobustHTMLParser(BeautifulSoup):
  1384.     pass
  1385.  
  1386.  
  1387. class RobustWackAssHTMLParser(ICantBelieveItsBeautifulSoup):
  1388.     pass
  1389.  
  1390.  
  1391. class RobustInsanelyWackAssHTMLParser(MinimalSoup):
  1392.     pass
  1393.  
  1394.  
  1395. class SimplifyingSOAPParser(BeautifulSOAP):
  1396.     pass
  1397.  
  1398.  
  1399. try:
  1400.     import chardet
  1401. except ImportError:
  1402.     chardet = None
  1403.  
  1404.  
  1405. try:
  1406.     import cjkcodecs.aliases as cjkcodecs
  1407. except ImportError:
  1408.     pass
  1409.  
  1410.  
  1411. try:
  1412.     import iconv_codec
  1413. except ImportError:
  1414.     pass
  1415.  
  1416.  
  1417. class UnicodeDammit:
  1418.     CHARSET_ALIASES = {
  1419.         'macintosh': 'mac-roman',
  1420.         'x-sjis': 'shift-jis' }
  1421.     
  1422.     def __init__(self, markup, overrideEncodings = [], smartQuotesTo = 'xml', isHTML = False):
  1423.         self.declaredHTMLEncoding = None
  1424.         (self.markup, documentEncoding, sniffedEncoding) = self._detectEncoding(markup, isHTML)
  1425.         self.smartQuotesTo = smartQuotesTo
  1426.         self.triedEncodings = []
  1427.         if markup == '' or isinstance(markup, unicode):
  1428.             self.originalEncoding = None
  1429.             self.unicode = unicode(markup)
  1430.             return None
  1431.         u = None
  1432.         for proposedEncoding in overrideEncodings:
  1433.             u = self._convertFrom(proposedEncoding)
  1434.             if u:
  1435.                 break
  1436.                 continue
  1437.             isinstance(markup, unicode)
  1438.         
  1439.         if not u:
  1440.             for proposedEncoding in (documentEncoding, sniffedEncoding):
  1441.                 u = self._convertFrom(proposedEncoding)
  1442.                 if u:
  1443.                     break
  1444.                     continue
  1445.             
  1446.         
  1447.         if not u and chardet and not isinstance(self.markup, unicode):
  1448.             u = self._convertFrom(chardet.detect(self.markup)['encoding'])
  1449.         
  1450.         if not u:
  1451.             for proposed_encoding in ('utf-8', 'windows-1252'):
  1452.                 u = self._convertFrom(proposed_encoding)
  1453.                 if u:
  1454.                     break
  1455.                     continue
  1456.             
  1457.         
  1458.         self.unicode = u
  1459.         if not u:
  1460.             self.originalEncoding = None
  1461.         
  1462.  
  1463.     
  1464.     def _subMSChar(self, match):
  1465.         orig = match.group(1)
  1466.         sub = self.MS_CHARS.get(orig)
  1467.         if type(sub) == types.TupleType:
  1468.             if self.smartQuotesTo == 'xml':
  1469.                 sub = '&#x'.encode() + sub[1].encode() + ';'.encode()
  1470.             else:
  1471.                 sub = '&'.encode() + sub[0].encode() + ';'.encode()
  1472.         else:
  1473.             sub = sub.encode()
  1474.         return sub
  1475.  
  1476.     
  1477.     def _convertFrom(self, proposed):
  1478.         proposed = self.find_codec(proposed)
  1479.         if not proposed or proposed in self.triedEncodings:
  1480.             return None
  1481.         self.triedEncodings.append(proposed)
  1482.         markup = self.markup
  1483.         if self.smartQuotesTo and proposed.lower() in ('windows-1252', 'iso-8859-1', 'iso-8859-2'):
  1484.             smart_quotes_re = '([\x80-\x9f])'
  1485.             smart_quotes_compiled = re.compile(smart_quotes_re)
  1486.             markup = smart_quotes_compiled.sub(self._subMSChar, markup)
  1487.         
  1488.         
  1489.         try:
  1490.             u = self._toUnicode(markup, proposed)
  1491.             self.markup = u
  1492.             self.originalEncoding = proposed
  1493.         except Exception:
  1494.             e = None
  1495.             return None
  1496.  
  1497.         return self.markup
  1498.  
  1499.     
  1500.     def _toUnicode(self, data, encoding):
  1501.         if len(data) >= 4 and data[:2] == '\xfe\xff' and data[2:4] != '\x00\x00':
  1502.             encoding = 'utf-16be'
  1503.             data = data[2:]
  1504.         elif len(data) >= 4 and data[:2] == '\xff\xfe' and data[2:4] != '\x00\x00':
  1505.             encoding = 'utf-16le'
  1506.             data = data[2:]
  1507.         elif data[:3] == '\xef\xbb\xbf':
  1508.             encoding = 'utf-8'
  1509.             data = data[3:]
  1510.         elif data[:4] == '\x00\x00\xfe\xff':
  1511.             encoding = 'utf-32be'
  1512.             data = data[4:]
  1513.         elif data[:4] == '\xff\xfe\x00\x00':
  1514.             encoding = 'utf-32le'
  1515.             data = data[4:]
  1516.         
  1517.         newdata = unicode(data, encoding)
  1518.         return newdata
  1519.  
  1520.     
  1521.     def _detectEncoding(self, xml_data, isHTML = False):
  1522.         xml_encoding = None
  1523.         sniffed_xml_encoding = None
  1524.         
  1525.         try:
  1526.             if xml_data[:4] == 'Lo\xa7\x94':
  1527.                 xml_data = self._ebcdic_to_ascii(xml_data)
  1528.             elif xml_data[:4] == '\x00<\x00?':
  1529.                 sniffed_xml_encoding = 'utf-16be'
  1530.                 xml_data = unicode(xml_data, 'utf-16be').encode('utf-8')
  1531.             elif len(xml_data) >= 4 and xml_data[:2] == '\xfe\xff' and xml_data[2:4] != '\x00\x00':
  1532.                 sniffed_xml_encoding = 'utf-16be'
  1533.                 xml_data = unicode(xml_data[2:], 'utf-16be').encode('utf-8')
  1534.             elif xml_data[:4] == '<\x00?\x00':
  1535.                 sniffed_xml_encoding = 'utf-16le'
  1536.                 xml_data = unicode(xml_data, 'utf-16le').encode('utf-8')
  1537.             elif len(xml_data) >= 4 and xml_data[:2] == '\xff\xfe' and xml_data[2:4] != '\x00\x00':
  1538.                 sniffed_xml_encoding = 'utf-16le'
  1539.                 xml_data = unicode(xml_data[2:], 'utf-16le').encode('utf-8')
  1540.             elif xml_data[:4] == '\x00\x00\x00<':
  1541.                 sniffed_xml_encoding = 'utf-32be'
  1542.                 xml_data = unicode(xml_data, 'utf-32be').encode('utf-8')
  1543.             elif xml_data[:4] == '<\x00\x00\x00':
  1544.                 sniffed_xml_encoding = 'utf-32le'
  1545.                 xml_data = unicode(xml_data, 'utf-32le').encode('utf-8')
  1546.             elif xml_data[:4] == '\x00\x00\xfe\xff':
  1547.                 sniffed_xml_encoding = 'utf-32be'
  1548.                 xml_data = unicode(xml_data[4:], 'utf-32be').encode('utf-8')
  1549.             elif xml_data[:4] == '\xff\xfe\x00\x00':
  1550.                 sniffed_xml_encoding = 'utf-32le'
  1551.                 xml_data = unicode(xml_data[4:], 'utf-32le').encode('utf-8')
  1552.             elif xml_data[:3] == '\xef\xbb\xbf':
  1553.                 sniffed_xml_encoding = 'utf-8'
  1554.                 xml_data = unicode(xml_data[3:], 'utf-8').encode('utf-8')
  1555.             else:
  1556.                 sniffed_xml_encoding = 'ascii'
  1557.         except:
  1558.             xml_encoding_match = None
  1559.  
  1560.         xml_encoding_re = '^<\\?.*encoding=[\'"](.*?)[\'"].*\\?>'.encode()
  1561.         xml_encoding_match = re.compile(xml_encoding_re).match(xml_data)
  1562.         if not xml_encoding_match and isHTML:
  1563.             meta_re = '<\\s*meta[^>]+charset=([^>]*?)[;\'">]'.encode()
  1564.             regexp = re.compile(meta_re, re.I)
  1565.             xml_encoding_match = regexp.search(xml_data)
  1566.         
  1567.         if xml_encoding_match is not None:
  1568.             xml_encoding = xml_encoding_match.groups()[0].decode('ascii').lower()
  1569.             if isHTML:
  1570.                 self.declaredHTMLEncoding = xml_encoding
  1571.             
  1572.             if sniffed_xml_encoding and xml_encoding in ('iso-10646-ucs-2', 'ucs-2', 'csunicode', 'iso-10646-ucs-4', 'ucs-4', 'csucs4', 'utf-16', 'utf-32', 'utf_16', 'utf_32', 'utf16', 'u16'):
  1573.                 xml_encoding = sniffed_xml_encoding
  1574.             
  1575.         
  1576.         return (xml_data, xml_encoding, sniffed_xml_encoding)
  1577.  
  1578.     
  1579.     def find_codec(self, charset):
  1580.         if not self._codec(self.CHARSET_ALIASES.get(charset, charset)):
  1581.             if not charset or self._codec(charset.replace('-', '')):
  1582.                 if not charset or self._codec(charset.replace('-', '_')):
  1583.                     pass
  1584.         return charset
  1585.  
  1586.     
  1587.     def _codec(self, charset):
  1588.         if not charset:
  1589.             return charset
  1590.         codec = None
  1591.         
  1592.         try:
  1593.             codecs.lookup(charset)
  1594.             codec = charset
  1595.         except (LookupError, ValueError):
  1596.             charset
  1597.             charset
  1598.         except:
  1599.             charset
  1600.  
  1601.         return codec
  1602.  
  1603.     EBCDIC_TO_ASCII_MAP = None
  1604.     
  1605.     def _ebcdic_to_ascii(self, s):
  1606.         c = self.__class__
  1607.         if not c.EBCDIC_TO_ASCII_MAP:
  1608.             emap = (0, 1, 2, 3, 156, 9, 134, 127, 151, 141, 142, 11, 12, 13, 14, 15, 16, 17, 18, 19, 157, 133, 8, 135, 24, 25, 146, 143, 28, 29, 30, 31, 128, 129, 130, 131, 132, 10, 23, 27, 136, 137, 138, 139, 140, 5, 6, 7, 144, 145, 22, 147, 148, 149, 150, 4, 152, 153, 154, 155, 20, 21, 158, 26, 32, 160, 161, 162, 163, 164, 165, 166, 167, 168, 91, 46, 60, 40, 43, 33, 38, 169, 170, 171, 172, 173, 174, 175, 176, 177, 93, 36, 42, 41, 59, 94, 45, 47, 178, 179, 180, 181, 182, 183, 184, 185, 124, 44, 37, 95, 62, 63, 186, 187, 188, 189, 190, 191, 192, 193, 194, 96, 58, 35, 64, 39, 61, 34, 195, 97, 98, 99, 100, 101, 102, 103, 104, 105, 196, 197, 198, 199, 200, 201, 202, 106, 107, 108, 109, 110, 111, 112, 113, 114, 203, 204, 205, 206, 207, 208, 209, 126, 115, 116, 117, 118, 119, 120, 121, 122, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 123, 65, 66, 67, 68, 69, 70, 71, 72, 73, 232, 233, 234, 235, 236, 237, 125, 74, 75, 76, 77, 78, 79, 80, 81, 82, 238, 239, 240, 241, 242, 243, 92, 159, 83, 84, 85, 86, 87, 88, 89, 90, 244, 245, 246, 247, 248, 249, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 250, 251, 252, 253, 254, 255)
  1609.             import string
  1610.             c.EBCDIC_TO_ASCII_MAP = string.maketrans(''.join(map(chr, range(256))), ''.join(map(chr, emap)))
  1611.         
  1612.         return s.translate(c.EBCDIC_TO_ASCII_MAP)
  1613.  
  1614.     MS_CHARS = {
  1615.         '\x80': ('euro', '20AC'),
  1616.         '\x81': ' ',
  1617.         '\x82': ('sbquo', '201A'),
  1618.         '\x83': ('fnof', '192'),
  1619.         '\x84': ('bdquo', '201E'),
  1620.         '\x85': ('hellip', '2026'),
  1621.         '\x86': ('dagger', '2020'),
  1622.         '\x87': ('Dagger', '2021'),
  1623.         '\x88': ('circ', '2C6'),
  1624.         '\x89': ('permil', '2030'),
  1625.         '\x8a': ('Scaron', '160'),
  1626.         '\x8b': ('lsaquo', '2039'),
  1627.         '\x8c': ('OElig', '152'),
  1628.         '\x8d': '?',
  1629.         '\x8e': ('#x17D', '17D'),
  1630.         '\x8f': '?',
  1631.         '\x90': '?',
  1632.         '\x91': ('lsquo', '2018'),
  1633.         '\x92': ('rsquo', '2019'),
  1634.         '\x93': ('ldquo', '201C'),
  1635.         '\x94': ('rdquo', '201D'),
  1636.         '\x95': ('bull', '2022'),
  1637.         '\x96': ('ndash', '2013'),
  1638.         '\x97': ('mdash', '2014'),
  1639.         '\x98': ('tilde', '2DC'),
  1640.         '\x99': ('trade', '2122'),
  1641.         '\x9a': ('scaron', '161'),
  1642.         '\x9b': ('rsaquo', '203A'),
  1643.         '\x9c': ('oelig', '153'),
  1644.         '\x9d': '?',
  1645.         '\x9e': ('#x17E', '17E'),
  1646.         '\x9f': ('Yuml', '') }
  1647.  
  1648. if __name__ == '__main__':
  1649.     import sys
  1650.     soup = BeautifulSoup(sys.stdin)
  1651.     print soup.prettify()
  1652.  
  1653.