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

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