Hello community,

here is the log from the commit of package python-pyquery for openSUSE:Factory 
checked in at 2019-12-02 11:32:31
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-pyquery (Old)
 and      /work/SRC/openSUSE:Factory/.python-pyquery.new.4691 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-pyquery"

Mon Dec  2 11:32:31 2019 rev:17 rq:751376 version:1.4.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-pyquery/python-pyquery.changes    
2019-02-26 22:15:34.518207749 +0100
+++ /work/SRC/openSUSE:Factory/.python-pyquery.new.4691/python-pyquery.changes  
2019-12-02 11:37:47.750453794 +0100
@@ -1,0 +2,14 @@
+Sun Nov 24 17:21:55 UTC 2019 - Arun Persaud <[email protected]>
+
+- specfile:
+  * be more specific in %files section
+
+- update to version 1.4.1:
+  * This is the latest release with py2 support
+  * Remove py33, py34 support
+  * web scraping improvements: default timeout and session support
+  * Add API methods to serialize form-related elements according to
+    spec
+  * Include HTML markup when querying textarea text/value
+
+-------------------------------------------------------------------

Old:
----
  pyquery-1.4.0.tar.gz

New:
----
  pyquery-1.4.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-pyquery.spec ++++++
--- /var/tmp/diff_new_pack.AyjsWC/_old  2019-12-02 11:37:48.890454034 +0100
+++ /var/tmp/diff_new_pack.AyjsWC/_new  2019-12-02 11:37:48.894454035 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package python-pyquery
 #
-# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2019 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -26,12 +26,12 @@
 %bcond_with test
 %endif
 Name:           python-pyquery%{psuffix}
-Version:        1.4.0
+Version:        1.4.1
 Release:        0
 Summary:        A jQuery-like library for python
 License:        BSD-3-Clause
 Group:          Development/Languages/Python
-URL:            http://pypi.python.org/pypi/pyquery
+URL:            https://pypi.python.org/pypi/pyquery
 Source:         
https://files.pythonhosted.org/packages/source/p/pyquery/pyquery-%{version}.tar.gz
 BuildRequires:  %{python_module coverage}
 BuildRequires:  %{python_module cssselect > 0.7.9}
@@ -77,7 +77,8 @@
 %files %{python_files}
 %license LICENSE.txt
 %doc CHANGES.rst README.rst
-%{python_sitelib}/*
+%{python_sitelib}/pyquery/
+%{python_sitelib}/pyquery-%{version}-py*.egg-info
 %endif
 
 %changelog

++++++ pyquery-1.4.0.tar.gz -> pyquery-1.4.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyquery-1.4.0/CHANGES.rst 
new/pyquery-1.4.1/CHANGES.rst
--- old/pyquery-1.4.0/CHANGES.rst       2018-01-11 20:50:55.000000000 +0100
+++ new/pyquery-1.4.1/CHANGES.rst       2019-10-26 10:47:47.000000000 +0200
@@ -1,3 +1,17 @@
+1.4.1 (2019-10-26)
+------------------
+
+- This is the latest release with py2 support
+
+- Remove py33, py34 support
+
+- web scraping improvements: default timeout and session support
+
+- Add API methods to serialize form-related elements according to spec
+
+- Include HTML markup when querying textarea text/value
+
+
 1.4.0 (2018-01-11)
 ------------------
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyquery-1.4.0/PKG-INFO new/pyquery-1.4.1/PKG-INFO
--- old/pyquery-1.4.0/PKG-INFO  2018-01-11 20:50:56.000000000 +0100
+++ new/pyquery-1.4.1/PKG-INFO  2019-10-26 10:47:47.000000000 +0200
@@ -1,12 +1,13 @@
-Metadata-Version: 1.1
+Metadata-Version: 1.2
 Name: pyquery
-Version: 1.4.0
+Version: 1.4.1
 Summary: A jquery-like library for python
 Home-page: https://github.com/gawel/pyquery
-Author: Gael Pasgrimaud
-Author-email: [email protected]
+Author: Olivier Lauzanne
+Author-email: [email protected]
+Maintainer: Gael Pasgrimaud
+Maintainer-email: [email protected]
 License: BSD
-Description-Content-Type: UNKNOWN
 Description: 
         pyquery: a jquery-like library for python
         =========================================
@@ -25,7 +26,7 @@
         
         The `project`_ is being actively developped on a git repository on 
Github. I
         have the policy of giving push access to anyone who wants it and then 
to review
-        what he does. So if you want to contribute just email me.
+        what they do. So if you want to contribute just email me.
         
         Please report bugs on the `github
         <https://github.com/gawel/pyquery/issues>`_ issue
@@ -88,6 +89,20 @@
         News
         ====
         
+        1.4.1 (2019-10-26)
+        ------------------
+        
+        - This is the latest release with py2 support
+        
+        - Remove py33, py34 support
+        
+        - web scraping improvements: default timeout and session support
+        
+        - Add API methods to serialize form-related elements according to spec
+        
+        - Include HTML markup when querying textarea text/value
+        
+        
         1.4.0 (2018-01-11)
         ------------------
         
@@ -335,7 +350,6 @@
 Classifier: Development Status :: 5 - Production/Stable
 Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.3
-Classifier: Programming Language :: Python :: 3.4
 Classifier: Programming Language :: Python :: 3.5
 Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyquery-1.4.0/README.rst new/pyquery-1.4.1/README.rst
--- old/pyquery-1.4.0/README.rst        2018-01-11 20:50:55.000000000 +0100
+++ new/pyquery-1.4.1/README.rst        2019-10-26 10:47:47.000000000 +0200
@@ -15,7 +15,7 @@
 
 The `project`_ is being actively developped on a git repository on Github. I
 have the policy of giving push access to anyone who wants it and then to review
-what he does. So if you want to contribute just email me.
+what they do. So if you want to contribute just email me.
 
 Please report bugs on the `github
 <https://github.com/gawel/pyquery/issues>`_ issue
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyquery-1.4.0/docs/scrap.rst 
new/pyquery-1.4.1/docs/scrap.rst
--- old/pyquery-1.4.0/docs/scrap.rst    2018-01-11 20:50:55.000000000 +0100
+++ new/pyquery-1.4.1/docs/scrap.rst    2019-10-26 10:47:47.000000000 +0200
@@ -19,4 +19,15 @@
   >>> pq(your_url, {'q': 'foo'}, method='post', verify=True)
   [<html>]
 
+
+Timeout
+-------
+
+The default timeout is 60 seconds, you can change it by setting the timeout 
parameter which is forwarded to the underlying urllib or requests library.
+
+Session
+-------
+
+When using the requests library you can instantiate a Session object which 
keeps state between http calls (for example - to keep cookies). You can set the 
session parameter to use this session object.
+
 .. _requests: http://docs.python-requests.org/en/latest/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyquery-1.4.0/pyquery/cssselectpatch.py 
new/pyquery-1.4.1/pyquery/cssselectpatch.py
--- old/pyquery-1.4.0/pyquery/cssselectpatch.py 2018-01-11 20:50:55.000000000 
+0100
+++ new/pyquery-1.4.1/pyquery/cssselectpatch.py 2019-10-26 10:47:47.000000000 
+0200
@@ -127,6 +127,26 @@
         xpath.add_condition("@selected and name(.) = 'option'")
         return xpath
 
+    def _format_disabled_xpath(self, disabled=True):
+        """Format XPath condition for :disabled or :enabled pseudo-classes
+        according to the WHATWG spec. See: https://html.spec.whatwg.org
+        /multipage/semantics-other.html#concept-element-disabled
+        """
+        bool_op = '' if disabled else 'not'
+        return '''(
+            ((name(.) = 'button' or name(.) = 'input' or name(.) = 'select'
+                    or name(.) = 'textarea' or name(.) = 'fieldset')
+                and %s(@disabled or (ancestor::fieldset[@disabled]
+                    and not(ancestor::legend[not(preceding-sibling::legend)])))
+            )
+            or
+            ((name(.) = 'option'
+                and %s(@disabled or ancestor::optgroup[@disabled]))
+            )
+            or
+            ((name(.) = 'optgroup' and %s(@disabled)))
+            )''' % (bool_op, bool_op, bool_op)
+
     def xpath_disabled_pseudo(self, xpath):
         """Matches all elements that are disabled::
 
@@ -137,7 +157,7 @@
 
         ..
         """
-        xpath.add_condition("@disabled")
+        xpath.add_condition(self._format_disabled_xpath())
         return xpath
 
     def xpath_enabled_pseudo(self, xpath):
@@ -150,7 +170,7 @@
 
         ..
         """
-        xpath.add_condition("not(@disabled) and name(.) = 'input'")
+        xpath.add_condition(self._format_disabled_xpath(disabled=False))
         return xpath
 
     def xpath_file_pseudo(self, xpath):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyquery-1.4.0/pyquery/openers.py 
new/pyquery-1.4.1/pyquery/openers.py
--- old/pyquery-1.4.0/pyquery/openers.py        2018-01-11 20:50:55.000000000 
+0100
+++ new/pyquery-1.4.1/pyquery/openers.py        2019-10-26 10:47:47.000000000 
+0200
@@ -19,6 +19,7 @@
 except ImportError:
     HAS_REQUEST = False
 
+DEFAULT_TIMEOUT = 60
 
 allowed_args = (
     'auth', 'data', 'headers', 'verify',
@@ -48,16 +49,21 @@
 
 
 def _requests(url, kwargs):
+
     encoding = kwargs.get('encoding')
     method = kwargs.get('method', 'get').lower()
-    meth = getattr(requests, str(method))
+    session = kwargs.get('session')
+    if session:
+        meth = getattr(session, str(method))
+    else:
+        meth = getattr(requests, str(method))
     if method == 'get':
         url, data = _query(url, method, kwargs)
     kw = {}
     for k in allowed_args:
         if k in kwargs:
             kw[k] = kwargs[k]
-    resp = meth(url=url, **kw)
+    resp = meth(url=url, timeout=kwargs.get('timeout', DEFAULT_TIMEOUT), **kw)
     if not (200 <= resp.status_code < 300):
         raise HTTPError(resp.url, resp.status_code,
                         resp.reason, resp.headers, None)
@@ -70,7 +76,7 @@
 def _urllib(url, kwargs):
     method = kwargs.get('method')
     url, data = _query(url, method, kwargs)
-    return urlopen(url, data)
+    return urlopen(url, data, timeout=kwargs.get('timeout', DEFAULT_TIMEOUT))
 
 
 def url_opener(url, kwargs):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyquery-1.4.0/pyquery/pyquery.py 
new/pyquery-1.4.1/pyquery/pyquery.py
--- old/pyquery-1.4.0/pyquery/pyquery.py        2018-01-11 20:50:55.000000000 
+0100
+++ new/pyquery-1.4.1/pyquery/pyquery.py        2019-10-26 10:47:47.000000000 
+0200
@@ -4,6 +4,7 @@
 #
 # Distributed under the BSD license, see LICENSE.txt
 from .cssselectpatch import JQueryTranslator
+from collections import OrderedDict
 from .openers import url_opener
 from .text import extract_text
 from copy import deepcopy
@@ -424,7 +425,7 @@
         """return the xml root element
         """
         if self._parent is not no_default:
-            return self._parent.getroottree()
+            return self._parent[0].getroottree()
         return self[0].getroottree()
 
     @property
@@ -996,13 +997,37 @@
             >>> d.val()
             'Youhou'
 
+        Set the selected values for a `select` element with the `multiple`
+        attribute::
+
+            >>> d = PyQuery('''
+            ...             <select multiple>
+            ...                 <option value="you"><option value="hou">
+            ...             </select>
+            ...             ''')
+            >>> d.val(['you', 'hou'])
+            [<select>]
+
+        Get the selected values for a `select` element with the `multiple`
+        attribute::
+
+            >>> d.val()
+            ['you', 'hou']
+
         """
         def _get_value(tag):
             # <textarea>
             if tag.tag == 'textarea':
-                return self._copy(tag).text()
+                return self._copy(tag).html()
             # <select>
             elif tag.tag == 'select':
+                if 'multiple' in tag.attrib:
+                    # Only extract value if selected
+                    selected = self._copy(tag)('option[selected]')
+                    # Rebuild list to avoid serialization error
+                    return list(selected.map(
+                        lambda _, o: self._copy(o).attr('value')
+                    ))
                 selected_option = self._copy(tag)('option[selected]:last')
                 if selected_option:
                     return selected_option.attr('value')
@@ -1015,25 +1040,36 @@
                     return 'on'
                 else:
                     return val
-            # <input> and everything else.
+            # <input>
+            elif tag.tag == 'input':
+                val = self._copy(tag).attr('value')
+                return val.replace('\n', '') if val else ''
+            # everything else.
             return self._copy(tag).attr('value') or ''
 
         def _set_value(pq, value):
             for tag in pq:
-                # <textarea>
-                if tag.tag == 'textarea':
-                    self._copy(tag).text(value)
-                    continue
                 # <select>
                 if tag.tag == 'select':
+                    if not isinstance(value, list):
+                        value = [value]
                     def _make_option_selected(_, elem):
                         pq = self._copy(elem)
-                        if pq.attr('value') == value:
+                        if pq.attr('value') in value:
                             pq.attr('selected', 'selected')
+                            if 'multiple' not in tag.attrib:
+                                del value[:]  # Ensure it toggles first match
                         else:
                             pq.removeAttr('selected')
                     self._copy(tag)('option').each(_make_option_selected)
                     continue
+                # Stringify array
+                if isinstance(value, list):
+                    value = ','.join(value)
+                # <textarea>
+                if tag.tag == 'textarea':
+                    self._copy(tag).text(value)
+                    continue
                 # <input> and everything else.
                 self._copy(tag).attr('value', value)
 
@@ -1101,7 +1137,6 @@
                 if children:
                     tag.extend(children)
                 tag.text = root.text
-                tag.tail = root.tail
         return self
 
     @with_camel_case_alias
@@ -1144,6 +1179,14 @@
             >>> print(doc.text())
             toto tata
 
+        Get the text value, without squashing newlines::
+
+            >>> doc = PyQuery('''<div><span>toto</span>
+            ...               <span>tata</span></div>''')
+            >>> print(doc.text(squash_space=False))
+            toto
+            tata
+
         Set the text value::
 
             >>> doc.text('Youhou !')
@@ -1156,7 +1199,10 @@
         if value is no_default:
             if not self:
                 return ''
-            return ' '.join(extract_text(tag, **kwargs) for tag in self)
+            return ' '.join(
+                self._copy(tag).html() if tag.tag == 'textarea' else
+                extract_text(tag, **kwargs) for tag in self
+            )
 
         for tag in self:
             for child in tag.getchildren():
@@ -1477,10 +1523,138 @@
             setattr(PyQuery, name, fn)
     fn = Fn()
 
+
+    ########
+    # AJAX #
+    ########
+
+    @with_camel_case_alias
+    def serialize_array(self):
+        """Serialize form elements as an array of dictionaries, whose structure
+        mirrors that produced by the jQuery API. Notably, it does not handle 
the
+        deprecated `keygen` form element.
+
+            >>> d = PyQuery('<form><input name="order" value="spam"></form>')
+            >>> d.serialize_array() == [{'name': 'order', 'value': 'spam'}]
+            True
+            >>> d.serializeArray() == [{'name': 'order', 'value': 'spam'}]
+            True
+        """
+        return list(map(
+            lambda p: {'name': p[0], 'value': p[1]},
+            self.serialize_pairs()
+        ))
+
+    def serialize(self):
+        """Serialize form elements as a URL-encoded string.
+
+            >>> h = (
+            ... '<form><input name="order" value="spam">'
+            ... '<input name="order2" value="baked beans"></form>'
+            ... )
+            >>> d = PyQuery(h)
+            >>> d.serialize()
+            'order=spam&order2=baked%20beans'
+        """
+        return urlencode(self.serialize_pairs()).replace('+', '%20')
+
+
     #####################################################
     # Additional methods that are not in the jQuery API #
     #####################################################
 
+    @with_camel_case_alias
+    def serialize_pairs(self):
+        """Serialize form elements as an array of 2-tuples conventional for
+        typical URL-parsing operations in Python.
+
+            >>> d = PyQuery('<form><input name="order" value="spam"></form>')
+            >>> d.serialize_pairs()
+            [('order', 'spam')]
+            >>> d.serializePairs()
+            [('order', 'spam')]
+        """
+        # https://github.com/jquery/jquery/blob
+        # /2d4f53416e5f74fa98e0c1d66b6f3c285a12f0ce/src/serialize.js#L14
+        _submitter_types = ['submit', 'button', 'image', 'reset', 'file']
+
+        controls = self._copy([])
+        # Expand list of form controls
+        for el in self.items():
+            if el[0].tag == 'form':
+                form_id = el.attr('id')
+                if form_id:
+                    # Include inputs outside of their form owner
+                    root = self._copy(el.root.getroot())
+                    controls.extend(root(
+                        '#%s :not([form]):input, [form="%s"]:input'
+                        % (form_id, form_id)))
+                else:
+                    controls.extend(el(':not([form]):input'))
+            elif el[0].tag == 'fieldset':
+                controls.extend(el(':input'))
+            else:
+                controls.extend(el)
+        # Filter controls
+        selector = '[name]:enabled:not(button)'  # Not serializing image button
+        selector += ''.join(map(
+            lambda s: ':not([type="%s"])' % s,
+            _submitter_types))
+        controls = controls.filter(selector)
+
+        def _filter_out_unchecked(_, el):
+            el = controls._copy(el)
+            return not el.is_(':checkbox:not(:checked)') \
+                    and not el.is_(':radio:not(:checked)')
+        controls = controls.filter(_filter_out_unchecked)
+
+        # jQuery serializes inputs with the datalist element as an ancestor
+        # contrary to WHATWG spec as of August 2018
+        #
+        # xpath = 'self::*[not(ancestor::datalist)]'
+        # results = []
+        # for tag in controls:
+        #     results.extend(tag.xpath(xpath, namespaces=controls.namespaces))
+        # controls = controls._copy(results)
+
+        # Serialize values
+        ret = []
+        for field in controls:
+            val = self._copy(field).val()
+            if isinstance(val, list):
+                ret.extend(map(
+                    lambda v: (field.attrib['name'], v.replace('\n', '\r\n')),
+                    val
+                ))
+            else:
+                ret.append((field.attrib['name'], val.replace('\n', '\r\n')))
+        return ret
+
+    @with_camel_case_alias
+    def serialize_dict(self):
+        """Serialize form elements as an ordered dictionary. Multiple values
+        corresponding to the same input name are concatenated into one list.
+
+            >>> d = PyQuery('''<form>
+            ...             <input name="order" value="spam">
+            ...             <input name="order" value="eggs">
+            ...             <input name="order2" value="ham">
+            ...             </form>''')
+            >>> d.serialize_dict()
+            OrderedDict([('order', ['spam', 'eggs']), ('order2', 'ham')])
+            >>> d.serializeDict()
+            OrderedDict([('order', ['spam', 'eggs']), ('order2', 'ham')])
+        """
+        ret = OrderedDict()
+        for name, val in self.serialize_pairs():
+            if name not in ret:
+                ret[name] = val
+            elif not isinstance(ret[name], list):
+                ret[name] = [ret[name], val]
+            else:
+                ret[name].append(val)
+        return ret
+
     @property
     def base_url(self):
         """Return the url of current html document or None if not available.
@@ -1507,6 +1681,11 @@
                 if attr_value is None:
                     return None
 
+                # skip specific "protocol" schemas
+                if any(attr_value.startswith(schema)
+                       for schema in ('tel:', 'callto:', 'sms:')):
+                    return None
+
                 return self(e).attr(attr,
                                     urljoin(base_url, attr_value.strip()))
             return rep
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyquery-1.4.0/pyquery.egg-info/PKG-INFO 
new/pyquery-1.4.1/pyquery.egg-info/PKG-INFO
--- old/pyquery-1.4.0/pyquery.egg-info/PKG-INFO 2018-01-11 20:50:56.000000000 
+0100
+++ new/pyquery-1.4.1/pyquery.egg-info/PKG-INFO 2019-10-26 10:47:47.000000000 
+0200
@@ -1,12 +1,13 @@
-Metadata-Version: 1.1
+Metadata-Version: 1.2
 Name: pyquery
-Version: 1.4.0
+Version: 1.4.1
 Summary: A jquery-like library for python
 Home-page: https://github.com/gawel/pyquery
-Author: Gael Pasgrimaud
-Author-email: [email protected]
+Author: Olivier Lauzanne
+Author-email: [email protected]
+Maintainer: Gael Pasgrimaud
+Maintainer-email: [email protected]
 License: BSD
-Description-Content-Type: UNKNOWN
 Description: 
         pyquery: a jquery-like library for python
         =========================================
@@ -25,7 +26,7 @@
         
         The `project`_ is being actively developped on a git repository on 
Github. I
         have the policy of giving push access to anyone who wants it and then 
to review
-        what he does. So if you want to contribute just email me.
+        what they do. So if you want to contribute just email me.
         
         Please report bugs on the `github
         <https://github.com/gawel/pyquery/issues>`_ issue
@@ -88,6 +89,20 @@
         News
         ====
         
+        1.4.1 (2019-10-26)
+        ------------------
+        
+        - This is the latest release with py2 support
+        
+        - Remove py33, py34 support
+        
+        - web scraping improvements: default timeout and session support
+        
+        - Add API methods to serialize form-related elements according to spec
+        
+        - Include HTML markup when querying textarea text/value
+        
+        
         1.4.0 (2018-01-11)
         ------------------
         
@@ -335,7 +350,6 @@
 Classifier: Development Status :: 5 - Production/Stable
 Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.3
-Classifier: Programming Language :: Python :: 3.4
 Classifier: Programming Language :: Python :: 3.5
 Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyquery-1.4.0/setup.py new/pyquery-1.4.1/setup.py
--- old/pyquery-1.4.0/setup.py  2018-01-11 20:50:55.000000000 +0100
+++ new/pyquery-1.4.1/setup.py  2019-10-26 10:47:47.000000000 +0200
@@ -40,7 +40,7 @@
 
 """ % read('README', 'CHANGES')
 
-version = '1.4.0'
+version = '1.4.1'
 
 setup(name='pyquery',
       version=version,
@@ -51,10 +51,9 @@
           "Development Status :: 5 - Production/Stable",
           "Programming Language :: Python :: 2.7",
           "Programming Language :: Python :: 3",
-          "Programming Language :: Python :: 3.3",
-          "Programming Language :: Python :: 3.4",
           "Programming Language :: Python :: 3.5",
           "Programming Language :: Python :: 3.6",
+          "Programming Language :: Python :: 3.7",
       ],
       keywords='jquery html xml scraping',
       author='Olivier Lauzanne',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyquery-1.4.0/tests/geckodriver.sh 
new/pyquery-1.4.1/tests/geckodriver.sh
--- old/pyquery-1.4.0/tests/geckodriver.sh      2018-01-11 20:50:55.000000000 
+0100
+++ new/pyquery-1.4.1/tests/geckodriver.sh      2019-10-26 10:47:47.000000000 
+0200
@@ -1,5 +1,5 @@
 #!/bin/bash
 
-driver="https://github.com/mozilla/geckodriver/releases/download/v0.19.1/geckodriver-v0.19.1-linux64.tar.gz";
+driver="https://github.com/mozilla/geckodriver/releases/download/v0.26.0/geckodriver-v0.26.0-linux64.tar.gz";
 
 [ -f geckodriver ] || wget -cqO- $driver | tar xvzf -
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyquery-1.4.0/tests/selenium.sh 
new/pyquery-1.4.1/tests/selenium.sh
--- old/pyquery-1.4.0/tests/selenium.sh 2018-01-11 20:50:55.000000000 +0100
+++ new/pyquery-1.4.1/tests/selenium.sh 2019-10-26 10:47:47.000000000 +0200
@@ -4,5 +4,5 @@
 # get geckodriver
 ./tests/geckodriver.sh
 
-# run tox with py3.6
-MOZ_HEADLESS=1 PATH=$PATH:$PWD tox -e py36 tests/test_real_browser.py
+# run tox with py3.7
+MOZ_HEADLESS=1 PATH=$PATH:$PWD tox -e py37 tests/test_real_browser.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyquery-1.4.0/tests/test_pyquery.py 
new/pyquery-1.4.1/tests/test_pyquery.py
--- old/pyquery-1.4.0/tests/test_pyquery.py     2018-01-11 20:50:55.000000000 
+0100
+++ new/pyquery-1.4.1/tests/test_pyquery.py     2019-10-26 10:47:47.000000000 
+0200
@@ -5,8 +5,10 @@
 # Distributed under the BSD license, see LICENSE.txt
 import os
 import sys
+import time
 from lxml import etree
-from pyquery.pyquery import PyQuery as pq
+from pyquery.pyquery import PyQuery as pq, no_default
+from pyquery.openers import HAS_REQUEST
 from webtest import http
 from webtest.debugapp import debug_app
 from .compat import PY3k
@@ -94,8 +96,32 @@
             <body>
               <form action="/">
                 <input name="enabled" type="text" value="test"/>
+                <b disabled>Not :disabled</b>
                 <input name="disabled" type="text"
                        value="disabled" disabled="disabled"/>
+                <fieldset>
+                    <input name="fieldset-enabled">
+                </fieldset>
+                <fieldset disabled>
+                    <legend>
+                        <input name="legend-enabled">
+                    </legend>
+                    <input name="fieldset-disabled">
+                    <legend>
+                        <input name="legend-disabled">
+                    </legend>
+                    <select id="disabled-select">
+                        <optgroup>
+                            <option></option>
+                        </optgroup>
+                    </select>
+                </fieldset>
+                <select>
+                    <optgroup id="disabled-optgroup" disabled>
+                        <option id="disabled-from-optgroup"></option>
+                        <option id="disabled-option" disabled></option>
+                    </optgroup>
+                </select>
                 <input name="file" type="file" />
                 <select name="select">
                   <option value="">Choose something</option>
@@ -137,6 +163,10 @@
         self.assertEqual(isinstance(doc.root, etree._ElementTree), True)
         self.assertEqual(doc.encoding, 'UTF-8')
 
+        child = doc.children().eq(0)
+        self.assertNotEqual(child._parent, no_default)
+        self.assertTrue(isinstance(child.root, etree._ElementTree))
+
     def test_selector_from_doc(self):
         doc = etree.fromstring(self.html)
         assert len(self.klass(doc)) == 1
@@ -178,12 +208,23 @@
 
         # test on the form
         e = self.klass(self.html4)
-        assert len(e(':disabled')) == 1
-        assert len(e('input:enabled')) == 9
+        disabled = e(':disabled')
+        self.assertIn(e('[name="disabled"]')[0], disabled)
+        self.assertIn(e('fieldset[disabled]')[0], disabled)
+        self.assertIn(e('[name="legend-disabled"]')[0], disabled)
+        self.assertIn(e('[name="fieldset-disabled"]')[0], disabled)
+        self.assertIn(e('#disabled-optgroup')[0], disabled)
+        self.assertIn(e('#disabled-from-optgroup')[0], disabled)
+        self.assertIn(e('#disabled-option')[0], disabled)
+        self.assertIn(e('#disabled-select')[0], disabled)
+
+        assert len(disabled) == 8
+        assert len(e('select:enabled')) == 2
+        assert len(e('input:enabled')) == 11
         assert len(e(':selected')) == 1
         assert len(e(':checked')) == 2
         assert len(e(':file')) == 1
-        assert len(e(':input')) == 12
+        assert len(e(':input')) == 18
         assert len(e(':button')) == 2
         assert len(e(':radio')) == 3
         assert len(e(':checkbox')) == 3
@@ -349,8 +390,18 @@
         <input type="radio" value="Ham">
     '''
 
+    html2_newline = '''
+        <input id="newline-text" type="text" name="order" value="S
+pam">
+        <input id="newline-radio" type="radio" name="order" value="S
+pam">
+    '''
+
     html3 = '''
-        <textarea>Spam</textarea>
+        <textarea id="textarea-single">Spam</textarea>
+        <textarea id="textarea-multi">Spam
+<b>Eggs</b>
+Bacon</textarea>
     '''
 
     html4 = '''
@@ -365,6 +416,29 @@
         </select>
         <select id="third">
         </select>
+        <select id="fourth">
+            <option value="spam">Spam</option>
+            <option value="spam">Eggs</option>
+            <option value="spam">Bacon</option>
+        </select>
+    '''
+
+    html6 = '''
+        <select id="first" multiple>
+            <option value="spam" selected>Spam</option>
+            <option value="eggs" selected>Eggs</option>
+            <option value="bacon">Bacon</option>
+        </select>
+        <select id="second" multiple>
+            <option value="spam">Spam</option>
+            <option value="eggs">Eggs</option>
+            <option value="bacon">Bacon</option>
+        </select>
+        <select id="third" multiple>
+            <option value="spam">Spam</option>
+            <option value="spam">Eggs</option>
+            <option value="spam">Bacon</option>
+        </select>
     '''
 
     html5 = '''
@@ -410,14 +484,27 @@
         self.assertEqual(d('input:checkbox').val(), '44')
         self.assertEqual(d('input:radio').val(), '45')
 
+    def test_val_for_inputs_with_newline(self):
+        d = pq(self.html2_newline)
+        self.assertEqual(d('#newline-text').val(), 'Spam')
+        self.assertEqual(d('#newline-radio').val(), 'S\npam')
+
     def test_val_for_textarea(self):
         d = pq(self.html3)
-        self.assertEqual(d('textarea').val(), 'Spam')
-        self.assertEqual(d('textarea').text(), 'Spam')
-        d('textarea').val('42')
-        self.assertEqual(d('textarea').val(), '42')
+        self.assertEqual(d('#textarea-single').val(), 'Spam')
+        self.assertEqual(d('#textarea-single').text(), 'Spam')
+        d('#textarea-single').val('42')
+        self.assertEqual(d('#textarea-single').val(), '42')
         # Note: jQuery still returns 'Spam' here.
-        self.assertEqual(d('textarea').text(), '42')
+        self.assertEqual(d('#textarea-single').text(), '42')
+
+        multi_expected = '''Spam\n<b>Eggs</b>\nBacon'''
+        self.assertEqual(d('#textarea-multi').val(), multi_expected)
+        self.assertEqual(d('#textarea-multi').text(), multi_expected)
+        multi_new = '''Bacon\n<b>Eggs</b>\nSpam'''
+        d('#textarea-multi').val(multi_new)
+        self.assertEqual(d('#textarea-multi').val(), multi_new)
+        self.assertEqual(d('#textarea-multi').text(), multi_new)
 
     def test_val_for_select(self):
         d = pq(self.html4)
@@ -432,6 +519,36 @@
         self.assertIsNone(d('#third').val())
         d('#first').val('bacon')  # Selecting non-existing option.
         self.assertEqual(d('#first').val(), 'spam')
+        # Value set based on option order, not value order
+        d('#second').val(['bacon', 'eggs'])
+        self.assertEqual(d('#second').val(), 'eggs')
+        d('#fourth').val(['spam'])
+        self.assertEqual(d('#fourth').val(), 'spam')
+        # Sets first option with matching value
+        self.assertEqual(d('#fourth option[selected]').length, 1)
+        self.assertEqual(d('#fourth option[selected]').text(), 'Spam')
+
+    def test_val_for_select_multiple(self):
+        d = pq(self.html6)
+        self.assertEqual(d('#first').val(), ['spam', 'eggs'])
+        # Selecting non-existing option.
+        d('#first').val(['eggs', 'sausage', 'bacon'])
+        self.assertEqual(d('#first').val(), ['eggs', 'bacon'])
+        self.assertEqual(d('#second').val(), [])
+        d('#second').val('eggs')
+        self.assertEqual(d('#second').val(), ['eggs'])
+        d('#second').val(['not spam', 'not eggs'])
+        self.assertEqual(d('#second').val(), [])
+        d('#third').val(['spam'])
+        self.assertEqual(d('#third').val(), ['spam', 'spam', 'spam'])
+
+    def test_val_for_input_and_textarea_given_array_value(self):
+        d = pq('<input type="text">')
+        d('input').val(['spam', 'eggs'])
+        self.assertEqual(d('input').val(), 'spam,eggs')
+        d = pq('<textarea></textarea>')
+        d('textarea').val(['spam', 'eggs'])
+        self.assertEqual(d('textarea').val(), 'spam,eggs')
 
     def test_val_for_multiple_elements(self):
         d = pq(self.html5)
@@ -461,6 +578,152 @@
         d = pq('<input>')
         self.assertEqual(d.val(), '')
 
+    def test_html_replacement(self):
+        html = '<div>Not Me<span>Replace Me</span>Not Me</div>'
+        replacement = 'New <em>Contents</em> New'
+        expected = html.replace('Replace Me', replacement)
+
+        d = pq(html)
+        d.find('span').html(replacement)
+
+        new_html = d.outerHtml()
+        self.assertEqual(new_html, expected)
+        self.assertIn(replacement, new_html)
+
+
+class TestAjax(TestCase):
+
+    html = '''
+    <div id="div">
+    <input form="dispersed" name="order" value="spam">
+    </div>
+    <form id="dispersed">
+    <div><input name="order" value="eggs"></div>
+    <input form="dispersed" name="order" value="ham">
+    <input form="other-form" name="order" value="nothing">
+    <input form="" name="order" value="nothing">
+    </form>
+    <form id="other-form">
+    <input form="dispersed" name="order" value="tomato">
+    </form>
+    <form class="no-id">
+    <input form="dispersed" name="order" value="baked beans">
+    <input name="spam" value="Spam">
+    </form>
+    '''
+
+    html2 = '''
+    <form id="first">
+    <input name="order" value="spam">
+    <fieldset>
+    <input name="fieldset" value="eggs">
+    <input id="input" name="fieldset" value="ham">
+    </fieldset>
+    </form>
+    <form id="datalist">
+    <datalist><div><input name="datalist" value="eggs"></div></datalist>
+    <input type="checkbox" name="checkbox" checked>
+    <input type="radio" name="radio" checked>
+    </form>
+    '''
+
+    html3 = '''
+    <form>
+    <input name="order" value="spam">
+    <input id="noname" value="sausage">
+    <fieldset disabled>
+    <input name="order" value="sausage">
+    </fieldset>
+    <input name="disabled" value="ham" disabled>
+    <input type="submit" name="submit" value="Submit">
+    <input type="button" name="button" value="">
+    <input type="image" name="image" value="">
+    <input type="reset" name="reset" value="Reset">
+    <input type="file" name="file" value="">
+    <button type="submit" name="submit" value="submit"></button>
+    <input type="checkbox" name="spam">
+    <input type="radio" name="eggs">
+    </form>
+    '''
+
+    html4 = '''
+    <form>
+    <input name="spam" value="Spam/
+spam">
+    <select name="order" multiple>
+    <option value="baked
+beans" selected>
+    <option value="tomato" selected>
+    <option value="spam">
+    </select>
+    <textarea name="multiline">multiple
+lines
+of text</textarea>
+    </form>
+    '''
+
+    def test_serialize_pairs_form_id(self):
+        d = pq(self.html)
+        self.assertEqual(d('#div').serialize_pairs(), [])
+        self.assertEqual(d('#dispersed').serialize_pairs(), [
+            ('order', 'spam'), ('order', 'eggs'), ('order', 'ham'),
+            ('order', 'tomato'), ('order', 'baked beans'),
+        ])
+        self.assertEqual(d('.no-id').serialize_pairs(), [
+            ('spam', 'Spam'),
+        ])
+
+    def test_serialize_pairs_form_controls(self):
+        d = pq(self.html2)
+        self.assertEqual(d('fieldset').serialize_pairs(), [
+            ('fieldset', 'eggs'), ('fieldset', 'ham'),
+        ])
+        self.assertEqual(d('#input, fieldset, #first').serialize_pairs(), [
+            ('order', 'spam'), ('fieldset', 'eggs'), ('fieldset', 'ham'),
+            ('fieldset', 'eggs'), ('fieldset', 'ham'), ('fieldset', 'ham'),
+        ])
+        self.assertEqual(d('#datalist').serialize_pairs(), [
+            ('datalist', 'eggs'), ('checkbox', 'on'), ('radio', 'on'),
+        ])
+
+    def test_serialize_pairs_filter_controls(self):
+        d = pq(self.html3)
+        self.assertEqual(d('form').serialize_pairs(), [
+            ('order', 'spam')
+        ])
+
+    def test_serialize_pairs_form_values(self):
+        d = pq(self.html4)
+        self.assertEqual(d('form').serialize_pairs(), [
+            ('spam', 'Spam/spam'), ('order', 'baked\r\nbeans'),
+            ('order', 'tomato'), ('multiline', 'multiple\r\nlines\r\nof text'),
+        ])
+
+    def test_serialize_array(self):
+        d = pq(self.html4)
+        self.assertEqual(d('form').serialize_array(), [
+            {'name': 'spam', 'value': 'Spam/spam'},
+            {'name': 'order', 'value': 'baked\r\nbeans'},
+            {'name': 'order', 'value': 'tomato'},
+            {'name': 'multiline', 'value': 'multiple\r\nlines\r\nof text'},
+        ])
+
+    def test_serialize(self):
+        d = pq(self.html4)
+        self.assertEqual(
+            d('form').serialize(),
+            'spam=Spam%2Fspam&order=baked%0D%0Abeans&order=tomato&'
+            'multiline=multiple%0D%0Alines%0D%0Aof%20text'
+        )
+
+    def test_serialize_dict(self):
+        d = pq(self.html4)
+        self.assertEqual(d('form').serialize_dict(), {
+            'spam': 'Spam/spam',
+            'order': ['baked\r\nbeans', 'tomato'],
+            'multiline': 'multiple\r\nlines\r\nof text',
+        })
+
 
 class TestMakeLinks(TestCase):
 
@@ -609,6 +872,17 @@
         self.assertIn('REQUEST_METHOD: POST', d('p').text())
         self.assertIn('q=foo', d('p').text())
 
+    def test_session(self):
+        if HAS_REQUEST:
+            import requests
+            session = requests.Session()
+            session.headers.update({'X-FOO': 'bar'})
+            d = pq(self.application_url, {'q': 'foo'},
+                   method='get', session=session)
+            self.assertIn('HTTP_X_FOO: bar', d('p').text())
+        else:
+            self.skipTest('no requests library')
+
     def tearDown(self):
         self.s.shutdown()
 
@@ -620,3 +894,23 @@
                method='get')
         print(d)
         self.assertEqual(d('#pt-login').text(), u'Войти')
+
+
+class TestWebScrappingTimeouts(TestCase):
+
+    def setUp(self):
+        def app(environ, start_response):
+            start_response('200 OK', [('Content-Type', 'text/plain')])
+            time.sleep(2)
+            return [b'foobar\n']
+        self.s = http.StopableWSGIServer.create(app)
+        self.s.wait()
+        self.application_url = self.s.application_url.rstrip('/')
+
+    def test_get(self):
+        pq(self.application_url)
+        with self.assertRaises(Exception):
+            pq(self.application_url, timeout=1)
+
+    def tearDown(self):
+        self.s.shutdown()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyquery-1.4.0/tox.ini new/pyquery-1.4.1/tox.ini
--- old/pyquery-1.4.0/tox.ini   2018-01-11 20:50:55.000000000 +0100
+++ new/pyquery-1.4.1/tox.ini   2019-10-26 10:47:47.000000000 +0200
@@ -1,5 +1,5 @@
 [tox]
-envlist=py27,py33,py34,py35,py36
+envlist=py27,py35,py36,py37
 
 [testenv]
 whitelist_externals=
@@ -10,7 +10,7 @@
     rm -f .coverage
     {envbindir}/nosetests []
 deps =
-    py36: selenium
+    py37: selenium
     requests
     WebOb>1.1.9
     WebTest
@@ -20,7 +20,7 @@
 [testenv:flake8]
 skipsdist=true
 skip_install=true
-basepython = python3.5
+basepython = python3.7
 commands =
     flake8 pyquery tests
 deps =
@@ -29,7 +29,7 @@
 [testenv:docs]
 skip_install=false
 skipsdist=true
-basepython = python3.5
+basepython = python3.7
 changedir = docs
 deps =
     sphinx


Reply via email to