home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2011 October / maximum-cd-2011-10.iso / DiscContents / digsby_setup.exe / lib / lxml / html / __init__.pyo (.txt) next >
Encoding:
Python Compiled Bytecode  |  2011-06-22  |  37.2 KB  |  1,236 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyo (Python 2.6)
  3.  
  4. import threading
  5. import re
  6.  
  7. try:
  8.     from urlparse import urljoin
  9. except ImportError:
  10.     from urllib.parse import urljoin
  11.  
  12. import copy
  13. from lxml import etree
  14. from lxml.html import defs
  15. from lxml import cssselect
  16. from lxml.html._setmixin import SetMixin
  17.  
  18. try:
  19.     from UserDict import DictMixin
  20. except ImportError:
  21.     from lxml.html._dictmixin import DictMixin
  22.  
  23.  
  24. try:
  25.     set
  26. except NameError:
  27.     from sets import Set as set
  28.  
  29.  
  30. try:
  31.     bytes = __builtins__['bytes']
  32. except (KeyError, NameError):
  33.     bytes = str
  34.  
  35.  
  36. try:
  37.     unicode = __builtins__['unicode']
  38. except (KeyError, NameError):
  39.     unicode = str
  40.  
  41.  
  42. try:
  43.     basestring = __builtins__['basestring']
  44. except (KeyError, NameError):
  45.     basestring = (str, bytes)
  46.  
  47.  
  48. def __fix_docstring(s):
  49.     if not s:
  50.         return s
  51.     import sys
  52.     if s is None:
  53.         return None
  54.     return sub("\\1'", s)
  55.  
  56. __all__ = [
  57.     'document_fromstring',
  58.     'fragment_fromstring',
  59.     'fragments_fromstring',
  60.     'fromstring',
  61.     'tostring',
  62.     'Element',
  63.     'defs',
  64.     'open_in_browser',
  65.     'submit_form',
  66.     'find_rel_links',
  67.     'find_class',
  68.     'make_links_absolute',
  69.     'resolve_base_href',
  70.     'iterlinks',
  71.     'rewrite_links',
  72.     'open_in_browser',
  73.     'parse']
  74. XHTML_NAMESPACE = 'http://www.w3.org/1999/xhtml'
  75. _rel_links_xpath = etree.XPath('descendant-or-self::a[@rel]|descendant-or-self::x:a[@rel]', namespaces = {
  76.     'x': XHTML_NAMESPACE })
  77. _options_xpath = etree.XPath('descendant-or-self::option|descendant-or-self::x:option', namespaces = {
  78.     'x': XHTML_NAMESPACE })
  79. _forms_xpath = etree.XPath('descendant-or-self::form|descendant-or-self::x:form', namespaces = {
  80.     'x': XHTML_NAMESPACE })
  81. _class_xpath = etree.XPath("descendant-or-self::*[@class and contains(concat(' ', normalize-space(@class), ' '), concat(' ', $class_name, ' '))]")
  82. _id_xpath = etree.XPath('descendant-or-self::*[@id=$id]')
  83. _collect_string_content = etree.XPath('string()')
  84. _css_url_re = re.compile('url\\((' + '["][^"]*["]|' + "['][^']*[']|" + '[^)]*)\\)', re.I)
  85. _css_import_re = re.compile('@import "(.*?)"')
  86. _label_xpath = etree.XPath('//label[@for=$id]|//x:label[@for=$id]', namespaces = {
  87.     'x': XHTML_NAMESPACE })
  88. _archive_re = re.compile('[^ ]+')
  89.  
  90. def _unquote_match(s, pos):
  91.     if (s[:1] == '"' or s[-1:] == '"' or s[:1] == "'") and s[-1:] == "'":
  92.         return (s[1:-1], pos + 1)
  93.     return (s, pos)
  94.  
  95.  
  96. def _transform_result(typ, result):
  97.     if issubclass(typ, bytes):
  98.         return tostring(result, encoding = 'utf-8')
  99.     if issubclass(typ, unicode):
  100.         return tostring(result, encoding = unicode)
  101.     return result
  102.  
  103.  
  104. def _nons(tag):
  105.     if isinstance(tag, basestring):
  106.         if tag[0] == '{' and tag[1:len(XHTML_NAMESPACE) + 1] == XHTML_NAMESPACE:
  107.             return tag.split('}')[-1]
  108.     
  109.     return tag
  110.  
  111.  
  112. class HtmlMixin(object):
  113.     
  114.     def base_url(self):
  115.         return self.getroottree().docinfo.URL
  116.  
  117.     base_url = property(base_url, doc = base_url.__doc__)
  118.     
  119.     def forms(self):
  120.         return _forms_xpath(self)
  121.  
  122.     forms = property(forms, doc = forms.__doc__)
  123.     
  124.     def body(self):
  125.         return self.xpath('//body|//x:body', namespaces = {
  126.             'x': XHTML_NAMESPACE })[0]
  127.  
  128.     body = property(body, doc = body.__doc__)
  129.     
  130.     def head(self):
  131.         return self.xpath('//head|//x:head', namespaces = {
  132.             'x': XHTML_NAMESPACE })[0]
  133.  
  134.     head = property(head, doc = head.__doc__)
  135.     
  136.     def _label__get(self):
  137.         id = self.get('id')
  138.         if not id:
  139.             return None
  140.         result = _label_xpath(self, id = id)
  141.         if not result:
  142.             return None
  143.         return result[0]
  144.  
  145.     
  146.     def _label__set(self, label):
  147.         id = self.get('id')
  148.         if not id:
  149.             raise TypeError('You cannot set a label for an element (%r) that has no id' % self)
  150.         id
  151.         if _nons(label.tag) != 'label':
  152.             raise TypeError('You can only assign label to a label element (not %r)' % label)
  153.         _nons(label.tag) != 'label'
  154.         label.set('for', id)
  155.  
  156.     
  157.     def _label__del(self):
  158.         label = self.label
  159.         if label is not None:
  160.             del label.attrib['for']
  161.         
  162.  
  163.     label = property(_label__get, _label__set, _label__del, doc = _label__get.__doc__)
  164.     
  165.     def drop_tree(self):
  166.         parent = self.getparent()
  167.         if self.tail:
  168.             previous = self.getprevious()
  169.             if previous is None:
  170.                 if not parent.text:
  171.                     pass
  172.                 parent.text = '' + self.tail
  173.             elif not previous.tail:
  174.                 pass
  175.             previous.tail = '' + self.tail
  176.         
  177.         parent.remove(self)
  178.  
  179.     
  180.     def drop_tag(self):
  181.         parent = self.getparent()
  182.         previous = self.getprevious()
  183.         if self.text and isinstance(self.tag, basestring):
  184.             if previous is None:
  185.                 if not parent.text:
  186.                     pass
  187.                 parent.text = '' + self.text
  188.             elif not previous.tail:
  189.                 pass
  190.             previous.tail = '' + self.text
  191.         
  192.         if self.tail:
  193.             if len(self):
  194.                 last = self[-1]
  195.                 if not last.tail:
  196.                     pass
  197.                 last.tail = '' + self.tail
  198.             elif previous is None:
  199.                 if not parent.text:
  200.                     pass
  201.                 parent.text = '' + self.tail
  202.             elif not previous.tail:
  203.                 pass
  204.             previous.tail = '' + self.tail
  205.         
  206.         index = parent.index(self)
  207.         parent[index:index + 1] = self[:]
  208.  
  209.     
  210.     def find_rel_links(self, rel):
  211.         rel = rel.lower()
  212.         return _[1]
  213.  
  214.     
  215.     def find_class(self, class_name):
  216.         return _class_xpath(self, class_name = class_name)
  217.  
  218.     
  219.     def get_element_by_id(self, id, *default):
  220.         
  221.         try:
  222.             return _id_xpath(self, id = id)[0]
  223.         except IndexError:
  224.             if default:
  225.                 return default[0]
  226.             raise KeyError(id)
  227.         except:
  228.             default
  229.  
  230.  
  231.     
  232.     def text_content(self):
  233.         return _collect_string_content(self)
  234.  
  235.     
  236.     def cssselect(self, expr):
  237.         return cssselect.CSSSelector(expr)(self)
  238.  
  239.     
  240.     def make_links_absolute(self, base_url = None, resolve_base_href = True):
  241.         if base_url is None:
  242.             base_url = self.base_url
  243.             if base_url is None:
  244.                 raise TypeError('No base_url given, and the document has no base_url')
  245.             base_url is None
  246.         
  247.         if resolve_base_href:
  248.             self.resolve_base_href()
  249.         
  250.         
  251.         def link_repl(href):
  252.             return urljoin(base_url, href)
  253.  
  254.         self.rewrite_links(link_repl)
  255.  
  256.     
  257.     def resolve_base_href(self):
  258.         base_href = None
  259.         basetags = self.xpath('//base[@href]|//x:base[@href]', namespaces = {
  260.             'x': XHTML_NAMESPACE })
  261.         for b in basetags:
  262.             base_href = b.get('href')
  263.             b.drop_tree()
  264.         
  265.         if not base_href:
  266.             return None
  267.         self.make_links_absolute(base_href, resolve_base_href = False)
  268.  
  269.     
  270.     def iterlinks(self):
  271.         link_attrs = defs.link_attrs
  272.         for el in self.iter():
  273.             attribs = el.attrib
  274.             tag = _nons(el.tag)
  275.             if tag != 'object':
  276.                 for attrib in link_attrs:
  277.                     if attrib in attribs:
  278.                         yield (el, attrib, attribs[attrib], 0)
  279.                         continue
  280.                 
  281.             elif tag == 'object':
  282.                 codebase = None
  283.                 if 'codebase' in attribs:
  284.                     codebase = el.get('codebase')
  285.                     yield (el, 'codebase', codebase, 0)
  286.                 
  287.                 for attrib in ('classid', 'data'):
  288.                     if attrib in attribs:
  289.                         value = el.get(attrib)
  290.                         if codebase is not None:
  291.                             value = urljoin(codebase, value)
  292.                         
  293.                         yield (el, attrib, value, 0)
  294.                         continue
  295.                 
  296.                 if 'archive' in attribs:
  297.                     for match in _archive_re.finditer(el.get('archive')):
  298.                         value = match.group(0)
  299.                         if codebase is not None:
  300.                             value = urljoin(codebase, value)
  301.                         
  302.                         yield (el, 'archive', value, match.start())
  303.                     
  304.                 
  305.             
  306.             if tag == 'param':
  307.                 if not el.get('valuetype'):
  308.                     pass
  309.                 valuetype = ''
  310.                 if valuetype.lower() == 'ref':
  311.                     yield (el, 'value', el.get('value'), 0)
  312.                 
  313.             
  314.             if tag == 'style' and el.text:
  315.                 for match in _css_url_re.finditer(el.text):
  316.                     (url, start) = _unquote_match(match.group(1), match.start(1))
  317.                     yield (el, None, url, start)
  318.                 
  319.                 for match in _css_import_re.finditer(el.text):
  320.                     yield (el, None, match.group(1), match.start(1))
  321.                 
  322.             
  323.             if 'style' in attribs:
  324.                 for match in _css_url_re.finditer(attribs['style']):
  325.                     (url, start) = _unquote_match(match.group(1), match.start(1))
  326.                     yield (el, 'style', url, start)
  327.                 
  328.         
  329.  
  330.     
  331.     def rewrite_links(self, link_repl_func, resolve_base_href = True, base_href = None):
  332.         if base_href is not None:
  333.             self.make_links_absolute(base_href, resolve_base_href = resolve_base_href)
  334.         elif resolve_base_href:
  335.             self.resolve_base_href()
  336.         
  337.         for el, attrib, link, pos in self.iterlinks():
  338.             new_link = link_repl_func(link.strip())
  339.             if new_link == link:
  340.                 continue
  341.             
  342.             if new_link is None:
  343.                 if attrib is None:
  344.                     el.text = ''
  345.                     continue
  346.                 del el.attrib[attrib]
  347.                 continue
  348.             
  349.             if attrib is None:
  350.                 new = el.text[:pos] + new_link + el.text[pos + len(link):]
  351.                 el.text = new
  352.                 continue
  353.             cur = el.attrib[attrib]
  354.             if not pos and len(cur) == len(link):
  355.                 el.attrib[attrib] = new_link
  356.                 continue
  357.             new = cur[:pos] + new_link + cur[pos + len(link):]
  358.             el.attrib[attrib] = new
  359.         
  360.  
  361.  
  362.  
  363. class _MethodFunc(object):
  364.     
  365.     def __init__(self, name, copy = False, source_class = HtmlMixin):
  366.         self.name = name
  367.         self.copy = copy
  368.         self.__doc__ = getattr(source_class, self.name).__doc__
  369.  
  370.     
  371.     def __call__(self, doc, *args, **kw):
  372.         result_type = type(doc)
  373.         if isinstance(doc, basestring):
  374.             if 'copy' in kw:
  375.                 raise TypeError("The keyword 'copy' can only be used with element inputs to %s, not a string input" % self.name)
  376.             'copy' in kw
  377.             doc = fromstring(doc, **kw)
  378.         elif 'copy' in kw:
  379.             copy = kw.pop('copy')
  380.         else:
  381.             copy = self.copy
  382.         if copy:
  383.             doc = copy.deepcopy(doc)
  384.         
  385.         meth = getattr(doc, self.name)
  386.         result = meth(*args, **kw)
  387.         if result is None:
  388.             return _transform_result(result_type, doc)
  389.         return result
  390.  
  391.  
  392. find_rel_links = _MethodFunc('find_rel_links', copy = False)
  393. find_class = _MethodFunc('find_class', copy = False)
  394. make_links_absolute = _MethodFunc('make_links_absolute', copy = True)
  395. resolve_base_href = _MethodFunc('resolve_base_href', copy = True)
  396. iterlinks = _MethodFunc('iterlinks', copy = False)
  397. rewrite_links = _MethodFunc('rewrite_links', copy = True)
  398.  
  399. class HtmlComment(etree.CommentBase, HtmlMixin):
  400.     pass
  401.  
  402.  
  403. class HtmlElement(etree.ElementBase, HtmlMixin):
  404.     pass
  405.  
  406.  
  407. class HtmlProcessingInstruction(etree.PIBase, HtmlMixin):
  408.     pass
  409.  
  410.  
  411. class HtmlEntity(etree.EntityBase, HtmlMixin):
  412.     pass
  413.  
  414.  
  415. class HtmlElementClassLookup(etree.CustomElementClassLookup):
  416.     _default_element_classes = { }
  417.     
  418.     def __init__(self, classes = None, mixins = None):
  419.         etree.CustomElementClassLookup.__init__(self)
  420.         if classes is None:
  421.             classes = self._default_element_classes.copy()
  422.         
  423.         if mixins:
  424.             mixers = { }
  425.             for name, value in mixins:
  426.                 if name == '*':
  427.                     for n in classes.keys():
  428.                         mixers.setdefault(n, []).append(value)
  429.                     
  430.                 mixers.setdefault(name, []).append(value)
  431.             
  432.             for name, mix_bases in mixers.items():
  433.                 cur = classes.get(name, HtmlElement)
  434.                 bases = tuple(mix_bases + [
  435.                     cur])
  436.                 classes[name] = type(cur.__name__, bases, { })
  437.             
  438.         
  439.         self._element_classes = classes
  440.  
  441.     
  442.     def lookup(self, node_type, document, namespace, name):
  443.         if node_type == 'element':
  444.             return self._element_classes.get(name.lower(), HtmlElement)
  445.         if node_type == 'comment':
  446.             return HtmlComment
  447.         if node_type == 'PI':
  448.             return HtmlProcessingInstruction
  449.         if node_type == 'entity':
  450.             return HtmlEntity
  451.  
  452.  
  453.  
  454. def document_fromstring(html, parser = None, **kw):
  455.     if parser is None:
  456.         parser = html_parser
  457.     
  458.     value = etree.fromstring(html, parser, **kw)
  459.     if value is None:
  460.         raise etree.ParserError('Document is empty')
  461.     value is None
  462.     return value
  463.  
  464.  
  465. def fragments_fromstring(html, no_leading_text = False, base_url = None, parser = None, **kw):
  466.     if parser is None:
  467.         parser = html_parser
  468.     
  469.     start = html[:20].lstrip().lower()
  470.     if not start.startswith('<html') and not start.startswith('<!doctype'):
  471.         html = '<html><body>%s</body></html>' % html
  472.     
  473.     doc = document_fromstring(html, parser = parser, base_url = base_url, **kw)
  474.     bodies = _[1]
  475.     body = bodies[0]
  476.     elements = []
  477.     if no_leading_text and body.text and body.text.strip():
  478.         raise etree.ParserError('There is leading text: %r' % body.text)
  479.     body.text.strip()
  480.     elements.extend(body)
  481.     return elements
  482.  
  483.  
  484. def fragment_fromstring(html, create_parent = False, base_url = None, parser = None, **kw):
  485.     if parser is None:
  486.         parser = html_parser
  487.     
  488.     if create_parent:
  489.         if not isinstance(create_parent, basestring):
  490.             create_parent = 'div'
  491.         
  492.         return fragment_fromstring('<%s>%s</%s>' % (create_parent, html, create_parent), parser = parser, base_url = base_url, **kw)
  493.     elements = fragments_fromstring(html, parser = parser, no_leading_text = True, base_url = base_url, **kw)
  494.     if not elements:
  495.         raise etree.ParserError('No elements found')
  496.     elements
  497.     if len(elements) > 1:
  498.         raise ', '.join([] % []([ _element_name(e) for e in elements ]))
  499.     len(elements) > 1
  500.     el = elements[0]
  501.     if el.tail and el.tail.strip():
  502.         raise etree.ParserError('Element followed by text: %r' % el.tail)
  503.     el.tail.strip()
  504.     el.tail = None
  505.     return el
  506.  
  507.  
  508. def fromstring(html, base_url = None, parser = None, **kw):
  509.     if parser is None:
  510.         parser = html_parser
  511.     
  512.     start = html[:10].lstrip().lower()
  513.     if start.startswith('<html') or start.startswith('<!doctype'):
  514.         return document_fromstring(html, parser = parser, base_url = base_url, **kw)
  515.     doc = document_fromstring(html, parser = parser, base_url = base_url, **kw)
  516.     bodies = doc.findall('body')
  517.     if not bodies:
  518.         bodies = doc.findall('{%s}body' % XHTML_NAMESPACE)
  519.     
  520.     if bodies:
  521.         body = bodies[0]
  522.         if len(bodies) > 1:
  523.             for other_body in bodies[1:]:
  524.                 if other_body.text:
  525.                     if len(body):
  526.                         if not body[-1].tail:
  527.                             pass
  528.                         body[-1].tail = '' + other_body.text
  529.                     elif not body.text:
  530.                         pass
  531.                     body.text = '' + other_body.text
  532.                 
  533.                 body.extend(other_body)
  534.                 other_body.drop_tree()
  535.             
  536.         
  537.     else:
  538.         body = None
  539.     heads = doc.findall('head')
  540.     if not heads:
  541.         heads = doc.findall('{%s}head' % XHTML_NAMESPACE)
  542.     
  543.     if heads:
  544.         head = heads[0]
  545.         if len(heads) > 1:
  546.             for other_head in heads[1:]:
  547.                 head.extend(other_head)
  548.                 other_head.drop_tree()
  549.             
  550.         
  551.         return doc
  552.     if len(body) == 1:
  553.         if not (body.text) or not body.text.strip():
  554.             if not (body[-1].tail) or not body[-1].tail.strip():
  555.                 return body[0]
  556.     return body
  557.  
  558.  
  559. def parse(filename_or_url, parser = None, base_url = None, **kw):
  560.     if parser is None:
  561.         parser = html_parser
  562.     
  563.     return etree.parse(filename_or_url, parser, base_url = base_url, **kw)
  564.  
  565.  
  566. def _contains_block_level_tag(el):
  567.     for el in el.iter():
  568.         if _nons(el.tag) in defs.block_tags:
  569.             return True
  570.     
  571.     return False
  572.  
  573.  
  574. def _element_name(el):
  575.     if isinstance(el, etree.CommentBase):
  576.         return 'comment'
  577.     if isinstance(el, basestring):
  578.         return 'string'
  579.     return _nons(el.tag)
  580.  
  581.  
  582. class FormElement(HtmlElement):
  583.     
  584.     def inputs(self):
  585.         return InputGetter(self)
  586.  
  587.     inputs = property(inputs, doc = inputs.__doc__)
  588.     
  589.     def _fields__get(self):
  590.         return FieldsDict(self.inputs)
  591.  
  592.     
  593.     def _fields__set(self, value):
  594.         prev_keys = self.fields.keys()
  595.         for key, value in value.iteritems():
  596.             if key in prev_keys:
  597.                 prev_keys.remove(key)
  598.             
  599.             self.fields[key] = value
  600.         
  601.         for key in prev_keys:
  602.             if key is None:
  603.                 continue
  604.             
  605.             self.fields[key] = None
  606.         
  607.  
  608.     fields = property(_fields__get, _fields__set, doc = _fields__get.__doc__)
  609.     
  610.     def _name(self):
  611.         if self.get('name'):
  612.             return self.get('name')
  613.         if self.get('id'):
  614.             return '#' + self.get('id')
  615.         forms = list(self.body.iter('form'))
  616.         return str(forms.index(self))
  617.  
  618.     
  619.     def form_values(self):
  620.         results = []
  621.         for el in self.inputs:
  622.             name = el.name
  623.             if not name:
  624.                 continue
  625.             
  626.             tag = _nons(el.tag)
  627.             if tag == 'textarea':
  628.                 results.append((name, el.value))
  629.                 continue
  630.             if tag == 'select':
  631.                 value = el.value
  632.                 if el.multiple:
  633.                     for v in value:
  634.                         results.append((name, v))
  635.                     
  636.                 elif value is not None:
  637.                     results.append((name, el.value))
  638.                 
  639.             el.multiple
  640.             if el.checkable and not (el.checked):
  641.                 continue
  642.             
  643.             if el.type in ('submit', 'image', 'reset'):
  644.                 continue
  645.             
  646.             value = el.value
  647.             if value is not None:
  648.                 results.append((name, el.value))
  649.                 continue
  650.         
  651.         return results
  652.  
  653.     
  654.     def _action__get(self):
  655.         base_url = self.base_url
  656.         action = self.get('action')
  657.         if base_url and action is not None:
  658.             return urljoin(base_url, action)
  659.         return action
  660.  
  661.     
  662.     def _action__set(self, value):
  663.         self.set('action', value)
  664.  
  665.     
  666.     def _action__del(self):
  667.         if 'action' in self.attrib:
  668.             del self.attrib['action']
  669.         
  670.  
  671.     action = property(_action__get, _action__set, _action__del, doc = _action__get.__doc__)
  672.     
  673.     def _method__get(self):
  674.         return self.get('method', 'GET').upper()
  675.  
  676.     
  677.     def _method__set(self, value):
  678.         self.set('method', value.upper())
  679.  
  680.     method = property(_method__get, _method__set, doc = _method__get.__doc__)
  681.  
  682. HtmlElementClassLookup._default_element_classes['form'] = FormElement
  683.  
  684. def submit_form(form, extra_values = None, open_http = None):
  685.     values = form.form_values()
  686.     if extra_values:
  687.         if hasattr(extra_values, 'items'):
  688.             extra_values = extra_values.items()
  689.         
  690.         values.extend(extra_values)
  691.     
  692.     if open_http is None:
  693.         open_http = open_http_urllib
  694.     
  695.     return open_http(form.method, form.action, values)
  696.  
  697.  
  698. def open_http_urllib(method, url, values):
  699.     import urllib
  700.     if method == 'GET':
  701.         if '?' in url:
  702.             url += '&'
  703.         else:
  704.             url += '?'
  705.         url += urllib.urlencode(values)
  706.         data = None
  707.     else:
  708.         data = urllib.urlencode(values)
  709.     return urllib.urlopen(url, data)
  710.  
  711.  
  712. class FieldsDict(DictMixin):
  713.     
  714.     def __init__(self, inputs):
  715.         self.inputs = inputs
  716.  
  717.     
  718.     def __getitem__(self, item):
  719.         return self.inputs[item].value
  720.  
  721.     
  722.     def __setitem__(self, item, value):
  723.         self.inputs[item].value = value
  724.  
  725.     
  726.     def __delitem__(self, item):
  727.         raise KeyError('You cannot remove keys from ElementDict')
  728.  
  729.     
  730.     def keys(self):
  731.         return self.inputs.keys()
  732.  
  733.     
  734.     def __contains__(self, item):
  735.         return item in self.inputs
  736.  
  737.     
  738.     def __repr__(self):
  739.         return '<%s for form %s>' % (self.__class__.__name__, self.inputs.form._name())
  740.  
  741.  
  742.  
  743. class InputGetter(object):
  744.     _name_xpath = etree.XPath(".//*[@name = $name and (local-name(.) = 'select' or local-name(.) = 'input' or local-name(.) = 'textarea')]")
  745.     _all_xpath = etree.XPath(".//*[local-name() = 'select' or local-name() = 'input' or local-name() = 'textarea']")
  746.     
  747.     def __init__(self, form):
  748.         self.form = form
  749.  
  750.     
  751.     def __repr__(self):
  752.         return '<%s for form %s>' % (self.__class__.__name__, self.form._name())
  753.  
  754.     
  755.     def __getitem__(self, name):
  756.         results = self._name_xpath(self.form, name = name)
  757.         if results:
  758.             type = results[0].get('type')
  759.             if type == 'radio' and len(results) > 1:
  760.                 group = RadioGroup(results)
  761.                 group.name = name
  762.                 return group
  763.             if type == 'checkbox' and len(results) > 1:
  764.                 group = CheckboxGroup(results)
  765.                 group.name = name
  766.                 return group
  767.             return results[0]
  768.         results
  769.         raise KeyError('No input element with the name %r' % name)
  770.  
  771.     
  772.     def __contains__(self, name):
  773.         results = self._name_xpath(self.form, name = name)
  774.         return bool(results)
  775.  
  776.     
  777.     def keys(self):
  778.         names = set()
  779.         for el in self:
  780.             names.add(el.name)
  781.         
  782.         if None in names:
  783.             names.remove(None)
  784.         
  785.         return list(names)
  786.  
  787.     
  788.     def __iter__(self):
  789.         return iter(self._all_xpath(self.form))
  790.  
  791.  
  792.  
  793. class InputMixin(object):
  794.     
  795.     def _name__get(self):
  796.         return self.get('name')
  797.  
  798.     
  799.     def _name__set(self, value):
  800.         self.set('name', value)
  801.  
  802.     
  803.     def _name__del(self):
  804.         if 'name' in self.attrib:
  805.             del self.attrib['name']
  806.         
  807.  
  808.     name = property(_name__get, _name__set, _name__del, doc = _name__get.__doc__)
  809.     
  810.     def __repr__(self):
  811.         type = getattr(self, 'type', None)
  812.         if type:
  813.             type = ' type=%r' % type
  814.         else:
  815.             type = ''
  816.         return '<%s %x name=%r%s>' % (self.__class__.__name__, id(self), self.name, type)
  817.  
  818.  
  819.  
  820. class TextareaElement(InputMixin, HtmlElement):
  821.     
  822.     def _value__get(self):
  823.         if not self.text:
  824.             pass
  825.         return ''
  826.  
  827.     
  828.     def _value__set(self, value):
  829.         self.text = value
  830.  
  831.     
  832.     def _value__del(self):
  833.         self.text = ''
  834.  
  835.     value = property(_value__get, _value__set, _value__del, doc = _value__get.__doc__)
  836.  
  837. HtmlElementClassLookup._default_element_classes['textarea'] = TextareaElement
  838.  
  839. class SelectElement(InputMixin, HtmlElement):
  840.     
  841.     def _value__get(self):
  842.         if self.multiple:
  843.             return MultipleSelectOptions(self)
  844.         for el in _options_xpath(self):
  845.             if 'selected' in el.attrib:
  846.                 value = el.get('value')
  847.                 return value
  848.         
  849.  
  850.     
  851.     def _value__set(self, value):
  852.         if self.multiple:
  853.             if isinstance(value, basestring):
  854.                 raise TypeError('You must pass in a sequence')
  855.             isinstance(value, basestring)
  856.             self.value.clear()
  857.             self.value.update(value)
  858.             return None
  859.         if value is not None:
  860.             for el in _options_xpath(self):
  861.                 if el.get('value') == value:
  862.                     checked_option = el
  863.                     break
  864.                     continue
  865.                 self.multiple
  866.             else:
  867.                 raise ValueError('There is no option with the value of %r' % value)
  868.         for el in _options_xpath(self):
  869.             if 'selected' in el.attrib:
  870.                 del el.attrib['selected']
  871.                 continue
  872.         
  873.         if value is not None:
  874.             checked_option.set('selected', '')
  875.         
  876.  
  877.     
  878.     def _value__del(self):
  879.         if self.multiple:
  880.             self.value.clear()
  881.         else:
  882.             self.value = None
  883.  
  884.     value = property(_value__get, _value__set, _value__del, doc = _value__get.__doc__)
  885.     
  886.     def value_options(self):
  887.         return [ el.get('value') for el in _options_xpath(self) ]
  888.  
  889.     value_options = property(value_options, doc = value_options.__doc__)
  890.     
  891.     def _multiple__get(self):
  892.         return 'multiple' in self.attrib
  893.  
  894.     
  895.     def _multiple__set(self, value):
  896.         if value:
  897.             self.set('multiple', '')
  898.         elif 'multiple' in self.attrib:
  899.             del self.attrib['multiple']
  900.         
  901.  
  902.     multiple = property(_multiple__get, _multiple__set, doc = _multiple__get.__doc__)
  903.  
  904. HtmlElementClassLookup._default_element_classes['select'] = SelectElement
  905.  
  906. class MultipleSelectOptions(SetMixin):
  907.     
  908.     def __init__(self, select):
  909.         self.select = select
  910.  
  911.     
  912.     def options(self):
  913.         return iter(_options_xpath(self.select))
  914.  
  915.     options = property(options)
  916.     
  917.     def __iter__(self):
  918.         for option in self.options:
  919.             yield option.get('value')
  920.         
  921.  
  922.     
  923.     def add(self, item):
  924.         for option in self.options:
  925.             if option.get('value') == item:
  926.                 option.set('selected', '')
  927.                 break
  928.                 continue
  929.         else:
  930.             raise ValueError('There is no option with the value %r' % item)
  931.  
  932.     
  933.     def remove(self, item):
  934.         for option in self.options:
  935.             if option.get('value') == item:
  936.                 if 'selected' in option.attrib:
  937.                     del option.attrib['selected']
  938.                 else:
  939.                     raise ValueError('The option %r is not currently selected' % item)
  940.                 continue
  941.             'selected' in option.attrib
  942.         else:
  943.             raise ValueError('There is not option with the value %r' % item)
  944.  
  945.     
  946.     def __repr__(self):
  947.         return ', '.join % ([], []([ repr(v) for v in self ]), self.select.name)
  948.  
  949.  
  950.  
  951. class RadioGroup(list):
  952.     
  953.     def _value__get(self):
  954.         for el in self:
  955.             if 'checked' in el.attrib:
  956.                 return el.get('value')
  957.         
  958.  
  959.     
  960.     def _value__set(self, value):
  961.         if value is not None:
  962.             for el in self:
  963.                 if el.get('value') == value:
  964.                     checked_option = el
  965.                     break
  966.                     continue
  967.             else:
  968.                 raise ValueError('There is no radio input with the value %r' % value)
  969.         for el in self:
  970.             if 'checked' in el.attrib:
  971.                 del el.attrib['checked']
  972.                 continue
  973.         
  974.         if value is not None:
  975.             checked_option.set('checked', '')
  976.         
  977.  
  978.     
  979.     def _value__del(self):
  980.         self.value = None
  981.  
  982.     value = property(_value__get, _value__set, _value__del, doc = _value__get.__doc__)
  983.     
  984.     def value_options(self):
  985.         return [ el.get('value') for el in self ]
  986.  
  987.     value_options = property(value_options, doc = value_options.__doc__)
  988.     
  989.     def __repr__(self):
  990.         return '%s(%s)' % (self.__class__.__name__, list.__repr__(self))
  991.  
  992.  
  993.  
  994. class CheckboxGroup(list):
  995.     
  996.     def _value__get(self):
  997.         return CheckboxValues(self)
  998.  
  999.     
  1000.     def _value__set(self, value):
  1001.         self.value.clear()
  1002.         if not hasattr(value, '__iter__'):
  1003.             raise ValueError('A CheckboxGroup (name=%r) must be set to a sequence (not %r)' % (self[0].name, value))
  1004.         hasattr(value, '__iter__')
  1005.         self.value.update(value)
  1006.  
  1007.     
  1008.     def _value__del(self):
  1009.         self.value.clear()
  1010.  
  1011.     value = property(_value__get, _value__set, _value__del, doc = _value__get.__doc__)
  1012.     
  1013.     def __repr__(self):
  1014.         return '%s(%s)' % (self.__class__.__name__, list.__repr__(self))
  1015.  
  1016.  
  1017.  
  1018. class CheckboxValues(SetMixin):
  1019.     
  1020.     def __init__(self, group):
  1021.         self.group = group
  1022.  
  1023.     
  1024.     def __iter__(self):
  1025.         return [](_[1])
  1026.  
  1027.     
  1028.     def add(self, value):
  1029.         for el in self.group:
  1030.             if el.get('value') == value:
  1031.                 el.set('checked', '')
  1032.                 break
  1033.                 continue
  1034.         else:
  1035.             raise KeyError('No checkbox with value %r' % value)
  1036.  
  1037.     
  1038.     def remove(self, value):
  1039.         for el in self.group:
  1040.             if el.get('value') == value:
  1041.                 if 'checked' in el.attrib:
  1042.                     del el.attrib['checked']
  1043.                 else:
  1044.                     raise KeyError('The checkbox with value %r was already unchecked' % value)
  1045.                 continue
  1046.             'checked' in el.attrib
  1047.         else:
  1048.             raise KeyError('No checkbox with value %r' % value)
  1049.  
  1050.     
  1051.     def __repr__(self):
  1052.         return ', '.join % ([], []([ repr(v) for v in self ]), self.group.name)
  1053.  
  1054.  
  1055.  
  1056. class InputElement(InputMixin, HtmlElement):
  1057.     
  1058.     def _value__get(self):
  1059.         if self.checkable:
  1060.             if self.checked:
  1061.                 if not self.get('value'):
  1062.                     pass
  1063.                 return 'on'
  1064.             return None
  1065.         self.checkable
  1066.         return self.get('value')
  1067.  
  1068.     
  1069.     def _value__set(self, value):
  1070.         if self.checkable:
  1071.             if not value:
  1072.                 self.checked = False
  1073.             else:
  1074.                 self.checked = True
  1075.                 if isinstance(value, basestring):
  1076.                     self.set('value', value)
  1077.                 
  1078.         else:
  1079.             self.set('value', value)
  1080.  
  1081.     
  1082.     def _value__del(self):
  1083.         if self.checkable:
  1084.             self.checked = False
  1085.         elif 'value' in self.attrib:
  1086.             del self.attrib['value']
  1087.         
  1088.  
  1089.     value = property(_value__get, _value__set, _value__del, doc = _value__get.__doc__)
  1090.     
  1091.     def _type__get(self):
  1092.         return self.get('type', 'text').lower()
  1093.  
  1094.     
  1095.     def _type__set(self, value):
  1096.         self.set('type', value)
  1097.  
  1098.     type = property(_type__get, _type__set, doc = _type__get.__doc__)
  1099.     
  1100.     def checkable(self):
  1101.         return self.type in ('checkbox', 'radio')
  1102.  
  1103.     checkable = property(checkable, doc = checkable.__doc__)
  1104.     
  1105.     def _checked__get(self):
  1106.         if not self.checkable:
  1107.             raise AttributeError('Not a checkable input type')
  1108.         self.checkable
  1109.         return 'checked' in self.attrib
  1110.  
  1111.     
  1112.     def _checked__set(self, value):
  1113.         if not self.checkable:
  1114.             raise AttributeError('Not a checkable input type')
  1115.         self.checkable
  1116.         if value:
  1117.             self.set('checked', '')
  1118.         elif 'checked' in self.attrib:
  1119.             del self.attrib['checked']
  1120.         
  1121.  
  1122.     checked = property(_checked__get, _checked__set, doc = _checked__get.__doc__)
  1123.  
  1124. HtmlElementClassLookup._default_element_classes['input'] = InputElement
  1125.  
  1126. class LabelElement(HtmlElement):
  1127.     
  1128.     def _for_element__get(self):
  1129.         id = self.get('for')
  1130.         if not id:
  1131.             return None
  1132.         return self.body.get_element_by_id(id)
  1133.  
  1134.     
  1135.     def _for_element__set(self, other):
  1136.         id = other.get('id')
  1137.         if not id:
  1138.             raise TypeError('Element %r has no id attribute' % other)
  1139.         id
  1140.         self.set('for', id)
  1141.  
  1142.     
  1143.     def _for_element__del(self):
  1144.         if 'id' in self.attrib:
  1145.             del self.attrib['id']
  1146.         
  1147.  
  1148.     for_element = property(_for_element__get, _for_element__set, _for_element__del, doc = _for_element__get.__doc__)
  1149.  
  1150. HtmlElementClassLookup._default_element_classes['label'] = LabelElement
  1151.  
  1152. def html_to_xhtml(html):
  1153.     
  1154.     try:
  1155.         html = html.getroot()
  1156.     except AttributeError:
  1157.         pass
  1158.  
  1159.     prefix = '{%s}' % XHTML_NAMESPACE
  1160.     for el in html.iter():
  1161.         tag = el.tag
  1162.         if isinstance(tag, basestring):
  1163.             if tag[0] != '{':
  1164.                 el.tag = prefix + tag
  1165.             
  1166.         tag[0] != '{'
  1167.     
  1168.  
  1169.  
  1170. def xhtml_to_html(xhtml):
  1171.     
  1172.     try:
  1173.         xhtml = xhtml.getroot()
  1174.     except AttributeError:
  1175.         pass
  1176.  
  1177.     prefix = '{%s}' % XHTML_NAMESPACE
  1178.     prefix_len = len(prefix)
  1179.     for el in xhtml.iter(prefix + '*'):
  1180.         el.tag = el.tag[prefix_len:]
  1181.     
  1182.  
  1183. __str_replace_meta_content_type = re.compile('<meta http-equiv="Content-Type"[^>]*>').sub
  1184. __bytes_replace_meta_content_type = re.compile('<meta http-equiv="Content-Type"[^>]*>'.encode('ASCII')).sub
  1185.  
  1186. def tostring(doc, pretty_print = False, include_meta_content_type = False, encoding = None, method = 'html'):
  1187.     html = etree.tostring(doc, method = method, pretty_print = pretty_print, encoding = encoding)
  1188.     if not include_meta_content_type:
  1189.         if isinstance(html, str):
  1190.             html = __str_replace_meta_content_type('', html)
  1191.         else:
  1192.             html = __bytes_replace_meta_content_type(bytes(), html)
  1193.     
  1194.     return html
  1195.  
  1196. tostring.__doc__ = __fix_docstring(tostring.__doc__)
  1197.  
  1198. def open_in_browser(doc):
  1199.     import os
  1200.     import webbrowser
  1201.     
  1202.     try:
  1203.         write_doc = doc.write
  1204.     except AttributeError:
  1205.         write_doc = etree.ElementTree(element = doc).write
  1206.  
  1207.     fn = os.tempnam() + '.html'
  1208.     write_doc(fn, method = 'html')
  1209.     url = 'file://' + fn.replace(os.path.sep, '/')
  1210.     print url
  1211.     webbrowser.open(url)
  1212.  
  1213.  
  1214. class HTMLParser(etree.HTMLParser):
  1215.     
  1216.     def __init__(self, **kwargs):
  1217.         super(HTMLParser, self).__init__(**kwargs)
  1218.         self.set_element_class_lookup(HtmlElementClassLookup())
  1219.  
  1220.  
  1221.  
  1222. class XHTMLParser(etree.XMLParser):
  1223.     
  1224.     def __init__(self, **kwargs):
  1225.         super(XHTMLParser, self).__init__(**kwargs)
  1226.         self.set_element_class_lookup(HtmlElementClassLookup())
  1227.  
  1228.  
  1229.  
  1230. def Element(*args, **kw):
  1231.     v = html_parser.makeelement(*args, **kw)
  1232.     return v
  1233.  
  1234. html_parser = HTMLParser()
  1235. xhtml_parser = XHTMLParser()
  1236.