Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-hyperlink for openSUSE:Factory checked in at 2021-03-16 15:42:19 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-hyperlink (Old) and /work/SRC/openSUSE:Factory/.python-hyperlink.new.2401 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-hyperlink" Tue Mar 16 15:42:19 2021 rev:5 rq:878749 version:21.0.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-hyperlink/python-hyperlink.changes 2020-10-25 18:09:11.079490914 +0100 +++ /work/SRC/openSUSE:Factory/.python-hyperlink.new.2401/python-hyperlink.changes 2021-03-16 15:43:36.868961333 +0100 @@ -1,0 +2,9 @@ +Sat Mar 13 13:07:05 UTC 2021 - Dirk M??ller <dmuel...@suse.com> + +- update to 21.0.0: + * Update plus sign (+) handling to work with/like HTML form encoding (POST) + by default, fixes #129, and associated roundtripping (#146). + * Package IDNA tables. + * Long overdue dependency bumps + +------------------------------------------------------------------- Old: ---- hyperlink-20.0.1.tar.gz New: ---- hyperlink-21.0.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-hyperlink.spec ++++++ --- /var/tmp/diff_new_pack.5weBmg/_old 2021-03-16 15:43:37.308962037 +0100 +++ /var/tmp/diff_new_pack.5weBmg/_new 2021-03-16 15:43:37.308962037 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-hyperlink # -# Copyright (c) 2020 SUSE LLC +# Copyright (c) 2021 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,7 +18,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-hyperlink -Version: 20.0.1 +Version: 21.0.0 Release: 0 Summary: Immutable URL support for Python License: MIT ++++++ hyperlink-20.0.1.tar.gz -> hyperlink-21.0.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hyperlink-20.0.1/CHANGELOG.md new/hyperlink-21.0.0/CHANGELOG.md --- old/hyperlink-20.0.1/CHANGELOG.md 2020-08-04 08:34:08.000000000 +0200 +++ new/hyperlink-21.0.0/CHANGELOG.md 2020-08-05 05:36:26.000000000 +0200 @@ -1,5 +1,13 @@ # Hyperlink Changelog +## 20.0.1 + +*(August 4, 2020)* + +Rerelease to fix packaging metadata around conditional requirements. +See [issue #133](https://github.com/python-hyper/hyperlink/issues/133) +for more details. + ## 20.0.0 *(August 3, 2020)* diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hyperlink-20.0.1/PKG-INFO new/hyperlink-21.0.0/PKG-INFO --- old/hyperlink-20.0.1/PKG-INFO 2020-08-05 05:34:31.000000000 +0200 +++ new/hyperlink-21.0.0/PKG-INFO 2021-01-08 06:51:20.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.2 Name: hyperlink -Version: 20.0.1 +Version: 21.0.0 Summary: A featureful, immutable, and correct URL for Python. Home-page: https://github.com/python-hyper/hyperlink Author: Mahmoud Hashemi and Glyph Lefkowitz @@ -28,6 +28,7 @@ Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: License :: OSI Approved :: MIT License Requires-Python: >=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hyperlink-20.0.1/docs/conf.py new/hyperlink-21.0.0/docs/conf.py --- old/hyperlink-20.0.1/docs/conf.py 2020-08-04 08:36:18.000000000 +0200 +++ new/hyperlink-21.0.0/docs/conf.py 2020-08-05 05:34:50.000000000 +0200 @@ -65,7 +65,7 @@ author = u'Mahmoud Hashemi' version = '20.0' -release = '20.0.0' +release = '20.0.1' if os.name != 'nt': today_fmt = '%B %d, %Y' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hyperlink-20.0.1/setup.py new/hyperlink-21.0.0/setup.py --- old/hyperlink-20.0.1/setup.py 2020-08-05 05:33:04.000000000 +0200 +++ new/hyperlink-21.0.0/setup.py 2021-01-08 06:47:35.000000000 +0100 @@ -11,7 +11,7 @@ __author__ = "Mahmoud Hashemi and Glyph Lefkowitz" -__version__ = "20.0.1" +__version__ = "21.0.0" __contact__ = "mahm...@hatnote.com" __url__ = "https://github.com/python-hyper/hyperlink" __license__ = "MIT" @@ -27,7 +27,7 @@ url=__url__, packages=find_packages(where="src"), package_dir={"": "src"}, - package_data=dict(hyperlink=["py.typed"]), + package_data=dict(hyperlink=["py.typed", "idna-tables-properties.csv.gz"]), zip_safe=False, license=__license__, platforms="any", @@ -47,6 +47,7 @@ "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", "Programming Language :: Python :: Implementation :: PyPy", "License :: OSI Approved :: MIT License", ], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hyperlink-20.0.1/src/hyperlink/_url.py new/hyperlink-21.0.0/src/hyperlink/_url.py --- old/hyperlink-20.0.1/src/hyperlink/_url.py 2020-08-04 08:14:29.000000000 +0200 +++ new/hyperlink-21.0.0/src/hyperlink/_url.py 2021-01-08 06:36:07.000000000 +0100 @@ -183,7 +183,7 @@ _SCHEMELESS_PATH_DELIMS = _ALL_DELIMS - _SCHEMELESS_PATH_SAFE _FRAGMENT_SAFE = _UNRESERVED_CHARS | _PATH_SAFE | set(u"/?") _FRAGMENT_DELIMS = _ALL_DELIMS - _FRAGMENT_SAFE -_QUERY_VALUE_SAFE = _UNRESERVED_CHARS | _FRAGMENT_SAFE - set(u"&+") +_QUERY_VALUE_SAFE = _UNRESERVED_CHARS | _FRAGMENT_SAFE - set(u"&") _QUERY_VALUE_DELIMS = _ALL_DELIMS - _QUERY_VALUE_SAFE _QUERY_KEY_SAFE = _UNRESERVED_CHARS | _QUERY_VALUE_SAFE - set(u"=") _QUERY_KEY_DELIMS = _ALL_DELIMS - _QUERY_KEY_SAFE @@ -467,9 +467,13 @@ ) # As of Mar 11, 2017, there were 44 netloc schemes, and 13 non-netloc +NO_QUERY_PLUS_SCHEMES = set() -def register_scheme(text, uses_netloc=True, default_port=None): - # type: (Text, bool, Optional[int]) -> None + +def register_scheme( + text, uses_netloc=True, default_port=None, query_plus_is_space=True +): + # type: (Text, bool, Optional[int], bool) -> None """Registers new scheme information, resulting in correct port and slash behavior from the URL object. There are dozens of standard schemes preregistered, so this function is mostly meant for @@ -478,13 +482,15 @@ `file an issue`_! Args: - text (Text): A string representation of the scheme. + text: A string representation of the scheme. (the 'http' in 'http://hatnote.com') - uses_netloc (bool): Does the scheme support specifying a + uses_netloc: Does the scheme support specifying a network host? For instance, "http" does, "mailto" does not. Defaults to True. - default_port (Optional[int]): The default port, if any, for + default_port: The default port, if any, for netloc-using schemes. + query_plus_is_space: If true, a "+" in the query string should be + decoded as a space by DecodedURL. .. _file an issue: https://github.com/mahmoud/hyperlink/issues """ @@ -510,6 +516,9 @@ else: raise ValueError("uses_netloc expected bool, not: %r" % uses_netloc) + if not query_plus_is_space: + NO_QUERY_PLUS_SCHEMES.add(text) + return @@ -922,43 +931,41 @@ https://example.com/hello/world The constructor runs basic type checks. All strings are expected - to be decoded (:class:`unicode` in Python 2). All arguments are - optional, defaulting to appropriately empty values. A full list of - constructor arguments is below. + to be text (:class:`str` in Python 3, :class:`unicode` in Python 2). All + arguments are optional, defaulting to appropriately empty values. A full + list of constructor arguments is below. Args: - scheme (Optional[Text]): The text name of the scheme. - host (Optional[Text]): The host portion of the network location - port (Optional[int]): The port part of the network location. If - ``None`` or no port is passed, the port will default to - the default port of the scheme, if it is known. See the - ``SCHEME_PORT_MAP`` and :func:`register_default_port` - for more info. - path (Iterable[Text]): A tuple of strings representing the - slash-separated parts of the path. - query (Sequence[Tuple[Text, Optional[Text]]]): The query parameters, as - a dictionary or as an sequence of key-value pairs. - fragment (Text): The fragment part of the URL. - rooted (bool): A rooted URL is one which indicates an absolute path. - This is True on any URL that includes a host, or any relative URL - that starts with a slash. - userinfo (Text): The username or colon-separated - username:password pair. - uses_netloc (Optional[bool]): Indicates whether ``://`` (the "netloc - separator") will appear to separate the scheme from the *path* in - cases where no host is present. Setting this to ``True`` is a - non-spec-compliant affordance for the common practice of having URIs - that are *not* URLs (cannot have a 'host' part) but nevertheless use - the common ``://`` idiom that most people associate with URLs; - e.g. ``message:`` URIs like ``message://message-id`` being - equivalent to ``message:message-id``. This may be inferred based on - the scheme depending on whether :func:`register_scheme` has been - used to register the scheme and should not be passed directly unless - you know the scheme works like this and you know it has not been - registered. + scheme: The text name of the scheme. + host: The host portion of the network location + port: The port part of the network location. If ``None`` or no port is + passed, the port will default to the default port of the scheme, if + it is known. See the ``SCHEME_PORT_MAP`` and + :func:`register_default_port` for more info. + path: A tuple of strings representing the slash-separated parts of the + path, each percent-encoded. + query: The query parameters, as a dictionary or as an sequence of + percent-encoded key-value pairs. + fragment: The fragment part of the URL. + rooted: A rooted URL is one which indicates an absolute path. + This is True on any URL that includes a host, or any relative URL + that starts with a slash. + userinfo: The username or colon-separated username:password pair. + uses_netloc: Indicates whether ``://`` (the "netloc separator") will + appear to separate the scheme from the *path* in cases where no + host is present. + Setting this to ``True`` is a non-spec-compliant affordance for the + common practice of having URIs that are *not* URLs (cannot have a + 'host' part) but nevertheless use the common ``://`` idiom that + most people associate with URLs; e.g. ``message:`` URIs like + ``message://message-id`` being equivalent to ``message:message-id``. + This may be inferred based on the scheme depending on whether + :func:`register_scheme` has been used to register the scheme and + should not be passed directly unless you know the scheme works like + this and you know it has not been registered. - All of these parts are also exposed as read-only attributes of - URL instances, along with several useful methods. + All of these parts are also exposed as read-only attributes of :class:`URL` + instances, along with several useful methods. .. _RFC 3986: https://tools.ietf.org/html/rfc3986 .. _RFC 3987: https://tools.ietf.org/html/rfc3987 @@ -1187,9 +1194,9 @@ u'user:pass@localhost:8080' Args: - with_password (bool): Whether the return value of this - method include the password in the URL, if it is - set. Defaults to False. + with_password: Whether the return value of this method include the + password in the URL, if it is set. + Defaults to False. Returns: Text: The authority (network location and user information) portion @@ -1298,32 +1305,29 @@ the value on the current URL. Args: - scheme (Optional[Text]): The text name of the scheme. - host (Optional[Text]): The host portion of the network location. - path (Iterable[Text]): A tuple of strings representing the - slash-separated parts of the path. - query (Sequence[Tuple[Text, Optional[Text]]]): The query - parameters, as a dictionary or as an sequence of key-value - pairs. - fragment (Text): The fragment part of the URL. - port (Optional[int]): The port part of the network location. - rooted (Optional[bool]): Whether or not the path begins with a - slash. - userinfo (Text): The username or colon-separated username:password - pair. - uses_netloc (bool): Indicates whether ``://`` (the "netloc - separator") will appear to separate the scheme from the *path* - in cases where no host is present. Setting this to ``True`` is - a non-spec-compliant affordance for the common practice of - having URIs that are *not* URLs (cannot have a 'host' part) but - nevertheless use the common ``://`` idiom that most people - associate with URLs; e.g. ``message:`` URIs like - ``message://message-id`` being equivalent to - ``message:message-id``. This may be inferred based on the - scheme depending on whether :func:`register_scheme` has been - used to register the scheme and should not be passed directly - unless you know the scheme works like this and you know it has - not been registered. + scheme: The text name of the scheme. + host: The host portion of the network location. + path: A tuple of strings representing the slash-separated parts of + the path. + query: The query parameters, as a dictionary or as an sequence of + key-value pairs. + fragment: The fragment part of the URL. + port: The port part of the network location. + rooted: Whether or not the path begins with a slash. + userinfo: The username or colon-separated username:password pair. + uses_netloc: Indicates whether ``://`` (the "netloc separator") + will appear to separate the scheme from the *path* in cases + where no host is present. + Setting this to ``True`` is a non-spec-compliant affordance for + the common practice of having URIs that are *not* URLs (cannot + have a 'host' part) but nevertheless use the common ``://`` + idiom that most people associate with URLs; e.g. ``message:`` + URIs like ``message://message-id`` being equivalent to + ``message:message-id``. + This may be inferred based on the scheme depending on whether + :func:`register_scheme` has been used to register the scheme + and should not be passed directly unless you know the scheme + works like this and you know it has not been registered. Returns: URL: A copy of the current :class:`URL`, with new values for @@ -1363,7 +1367,7 @@ sure to decode those bytestrings. Args: - text (Text): A valid URL string. + text: A valid URL string. Returns: URL: The structured object version of the parsed string. @@ -1469,15 +1473,14 @@ name. Args: - scheme (bool): Convert the scheme to lowercase - host (bool): Convert the host to lowercase - path (bool): Normalize the path (see above for details) - query (bool): Normalize the query string - fragment (bool): Normalize the fragment - userinfo (bool): Normalize the userinfo - percents (bool): Encode isolated percent signs for any - percent-encoded fields which are being normalized - (defaults to True). + scheme: Convert the scheme to lowercase + host: Convert the host to lowercase + path: Normalize the path (see above for details) + query: Normalize the query string + fragment: Normalize the fragment + userinfo: Normalize the userinfo + percents: Encode isolated percent signs for any percent-encoded + fields which are being normalized (defaults to `True`). >>> url = URL.from_text(u'Http://example.COM/a/../b/./c%2f?%61%') >>> print(url.normalize().to_text()) @@ -1537,9 +1540,9 @@ u'http://localhost/a/b/c/d?x=y' Args: - segments (Text): Additional parts to be joined and added to - the path, like :func:`os.path.join`. Special characters - in segments will be percent encoded. + segments: Additional parts to be joined and added to the path, like + :func:`os.path.join`. Special characters in segments will be + percent encoded. Returns: URL: A copy of the current URL with the extra path segments. @@ -1562,7 +1565,7 @@ sibling of this URL path. Args: - segment (Text): A single path segment. + segment: A single path segment. Returns: URL: A copy of the current URL with the last path segment @@ -1861,11 +1864,11 @@ URL.from_text(u'https://example.com/?x=y&x=z') Args: - name (Text): The name of the query parameter to add. + name: The name of the query parameter to add. The part before the ``=``. - value (Optional[Text]): The value of the query parameter to add. - The part after the ``=``. Defaults to ``None``, meaning no - value. + value: The value of the query parameter to add. + The part after the ``=``. + Defaults to ``None``, meaning no value. Returns: URL: A new :class:`URL` instance with the parameter added. @@ -1884,11 +1887,11 @@ URL.from_text(u'https://example.com/?x=z') Args: - name (Text): The name of the query parameter to set. + name: The name of the query parameter to set. The part before the ``=``. - value (Optional[Text]): The value of the query parameter to set. - The part after the ``=``. Defaults to ``None``, meaning no - value. + value: The value of the query parameter to set. + The part after the ``=``. + Defaults to ``None``, meaning no value. Returns: URL: A new :class:`URL` instance with the parameter set. @@ -1915,7 +1918,7 @@ list is always returned, and this method raises no exceptions. Args: - name (Text): The name of the query parameter to get. + name: The name of the query parameter to get. Returns: List[Optional[Text]]: A list of all the values associated with the @@ -1936,12 +1939,11 @@ parameter is not already set. Args: - name (Text): The name of the query parameter to remove. - value (Text): Optional value to additionally filter on. + name: The name of the query parameter to remove. + value: Optional value to additionally filter on. Setting this removes query parameters which match both name and value. - limit (Optional[int]): Optional maximum number of parameters to - remove. + limit: Optional maximum number of parameters to remove. Returns: URL: A new :class:`URL` instance with the parameter removed. @@ -1976,6 +1978,16 @@ _EMPTY_URL = URL() +def _replace_plus(text): + # type: (Text) -> Text + return text.replace("+", "%20") + + +def _no_op(text): + # type: (Text) -> Text + return text + + class DecodedURL(object): """ :class:`DecodedURL` is a type designed to act as a higher-level @@ -2001,9 +2013,13 @@ special characters encoded with codecs other than UTF-8. Args: - url (URL): A :class:`URL` object to wrap. - lazy (bool): Set to True to avoid pre-decode all parts of the URL to - check for validity. Defaults to False. + url: A :class:`URL` object to wrap. + lazy: Set to True to avoid pre-decode all parts of the URL to check for + validity. + Defaults to False. + query_plus_is_space: + characters in the query string should be treated + as spaces when decoding. If unspecified, the default is taken from + the scheme. .. note:: @@ -2018,9 +2034,12 @@ .. versionadded:: 18.0.0 """ - def __init__(self, url=_EMPTY_URL, lazy=False): - # type: (URL, bool) -> None + def __init__(self, url=_EMPTY_URL, lazy=False, query_plus_is_space=None): + # type: (URL, bool, Optional[bool]) -> None self._url = url + if query_plus_is_space is None: + query_plus_is_space = url.scheme not in NO_QUERY_PLUS_SCHEMES + self._query_plus_is_space = query_plus_is_space if not lazy: # cache the following, while triggering any decoding # issues with decodable fields @@ -2028,18 +2047,19 @@ return @classmethod - def from_text(cls, text, lazy=False): - # type: (Text, bool) -> DecodedURL + def from_text(cls, text, lazy=False, query_plus_is_space=None): + # type: (Text, bool, Optional[bool]) -> DecodedURL """\ Make a `DecodedURL` instance from any text string containing a URL. Args: - text (Text): Text containing the URL - lazy (bool): Whether to pre-decode all parts of the URL to check for - validity. Defaults to True. + text: Text containing the URL + lazy: Whether to pre-decode all parts of the URL to check for + validity. + Defaults to True. """ _url = URL.from_text(text) - return cls(_url, lazy=lazy) + return cls(_url, lazy=lazy, query_plus_is_space=query_plus_is_space) @property def encoded_url(self): @@ -2064,6 +2084,14 @@ "Passthrough to :meth:`~hyperlink.URL.to_iri()`" return self._url.to_iri() + def _clone(self, url): + # type: (URL) -> DecodedURL + return self.__class__( + url, + # TODO: propagate laziness? + query_plus_is_space=self._query_plus_is_space, + ) + def click(self, href=u""): # type: (Union[Text, URL, DecodedURL]) -> DecodedURL """Return a new DecodedURL wrapping the result of @@ -2071,7 +2099,9 @@ """ if isinstance(href, DecodedURL): href = href._url - return self.__class__(self._url.click(href=href)) + return self._clone( + self._url.click(href=href), + ) def sibling(self, segment): # type: (Text) -> DecodedURL @@ -2079,7 +2109,9 @@ return a new `DecodedURL` wrapping the result of :meth:`~hyperlink.URL.sibling()` """ - return self.__class__(self._url.sibling(_encode_reserved(segment))) + return self._clone( + self._url.sibling(_encode_reserved(segment)), + ) def child(self, *segments): # type: (Text) -> DecodedURL @@ -2090,7 +2122,7 @@ if not segments: return self new_segs = [_encode_reserved(s) for s in segments] - return self.__class__(self._url.child(*new_segs)) + return self._clone(self._url.child(*new_segs)) def normalize( self, @@ -2106,7 +2138,7 @@ """Return a new `DecodedURL` wrapping the result of :meth:`~hyperlink.URL.normalize()` """ - return self.__class__( + return self._clone( self._url.normalize( scheme, host, path, query, fragment, userinfo, percents ) @@ -2153,11 +2185,18 @@ def query(self): # type: () -> QueryPairs if not hasattr(self, "_query"): + if self._query_plus_is_space: + predecode = _replace_plus + else: + predecode = _no_op + self._query = cast( QueryPairs, tuple( tuple( - _percent_decode(x, raise_subencoding_exc=True) + _percent_decode( + predecode(x), raise_subencoding_exc=True + ) if x is not None else None for x in (k, v) @@ -2253,7 +2292,7 @@ userinfo=userinfo_text, uses_netloc=uses_netloc, ) - return self.__class__(url=new_url) + return self._clone(url=new_url) def get(self, name): # type: (Text) -> List[Optional[Text]] @@ -2386,16 +2425,16 @@ https://github.com/python-hyper/hyperlink Args: - url (str): A text string representation of a URL. + url: A text string representation of a URL. - decoded (bool): Whether or not to return a :class:`DecodedURL`, + decoded: Whether or not to return a :class:`DecodedURL`, which automatically handles all encoding/decoding/quoting/unquoting for all the various accessors of parts of the URL, or a :class:`URL`, which has the same API, but requires handling of special characters for different parts of the URL. - lazy (bool): In the case of `decoded=True`, this controls + lazy: In the case of `decoded=True`, this controls whether the URL is decoded immediately or as accessed. The default, `lazy=False`, checks all encoded parts of the URL for decodability. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hyperlink-20.0.1/src/hyperlink/hypothesis.py new/hyperlink-21.0.0/src/hyperlink/hypothesis.py --- old/hyperlink-20.0.1/src/hyperlink/hypothesis.py 2020-08-04 08:14:29.000000000 +0200 +++ new/hyperlink-21.0.0/src/hyperlink/hypothesis.py 2021-01-08 06:36:07.000000000 +0100 @@ -78,7 +78,8 @@ ) with open_gzip(dataFileName) as dataFile: reader = csv_reader( - (line.decode("utf-8") for line in dataFile), delimiter=",", + (line.decode("utf-8") for line in dataFile), + delimiter=",", ) next(reader) # Skip header row for row in reader: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hyperlink-20.0.1/src/hyperlink/test/common.py new/hyperlink-21.0.0/src/hyperlink/test/common.py --- old/hyperlink-20.0.1/src/hyperlink/test/common.py 2020-08-04 08:14:29.000000000 +0200 +++ new/hyperlink-21.0.0/src/hyperlink/test/common.py 2021-01-08 06:36:07.000000000 +0100 @@ -16,26 +16,26 @@ ): # type: (...) -> Any """Fail unless an exception of class expected_exception is raised - by callableObj when invoked with arguments args and keyword - arguments kwargs. If a different type of exception is - raised, it will not be caught, and the test case will be - deemed to have suffered an error, exactly as for an - unexpected exception. + by callableObj when invoked with arguments args and keyword + arguments kwargs. If a different type of exception is + raised, it will not be caught, and the test case will be + deemed to have suffered an error, exactly as for an + unexpected exception. - If called with callableObj omitted or None, will return a - context object used like this:: + If called with callableObj omitted or None, will return a + context object used like this:: - with self.assertRaises(SomeException): - do_something() + with self.assertRaises(SomeException): + do_something() - The context manager keeps a reference to the exception as - the 'exception' attribute. This allows you to inspect the - exception after the assertion:: + The context manager keeps a reference to the exception as + the 'exception' attribute. This allows you to inspect the + exception after the assertion:: - with self.assertRaises(SomeException) as cm: - do_something() - the_exception = cm.exception - self.assertEqual(the_exception.error_code, 3) + with self.assertRaises(SomeException) as cm: + do_something() + the_exception = cm.exception + self.assertEqual(the_exception.error_code, 3) """ context = _AssertRaisesContext(expected_exception, self) if callableObj is None: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hyperlink-20.0.1/src/hyperlink/test/test_common.py new/hyperlink-21.0.0/src/hyperlink/test/test_common.py --- old/hyperlink-20.0.1/src/hyperlink/test/test_common.py 2020-08-04 08:14:29.000000000 +0200 +++ new/hyperlink-21.0.0/src/hyperlink/test/test_common.py 2021-01-08 06:36:07.000000000 +0100 @@ -7,15 +7,11 @@ class _ExpectedException(Exception): - """An exception used to test HyperlinkTestCase.assertRaises. - - """ + """An exception used to test HyperlinkTestCase.assertRaises.""" class _UnexpectedException(Exception): - """An exception used to test HyperlinkTestCase.assertRaises. - - """ + """An exception used to test HyperlinkTestCase.assertRaises.""" class TestHyperlink(TestCase): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hyperlink-20.0.1/src/hyperlink/test/test_decoded_url.py new/hyperlink-21.0.0/src/hyperlink/test/test_decoded_url.py --- old/hyperlink-20.0.1/src/hyperlink/test/test_decoded_url.py 2020-08-04 08:14:29.000000000 +0200 +++ new/hyperlink-21.0.0/src/hyperlink/test/test_decoded_url.py 2021-01-08 06:36:07.000000000 +0100 @@ -210,3 +210,19 @@ assert clicked.host == durl.host assert clicked.path == durl_dest.path assert clicked.path == ("t??st",) + + def test_decode_plus(self): + # type: () -> None + durl = DecodedURL.from_text("/x+y%2B?a=b+c%2B") + assert durl.path == ("x+y+",) + assert durl.get("a") == ["b c+"] + assert durl.query == (("a", "b c+"),) + + def test_decode_nonplussed(self): + # type: () -> None + durl = DecodedURL.from_text( + "/x+y%2B?a=b+c%2B", query_plus_is_space=False + ) + assert durl.path == ("x+y+",) + assert durl.get("a") == ["b+c+"] + assert durl.query == (("a", "b+c+"),) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hyperlink-20.0.1/src/hyperlink/test/test_scheme_registration.py new/hyperlink-21.0.0/src/hyperlink/test/test_scheme_registration.py --- old/hyperlink-20.0.1/src/hyperlink/test/test_scheme_registration.py 2020-08-04 08:14:29.000000000 +0200 +++ new/hyperlink-21.0.0/src/hyperlink/test/test_scheme_registration.py 2021-01-08 06:36:07.000000000 +0100 @@ -5,7 +5,7 @@ from .. import _url from .common import HyperlinkTestCase -from .._url import register_scheme, URL +from .._url import register_scheme, URL, DecodedURL class TestSchemeRegistration(HyperlinkTestCase): @@ -70,3 +70,13 @@ # type: () -> None with self.assertRaises(ValueError): register_scheme("nope", default_port=cast(bool, object())) + + def test_register_no_quote_plus_scheme(self): + # type: () -> None + register_scheme("keepplus", query_plus_is_space=False) + plus_is_not_space = DecodedURL.from_text( + "keepplus://example.com/?q=a+b" + ) + plus_is_space = DecodedURL.from_text("https://example.com/?q=a+b") + assert plus_is_not_space.get("q") == ["a+b"] + assert plus_is_space.get("q") == ["a b"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hyperlink-20.0.1/src/hyperlink/test/test_url.py new/hyperlink-21.0.0/src/hyperlink/test/test_url.py --- old/hyperlink-20.0.1/src/hyperlink/test/test_url.py 2020-08-04 08:14:29.000000000 +0200 +++ new/hyperlink-21.0.0/src/hyperlink/test/test_url.py 2021-01-08 06:36:07.000000000 +0100 @@ -133,6 +133,8 @@ "https://example.com/?a=%23", # hash in query param value "https://example.com/?a=%26", # ampersand in query param value "https://example.com/?a=%3D", # equals in query param value + "https://example.com/?foo+bar=baz", # plus in query param name + "https://example.com/?foo=bar+baz", # plus in query param value # double-encoded percent sign in all percent-encodable positions: "http://(%2525):(%2525)@example.com/(%2525)/?(%2525)=(%2525)#(%2525)", # colon in first part of schemeless relative url diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hyperlink-20.0.1/src/hyperlink.egg-info/PKG-INFO new/hyperlink-21.0.0/src/hyperlink.egg-info/PKG-INFO --- old/hyperlink-20.0.1/src/hyperlink.egg-info/PKG-INFO 2020-08-05 05:34:31.000000000 +0200 +++ new/hyperlink-21.0.0/src/hyperlink.egg-info/PKG-INFO 2021-01-08 06:51:20.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.2 Name: hyperlink -Version: 20.0.1 +Version: 21.0.0 Summary: A featureful, immutable, and correct URL for Python. Home-page: https://github.com/python-hyper/hyperlink Author: Mahmoud Hashemi and Glyph Lefkowitz @@ -28,6 +28,7 @@ Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: License :: OSI Approved :: MIT License Requires-Python: >=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hyperlink-20.0.1/tox.ini new/hyperlink-21.0.0/tox.ini --- old/hyperlink-20.0.1/tox.ini 2020-08-04 08:14:29.000000000 +0200 +++ new/hyperlink-21.0.0/tox.ini 2021-01-08 06:36:07.000000000 +0100 @@ -48,7 +48,7 @@ {[default]deps} # In Python 2, we need to pull in typing, mock - py{26,27,py2}: typing==3.7.4.1 + py{26,27,py2}: typing==3.7.4.3 py{26,27,py2}: mock==3.0.5 # rq.filter: <4 # For pytest @@ -58,7 +58,7 @@ # For code coverage {[testenv:coverage_report]deps} py{26,27,34,py2}: pytest-cov==2.8.1 # rq.filter: <2.9 - py{35,36,37,38,39,py3}: pytest-cov==2.10.0 + py{35,36,37,38,39,py3}: pytest-cov==2.10.1 # For hypothesis. Note Python 3.4 isn't supported by hypothesis. py{26,27,py2}: hypothesis==4.43.9 # rq.filter: <4.44 @@ -89,7 +89,7 @@ skip_install = True deps = - black==19.10b0 + black==20.8b1 setenv = BLACK_LINT_ARGS=--check @@ -120,14 +120,13 @@ skip_install = True deps = - flake8-bugbear==20.1.4 - flake8==3.8.3 + flake8-bugbear==20.11.1 + flake8==3.8.4 mccabe==0.6.1 pep8-naming==0.11.1 pycodestyle==2.6.0 - pydocstyle==5.0.2 - # pin pyflakes pending a release with https://github.com/PyCQA/pyflakes/pull/455 - git+git://github.com/PyCQA/pyflakes@ffe9386#egg=pyflakes + pydocstyle==5.1.1 + pyflakes==2.2.0 commands = flake8 {posargs:setup.py src/{env:PY_MODULE}} @@ -184,7 +183,7 @@ basepython = {[default]basepython} deps = - mypy==0.782 + mypy==0.790 {[default]deps} @@ -247,7 +246,8 @@ skip_install = True deps = - coverage==4.5.4 # rq.filter: <5 # coverage 5.0 drops Python 3.4 support + # coverage 5.0 drops Python 3.4 support + coverage==4.5.4 # rq.filter: <5 setenv = {[default]setenv} @@ -276,7 +276,7 @@ deps = {[testenv:coverage_report]deps} - codecov==2.1.7 + codecov==2.1.11 passenv = # See https://github.com/codecov/codecov-python/blob/master/README.md#using-tox @@ -318,8 +318,8 @@ basepython = {[default]basepython} deps = - Sphinx==2.4.4 - sphinx-rtd-theme==0.5.0 + Sphinx==3.4.3 + sphinx-rtd-theme==0.5.1 commands = sphinx-build \ @@ -336,7 +336,7 @@ deps = {[testenv:docs]deps} - sphinx-autobuild==0.7.1 + sphinx-autobuild==2020.9.1 commands = sphinx-autobuild \ @@ -359,9 +359,9 @@ skip_install = True deps = - check-manifest==0.42 - readme-renderer==26.0 - twine==3.1.1 + check-manifest==0.46 + readme-renderer==28.0 + twine==3.3.0 commands = check-manifest