diff options
author | AlexSm <alex@ydb.tech> | 2023-12-22 17:10:22 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-12-22 17:10:22 +0100 |
commit | 148f920350c60c0ca2d89b637a5aea9093eee450 (patch) | |
tree | 6314b1433dac833398c333731e83f0ad77e81a0b /contrib/python/yarl | |
parent | 7116d46ae7c0259b5f9d489de263f8701e432b1c (diff) | |
download | ydb-148f920350c60c0ca2d89b637a5aea9093eee450.tar.gz |
Library import 2 (#639)
Diffstat (limited to 'contrib/python/yarl')
-rw-r--r-- | contrib/python/yarl/.dist-info/METADATA | 104 | ||||
-rw-r--r-- | contrib/python/yarl/README.rst | 14 | ||||
-rw-r--r-- | contrib/python/yarl/tests/test_cached_property.py | 33 | ||||
-rw-r--r-- | contrib/python/yarl/tests/test_quoting.py | 21 | ||||
-rw-r--r-- | contrib/python/yarl/tests/test_update_query.py | 2 | ||||
-rw-r--r-- | contrib/python/yarl/tests/test_url.py | 159 | ||||
-rw-r--r-- | contrib/python/yarl/tests/test_url_build.py | 74 | ||||
-rw-r--r-- | contrib/python/yarl/tests/test_url_parsing.py | 18 | ||||
-rw-r--r-- | contrib/python/yarl/tests/test_url_update_netloc.py | 20 | ||||
-rw-r--r-- | contrib/python/yarl/ya.make | 2 | ||||
-rw-r--r-- | contrib/python/yarl/yarl/__init__.py | 2 | ||||
-rw-r--r-- | contrib/python/yarl/yarl/_quoting_c.pyx | 8 | ||||
-rw-r--r-- | contrib/python/yarl/yarl/_url.py | 4 |
13 files changed, 282 insertions, 179 deletions
diff --git a/contrib/python/yarl/.dist-info/METADATA b/contrib/python/yarl/.dist-info/METADATA index 8585ab738d..548567bd12 100644 --- a/contrib/python/yarl/.dist-info/METADATA +++ b/contrib/python/yarl/.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: yarl -Version: 1.9.3 +Version: 1.9.4 Summary: Yet another URL library Home-page: https://github.com/aio-libs/yarl Author: Andrew Svetlov @@ -116,9 +116,9 @@ automatically encoded giving canonical representation as result: .. code-block:: pycon - >>> url = URL('https://www.python.org/путь') + >>> url = URL('https://www.python.org/шлях') >>> url - URL('https://www.python.org/%D0%BF%D1%83%D1%82%D1%8C') + URL('https://www.python.org/%D1%88%D0%BB%D1%8F%D1%85') Regular properties are *percent-decoded*, use ``raw_`` versions for getting *encoded* strings: @@ -126,17 +126,17 @@ getting *encoded* strings: .. code-block:: pycon >>> url.path - '/путь' + '/шлях' >>> url.raw_path - '/%D0%BF%D1%83%D1%82%D1%8C' + '/%D1%88%D0%BB%D1%8F%D1%85' Human readable representation of URL is available as ``.human_repr()``: .. code-block:: pycon >>> url.human_repr() - 'https://www.python.org/путь' + 'https://www.python.org/шлях' For full documentation please read https://yarl.aio-libs.org. @@ -157,12 +157,12 @@ used with our wheels) the the tarball will be used to compile the library from the source code. It requires a C compiler and and Python headers installed. To skip the compilation you must explicitly opt-in by using a PEP 517 -configuration setting ``--pure-python``, or setting the ``YARL_NO_EXTENSIONS`` +configuration setting ``pure-python``, or setting the ``YARL_NO_EXTENSIONS`` environment variable to a non-empty value, e.g.: .. code-block:: console - $ pip install yarl --config-settings=--pure-python= + $ pip install yarl --config-settings=pure-python=false Please note that the pure-Python (uncompiled) version is much slower. However, PyPy always uses a pure-Python implementation, and, as such, it is unaffected @@ -262,13 +262,83 @@ It's *Apache 2* licensed and freely available. .. towncrier release notes start +1.9.4 (2023-12-06) +================== + +Bug fixes +--------- + +- Started raising ``TypeError`` when a string value is passed into + ``yarl.URL.build()`` as the ``port`` argument -- by `@commonism <https://github.com/sponsors/commonism>`__. + + Previously the empty string as port would create malformed URLs when rendered as string representations. (`#883 <https://github.com/aio-libs/yarl/issues/883>`__) + + +Packaging updates and notes for downstreams +------------------------------------------- + +- The leading ``--`` has been dropped from the `PEP 517 <https://peps.python.org/pep-517>`__ in-tree build + backend config setting names. ``--pure-python`` is now just ``pure-python`` + -- by `@webknjaz <https://github.com/sponsors/webknjaz>`__. + + The usage now looks as follows: + + .. code-block:: console + + $ python -m build \ + --config-setting=pure-python=true \ + --config-setting=with-cython-tracing=true + + (`#963 <https://github.com/aio-libs/yarl/issues/963>`__) + + +Contributor-facing changes +-------------------------- + +- A step-by-step ``Release Guide`` guide has + been added, describing how to release *yarl* -- by `@webknjaz <https://github.com/sponsors/webknjaz>`__. + + This is primarily targeting maintainers. (`#960 <https://github.com/aio-libs/yarl/issues/960>`__) +- Coverage collection has been implemented for the Cython modules + -- by `@webknjaz <https://github.com/sponsors/webknjaz>`__. + + It will also be reported to Codecov from any non-release CI jobs. + + To measure coverage in a development environment, *yarl* can be + installed in editable mode, which requires an environment variable + ``YARL_CYTHON_TRACING=1`` to be set: + + .. code-block:: console + + $ YARL_CYTHON_TRACING=1 python -Im pip install -e . + + Editable install produces C-files required for the Cython coverage + plugin to map the measurements back to the PYX-files. (`#961 <https://github.com/aio-libs/yarl/issues/961>`__) +- It is now possible to request line tracing in Cython builds using the + ``with-cython-tracing`` `PEP 517 <https://peps.python.org/pep-517>`__ config setting + -- `@webknjaz <https://github.com/sponsors/webknjaz>`__. + + This can be used in CI and development environment to measure coverage + on Cython modules, but is not normally useful to the end-users or + downstream packagers. + + Here's a usage example: + + .. code-block:: console + + $ python -Im pip install . --config-settings=with-cython-tracing=true + + For editable installs, this setting is on by default. Otherwise, it's + off unless requested explicitly. (`#962 <https://github.com/aio-libs/yarl/issues/962>`__) + + 1.9.3 (2023-11-20) ================== Bug fixes --------- -- Stopped dropping trailing slashes in ``yarl.URL.joinpath()`` -- by `@mjpieters <https://github.com/sponsors/mjpieters>`__. (`#862 <https://github.com/aio-libs/yarl/issues/862>`__, `#866 <https://github.com/aio-libs/yarl/issues/866>`__) +- Stopped dropping trailing slashes in ``yarl.URL.joinpath()`` -- by `@gmacon <https://github.com/sponsors/gmacon>`__. (`#862 <https://github.com/aio-libs/yarl/issues/862>`__, `#866 <https://github.com/aio-libs/yarl/issues/866>`__) - Started accepting string subclasses in ``__truediv__()`` operations (``URL / segment``) -- by `@mjpieters <https://github.com/sponsors/mjpieters>`__. (`#871 <https://github.com/aio-libs/yarl/issues/871>`__, `#884 <https://github.com/aio-libs/yarl/issues/884>`__) - Fixed the human representation of URLs with square brackets in usernames and passwords -- by `@mjpieters <https://github.com/sponsors/mjpieters>`__. (`#876 <https://github.com/aio-libs/yarl/issues/876>`__, `#882 <https://github.com/aio-libs/yarl/issues/882>`__) - Updated type hints to include ``URL.missing_port()``, ``URL.__bytes__()`` @@ -280,12 +350,12 @@ Packaging updates and notes for downstreams ------------------------------------------- - Integrated Cython 3 to enable building *yarl* under Python 3.12 -- by `@mjpieters <https://github.com/sponsors/mjpieters>`__. (`#829 <https://github.com/aio-libs/yarl/issues/829>`__, `#881 <https://github.com/aio-libs/yarl/issues/881>`__) -- Declared modern ``setuptools.build_meta`` as the ``517`` build +- Declared modern ``setuptools.build_meta`` as the `PEP 517 <https://peps.python.org/pep-517>`__ build backend in ``pyproject.toml`` explicitly -- by `@webknjaz <https://github.com/sponsors/webknjaz>`__. (`#886 <https://github.com/aio-libs/yarl/issues/886>`__) - Converted most of the packaging setup into a declarative ``setup.cfg`` config -- by `@webknjaz <https://github.com/sponsors/webknjaz>`__. (`#890 <https://github.com/aio-libs/yarl/issues/890>`__) -- Replaced the packaging is replaced from an old-fashioned ``setup.py`` to an - in-tree ``517`` build backend -- by `@webknjaz <https://github.com/sponsors/webknjaz>`__. +- The packaging is replaced from an old-fashioned ``setup.py`` to an + in-tree `PEP 517 <https://peps.python.org/pep-517>`__ build backend -- by `@webknjaz <https://github.com/sponsors/webknjaz>`__. Whenever the end-users or downstream packagers need to build ``yarl`` from source (a Git checkout or an sdist), they may pass a ``config_settings`` @@ -296,7 +366,7 @@ Packaging updates and notes for downstreams .. code-block:: console - $ python -m pip install . --config-settings=--pure-python= + $ python -m pip install . --config-settings=--pure-python=false This will also work with ``-e | --editable``. @@ -304,10 +374,16 @@ Packaging updates and notes for downstreams .. code-block:: console - $ python -m build --config-setting=--pure-python= + $ python -m build --config-setting=--pure-python=false Adding ``-w | --wheel`` can force ``pypa/build`` produce a wheel from source directly, as opposed to building an ``sdist`` and then building from it. (`#893 <https://github.com/aio-libs/yarl/issues/893>`__) + + .. attention:: + + v1.9.3 was the only version using the ``--pure-python`` setting name. + Later versions dropped the ``--`` prefix, making it just ``pure-python``. + - Declared Python 3.12 supported officially in the distribution package metadata -- by `@edgarrmondragon <https://github.com/sponsors/edgarrmondragon>`__. (`#942 <https://github.com/aio-libs/yarl/issues/942>`__) diff --git a/contrib/python/yarl/README.rst b/contrib/python/yarl/README.rst index a1032b206a..844ffff692 100644 --- a/contrib/python/yarl/README.rst +++ b/contrib/python/yarl/README.rst @@ -74,9 +74,9 @@ automatically encoded giving canonical representation as result: .. code-block:: pycon - >>> url = URL('https://www.python.org/путь') + >>> url = URL('https://www.python.org/шлях') >>> url - URL('https://www.python.org/%D0%BF%D1%83%D1%82%D1%8C') + URL('https://www.python.org/%D1%88%D0%BB%D1%8F%D1%85') Regular properties are *percent-decoded*, use ``raw_`` versions for getting *encoded* strings: @@ -84,17 +84,17 @@ getting *encoded* strings: .. code-block:: pycon >>> url.path - '/путь' + '/шлях' >>> url.raw_path - '/%D0%BF%D1%83%D1%82%D1%8C' + '/%D1%88%D0%BB%D1%8F%D1%85' Human readable representation of URL is available as ``.human_repr()``: .. code-block:: pycon >>> url.human_repr() - 'https://www.python.org/путь' + 'https://www.python.org/шлях' For full documentation please read https://yarl.aio-libs.org. @@ -115,12 +115,12 @@ used with our wheels) the the tarball will be used to compile the library from the source code. It requires a C compiler and and Python headers installed. To skip the compilation you must explicitly opt-in by using a PEP 517 -configuration setting ``--pure-python``, or setting the ``YARL_NO_EXTENSIONS`` +configuration setting ``pure-python``, or setting the ``YARL_NO_EXTENSIONS`` environment variable to a non-empty value, e.g.: .. code-block:: console - $ pip install yarl --config-settings=--pure-python= + $ pip install yarl --config-settings=pure-python=false Please note that the pure-Python (uncompiled) version is much slower. However, PyPy always uses a pure-Python implementation, and, as such, it is unaffected diff --git a/contrib/python/yarl/tests/test_cached_property.py b/contrib/python/yarl/tests/test_cached_property.py index 5dcb5ece23..834f6db437 100644 --- a/contrib/python/yarl/tests/test_cached_property.py +++ b/contrib/python/yarl/tests/test_cached_property.py @@ -3,42 +3,27 @@ import pytest from yarl._url import cached_property -def test_reify(): - class A: - def __init__(self): - self._cache = {} +class A: + def __init__(self): + self._cache = {} + + @cached_property + def prop(self): + """Docstring.""" + return 1 - @cached_property - def prop(self): - return 1 +def test_reify(): a = A() assert 1 == a.prop def test_reify_class(): - class A: - def __init__(self): - self._cache = {} - - @cached_property - def prop(self): - """Docstring.""" - return 1 - assert isinstance(A.prop, cached_property) assert "Docstring." == A.prop.__doc__ def test_reify_assignment(): - class A: - def __init__(self): - self._cache = {} - - @cached_property - def prop(self): - return 1 - a = A() with pytest.raises(AttributeError): diff --git a/contrib/python/yarl/tests/test_quoting.py b/contrib/python/yarl/tests/test_quoting.py index 7ebc0f9b04..d9b6ae8e4b 100644 --- a/contrib/python/yarl/tests/test_quoting.py +++ b/contrib/python/yarl/tests/test_quoting.py @@ -226,14 +226,21 @@ def test_unquoting_bad_percent_escapes(unquoter, input, expected): assert unquoter()(input) == expected -@pytest.mark.xfail -# FIXME: After conversion to bytes, should not cause UTF-8 decode fail. -# See https://url.spec.whatwg.org/#percent-encoded-bytes -def test_unquoting_invalid_utf8_sequence(unquoter): - with pytest.raises(ValueError): - unquoter()("%AB") +@pytest.mark.xfail( + reason=""" + FIXME: After conversion to bytes, should not cause UTF-8 decode fail. + See https://url.spec.whatwg.org/#percent-encoded-bytes + + Refs: + * https://github.com/aio-libs/yarl/pull/216 + * https://github.com/aio-libs/yarl/pull/214 + * https://github.com/aio-libs/yarl/pull/7 + """, +) +@pytest.mark.parametrize("urlencoded_string", ("%AB", "%AB%AB")) +def test_unquoting_invalid_utf8_sequence(unquoter, urlencoded_string): with pytest.raises(ValueError): - unquoter()("%AB%AB") + unquoter()(urlencoded_string) def test_unquoting_mixed_case_percent_escapes(unquoter): diff --git a/contrib/python/yarl/tests/test_update_query.py b/contrib/python/yarl/tests/test_update_query.py index e47c468341..176259d750 100644 --- a/contrib/python/yarl/tests/test_update_query.py +++ b/contrib/python/yarl/tests/test_update_query.py @@ -171,7 +171,7 @@ class _CStr(str): class _EmptyStrEr: def __str__(self): - return "" + return "" # pragma: no cover # <-- this should never happen class _CInt(int, _EmptyStrEr): diff --git a/contrib/python/yarl/tests/test_url.py b/contrib/python/yarl/tests/test_url.py index af13d0b5d5..59d543754d 100644 --- a/contrib/python/yarl/tests/test_url.py +++ b/contrib/python/yarl/tests/test_url.py @@ -64,8 +64,8 @@ def test_origin(): def test_origin_nonascii(): - url = URL("http://user:password@историк.рф:8888/path/to?a=1&b=2") - assert str(url.origin()) == "http://xn--h1aagokeh.xn--p1ai:8888" + url = URL("http://user:password@оун-упа.укр:8888/path/to?a=1&b=2") + assert str(url.origin()) == "http://xn----8sb1bdhvc.xn--j1amh:8888" def test_origin_ipv6(): @@ -117,8 +117,8 @@ def test_raw_user(): def test_raw_user_non_ascii(): - url = URL("http://вася@example.com") - assert "%D0%B2%D0%B0%D1%81%D1%8F" == url.raw_user + url = URL("http://бажан@example.com") + assert "%D0%B1%D0%B0%D0%B6%D0%B0%D0%BD" == url.raw_user def test_no_user(): @@ -127,8 +127,8 @@ def test_no_user(): def test_user_non_ascii(): - url = URL("http://вася@example.com") - assert "вася" == url.user + url = URL("http://бажан@example.com") + assert "бажан" == url.user def test_raw_password(): @@ -164,13 +164,13 @@ def test_raw_host(): def test_raw_host_non_ascii(): - url = URL("http://историк.рф") - assert "xn--h1aagokeh.xn--p1ai" == url.raw_host + url = URL("http://оун-упа.укр") + assert "xn----8sb1bdhvc.xn--j1amh" == url.raw_host def test_host_non_ascii(): - url = URL("http://историк.рф") - assert "историк.рф" == url.host + url = URL("http://оун-упа.укр") + assert "оун-упа.укр" == url.host def test_localhost(): @@ -210,12 +210,13 @@ def test_authority_short() -> None: def test_authority_full_nonasci() -> None: - url = URL("http://ваня:пароль@айдеко.рф:8080/path") + url = URL("http://степан:пароль@слава.укр:8080/path") assert url.raw_authority == ( - "%D0%B2%D0%B0%D0%BD%D1%8F:%D0%BF%D0%B0%D1%80%D0%BE%D0%BB%D1%8C@" - "xn--80aidohy.xn--p1ai:8080" + "%D1%81%D1%82%D0%B5%D0%BF%D0%B0%D0%BD:" + "%D0%BF%D0%B0%D1%80%D0%BE%D0%BB%D1%8C@" + "xn--80aaf8a3a.xn--j1amh:8080" ) - assert url.authority == "ваня:пароль@айдеко.рф:8080" + assert url.authority == "степан:пароль@слава.укр:8080" def test_lowercase(): @@ -225,9 +226,9 @@ def test_lowercase(): def test_lowercase_nonascii(): - url = URL("http://Айдеко.Рф") - assert url.raw_host == "xn--80aidohy.xn--p1ai" - assert url.host == "айдеко.рф" + url = URL("http://Слава.Укр") + assert url.raw_host == "xn--80aaf8a3a.xn--j1amh" + assert url.host == "слава.укр" def test_compressed_ipv6(): @@ -294,13 +295,13 @@ def test_raw_path(): def test_raw_path_non_ascii(): - url = URL("http://example.com/путь/сюда") - assert "/%D0%BF%D1%83%D1%82%D1%8C/%D1%81%D1%8E%D0%B4%D0%B0" == url.raw_path + url = URL("http://example.com/шлях/сюди") + assert "/%D1%88%D0%BB%D1%8F%D1%85/%D1%81%D1%8E%D0%B4%D0%B8" == url.raw_path def test_path_non_ascii(): - url = URL("http://example.com/путь/сюда") - assert "/путь/сюда" == url.path + url = URL("http://example.com/шлях/сюди") + assert "/шлях/сюди" == url.path def test_path_with_spaces(): @@ -352,8 +353,8 @@ def test_raw_path_qs(): assert url.raw_path_qs == "/?%D0%B1=%D0%B2&%D1%8E=%D0%BA" url = URL("http://example.com/path?б=в&ю=к") assert url.raw_path_qs == "/path?%D0%B1=%D0%B2&%D1%8E=%D0%BA" - url = URL("http://example.com/путь?a=1&b=2") - assert url.raw_path_qs == "/%D0%BF%D1%83%D1%82%D1%8C?a=1&b=2" + url = URL("http://example.com/шлях?a=1&b=2") + assert url.raw_path_qs == "/%D1%88%D0%BB%D1%8F%D1%85?a=1&b=2" def test_query_string_spaces(): @@ -375,8 +376,8 @@ def test_raw_fragment(): def test_raw_fragment_non_ascii(): - url = URL("http://example.com/path#якорь") - assert "%D1%8F%D0%BA%D0%BE%D1%80%D1%8C" == url.raw_fragment + url = URL("http://example.com/path#якір") + assert "%D1%8F%D0%BA%D1%96%D1%80" == url.raw_fragment def test_raw_fragment_safe(): @@ -385,8 +386,8 @@ def test_raw_fragment_safe(): def test_fragment_non_ascii(): - url = URL("http://example.com/path#якорь") - assert "якорь" == url.fragment + url = URL("http://example.com/path#якір") + assert "якір" == url.fragment def test_raw_parts_empty(): @@ -435,17 +436,17 @@ def test_parts_for_empty_url(): def test_raw_parts_non_ascii(): - url = URL("http://example.com/путь/сюда") + url = URL("http://example.com/шлях/сюди") assert ( "/", - "%D0%BF%D1%83%D1%82%D1%8C", - "%D1%81%D1%8E%D0%B4%D0%B0", + "%D1%88%D0%BB%D1%8F%D1%85", + "%D1%81%D1%8E%D0%B4%D0%B8", ) == url.raw_parts def test_parts_non_ascii(): - url = URL("http://example.com/путь/сюда") - assert ("/", "путь", "сюда") == url.parts + url = URL("http://example.com/шлях/сюди") + assert ("/", "шлях", "сюди") == url.parts def test_name_for_empty_url(): @@ -489,8 +490,8 @@ def test_relative_raw_name_slash(): def test_name_non_ascii(): - url = URL("http://example.com/путь") - assert url.name == "путь" + url = URL("http://example.com/шлях") + assert url.name == "шлях" def test_suffix_for_empty_url(): @@ -534,8 +535,8 @@ def test_relative_raw_suffix_dot(): def test_suffix_non_ascii(): - url = URL("http://example.com/путь.суффикс") - assert url.suffix == ".суффикс" + url = URL("http://example.com/шлях.суфікс") + assert url.suffix == ".суфікс" def test_suffix_with_empty_name(): @@ -594,8 +595,8 @@ def test_relative_raw_suffixes_dot(): def test_suffixes_non_ascii(): - url = URL("http://example.com/путь.суффикс") - assert url.suffixes == (".суффикс",) + url = URL("http://example.com/шлях.суфікс") + assert url.suffixes == (".суфікс",) def test_suffixes_with_empty_name(): @@ -753,15 +754,15 @@ def test_div_for_relative_url_started_with_slash(): def test_div_non_ascii(): - url = URL("http://example.com/сюда") - url2 = url / "туда" - assert url2.path == "/сюда/туда" - assert url2.raw_path == "/%D1%81%D1%8E%D0%B4%D0%B0/%D1%82%D1%83%D0%B4%D0%B0" - assert url2.parts == ("/", "сюда", "туда") + url = URL("http://example.com/сюди") + url2 = url / "туди" + assert url2.path == "/сюди/туди" + assert url2.raw_path == "/%D1%81%D1%8E%D0%B4%D0%B8/%D1%82%D1%83%D0%B4%D0%B8" + assert url2.parts == ("/", "сюди", "туди") assert url2.raw_parts == ( "/", - "%D1%81%D1%8E%D0%B4%D0%B0", - "%D1%82%D1%83%D0%B4%D0%B0", + "%D1%81%D1%8E%D0%B4%D0%B8", + "%D1%82%D1%83%D0%B4%D0%B8", ) @@ -846,13 +847,13 @@ def test_joinpath_relative(url, to_join, expected): "url,to_join,encoded,e_path,e_raw_path,e_parts,e_raw_parts", [ pytest.param( - "http://example.com/сюда", - ("туда",), + "http://example.com/сюди", + ("туди",), False, - "/сюда/туда", - "/%D1%81%D1%8E%D0%B4%D0%B0/%D1%82%D1%83%D0%B4%D0%B0", - ("/", "сюда", "туда"), - ("/", "%D1%81%D1%8E%D0%B4%D0%B0", "%D1%82%D1%83%D0%B4%D0%B0"), + "/сюди/туди", + "/%D1%81%D1%8E%D0%B4%D0%B8/%D1%82%D1%83%D0%B4%D0%B8", + ("/", "сюди", "туди"), + ("/", "%D1%81%D1%8E%D0%B4%D0%B8", "%D1%82%D1%83%D0%B4%D0%B8"), id="non-ascii", ), pytest.param( @@ -1093,11 +1094,11 @@ def test_with_name_empty(): def test_with_name_non_ascii(): - url = URL("http://example.com/path").with_name("путь") - assert url.path == "/путь" - assert url.raw_path == "/%D0%BF%D1%83%D1%82%D1%8C" - assert url.parts == ("/", "путь") - assert url.raw_parts == ("/", "%D0%BF%D1%83%D1%82%D1%8C") + url = URL("http://example.com/path").with_name("шлях") + assert url.path == "/шлях" + assert url.raw_path == "/%D1%88%D0%BB%D1%8F%D1%85" + assert url.parts == ("/", "шлях") + assert url.raw_parts == ("/", "%D1%88%D0%BB%D1%8F%D1%85") def test_with_name_percent_encoded(): @@ -1185,11 +1186,11 @@ def test_with_suffix_empty(): def test_with_suffix_non_ascii(): - url = URL("http://example.com/path").with_suffix(".путь") - assert url.path == "/path.путь" - assert url.raw_path == "/path.%D0%BF%D1%83%D1%82%D1%8C" - assert url.parts == ("/", "path.путь") - assert url.raw_parts == ("/", "path.%D0%BF%D1%83%D1%82%D1%8C") + url = URL("http://example.com/path").with_suffix(".шлях") + assert url.path == "/path.шлях" + assert url.raw_path == "/path.%D1%88%D0%BB%D1%8F%D1%85" + assert url.parts == ("/", "path.шлях") + assert url.raw_parts == ("/", "path.%D1%88%D0%BB%D1%8F%D1%85") def test_with_suffix_percent_encoded(): @@ -1340,8 +1341,8 @@ def test_from_ascii_login(): def test_from_non_ascii_login(): - url = URL("http://вася@host:1234/") - assert ("http://" "%D0%B2%D0%B0%D1%81%D1%8F" "@host:1234/") == str(url) + url = URL("http://бажан@host:1234/") + assert ("http://%D0%B1%D0%B0%D0%B6%D0%B0%D0%BD@host:1234/") == str(url) def test_from_ascii_login_and_password(): @@ -1360,10 +1361,10 @@ def test_from_ascii_login_and_password(): def test_from_non_ascii_login_and_password(): - url = URL("http://вася:пароль@host:1234/") + url = URL("http://бажан:пароль@host:1234/") assert ( "http://" - "%D0%B2%D0%B0%D1%81%D1%8F" + "%D0%B1%D0%B0%D0%B6%D0%B0%D0%BD" ":%D0%BF%D0%B0%D1%80%D0%BE%D0%BB%D1%8C" "@host:1234/" ) == str(url) @@ -1384,16 +1385,16 @@ def test_from_ascii_path_lower_case(): def test_from_non_ascii_path(): - url = URL("http://example.com/путь/туда") + url = URL("http://example.com/шлях/туди") assert ( - "http://example.com/" "%D0%BF%D1%83%D1%82%D1%8C/%D1%82%D1%83%D0%B4%D0%B0" + "http://example.com/%D1%88%D0%BB%D1%8F%D1%85/%D1%82%D1%83%D0%B4%D0%B8" ) == str(url) def test_bytes(): - url = URL("http://example.com/путь/туда") + url = URL("http://example.com/шлях/туди") assert ( - b"http://example.com/%D0%BF%D1%83%D1%82%D1%8C/%D1%82%D1%83%D0%B4%D0%B0" + b"http://example.com/%D1%88%D0%BB%D1%8F%D1%85/%D1%82%D1%83%D0%B4%D0%B8" == bytes(url) ) @@ -1610,23 +1611,23 @@ def test_split_result_non_decoded(): def test_human_repr(): - url = URL("http://вася:пароль@хост.домен:8080/путь/сюда?арг=вал#фраг") + url = URL("http://бажан:пароль@хост.домен:8080/шлях/сюди?арг=вал#фраг") s = url.human_repr() assert URL(s) == url - assert s == "http://вася:пароль@хост.домен:8080/путь/сюда?арг=вал#фраг" + assert s == "http://бажан:пароль@хост.домен:8080/шлях/сюди?арг=вал#фраг" def test_human_repr_defaults(): - url = URL("путь") + url = URL("шлях") s = url.human_repr() - assert s == "путь" + assert s == "шлях" def test_human_repr_default_port(): - url = URL("http://вася:пароль@хост.домен/путь/сюда?арг=вал#фраг") + url = URL("http://бажан:пароль@хост.домен/шлях/сюди?арг=вал#фраг") s = url.human_repr() assert URL(s) == url - assert s == "http://вася:пароль@хост.домен/путь/сюда?арг=вал#фраг" + assert s == "http://бажан:пароль@хост.домен/шлях/сюди?арг=вал#фраг" def test_human_repr_ipv6(): @@ -1667,20 +1668,20 @@ def test_human_repr_delimiters(): def test_human_repr_non_printable(): url = URL.build( scheme="http", - user="вася\n\xad\u200b", + user="бажан\n\xad\u200b", password="пароль\n\xad\u200b", host="хост.домен", port=8080, - path="/путь\n\xad\u200b", + path="/шлях\n\xad\u200b", query={"арг\n\xad\u200b": "вал\n\xad\u200b"}, fragment="фраг\n\xad\u200b", ) s = url.human_repr() assert URL(s) == url assert ( - s == "http://вася%0A%C2%AD%E2%80%8B:пароль%0A%C2%AD%E2%80%8B" + s == "http://бажан%0A%C2%AD%E2%80%8B:пароль%0A%C2%AD%E2%80%8B" "@хост.домен:8080" - "/путь%0A%C2%AD%E2%80%8B" + "/шлях%0A%C2%AD%E2%80%8B" "?арг%0A%C2%AD%E2%80%8B=вал%0A%C2%AD%E2%80%8B" "#фраг%0A%C2%AD%E2%80%8B" ) diff --git a/contrib/python/yarl/tests/test_url_build.py b/contrib/python/yarl/tests/test_url_build.py index 51969fa849..5aecbc5854 100644 --- a/contrib/python/yarl/tests/test_url_build.py +++ b/contrib/python/yarl/tests/test_url_build.py @@ -32,12 +32,28 @@ def test_build_with_scheme_and_host(): assert u == URL("http://127.0.0.1") -def test_build_with_port(): - with pytest.raises(ValueError): - URL.build(port=8000) - - u = URL.build(scheme="http", host="127.0.0.1", port=8000) - assert str(u) == "http://127.0.0.1:8000" +@pytest.mark.parametrize( + ("port", "exc", "match"), + [ + pytest.param( + 8000, + ValueError, + r"""(?x) + ^ + Can't\ build\ URL\ with\ "port"\ but\ without\ "host"\. + $ + """, + id="port-only", + ), + pytest.param( + "", TypeError, r"^The port is required to be int\.$", id="port-str" + ), + ], +) +def test_build_with_port(port, exc, match): + print(match) + with pytest.raises(exc, match=match): + URL.build(port=port) def test_build_with_user(): @@ -85,8 +101,10 @@ def test_build_with_authority_and_host(): def test_build_with_authority(): - url = URL.build(scheme="http", authority="ваня:bar@host.com:8000", path="path") - assert str(url) == "http://%D0%B2%D0%B0%D0%BD%D1%8F:bar@host.com:8000/path" + url = URL.build(scheme="http", authority="степан:bar@host.com:8000", path="path") + assert ( + str(url) == "http://%D1%81%D1%82%D0%B5%D0%BF%D0%B0%D0%BD:bar@host.com:8000/path" + ) def test_build_with_authority_without_encoding(): @@ -109,23 +127,33 @@ def test_query_dict(): def test_build_path_quoting(): u = URL.build( - scheme="http", host="127.0.0.1", path="/файл.jpg", query=dict(arg="Привет") + scheme="http", + host="127.0.0.1", + path="/фотографія.jpg", + query=dict(arg="Привіт"), ) - assert u == URL("http://127.0.0.1/файл.jpg?arg=Привет") + assert u == URL("http://127.0.0.1/фотографія.jpg?arg=Привіт") assert str(u) == ( - "http://127.0.0.1/%D1%84%D0%B0%D0%B9%D0%BB.jpg?" - "arg=%D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82" + "http://127.0.0.1/" + "%D1%84%D0%BE%D1%82%D0%BE%D0%B3%D1%80%D0%B0%D1%84%D1%96%D1%8F.jpg?" + "arg=%D0%9F%D1%80%D0%B8%D0%B2%D1%96%D1%82" ) def test_build_query_quoting(): - u = URL.build(scheme="http", host="127.0.0.1", path="/файл.jpg", query="arg=Привет") + u = URL.build( + scheme="http", + host="127.0.0.1", + path="/фотографія.jpg", + query="arg=Привіт", + ) - assert u == URL("http://127.0.0.1/файл.jpg?arg=Привет") + assert u == URL("http://127.0.0.1/фотографія.jpg?arg=Привіт") assert str(u) == ( - "http://127.0.0.1/%D1%84%D0%B0%D0%B9%D0%BB.jpg?" - "arg=%D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82" + "http://127.0.0.1/" + "%D1%84%D0%BE%D1%82%D0%BE%D0%B3%D1%80%D0%B0%D1%84%D1%96%D1%8F.jpg?" + "arg=%D0%9F%D1%80%D0%B8%D0%B2%D1%96%D1%82" ) @@ -143,14 +171,14 @@ def test_build_drop_dots(): def test_build_encode(): u = URL.build( scheme="http", - host="историк.рф", - path="/путь/файл", + host="оун-упа.укр", + path="/шлях/криївка", query_string="ключ=знач", fragment="фраг", ) expected = ( - "http://xn--h1aagokeh.xn--p1ai" - "/%D0%BF%D1%83%D1%82%D1%8C/%D1%84%D0%B0%D0%B9%D0%BB" + "http://xn----8sb1bdhvc.xn--j1amh" + "/%D1%88%D0%BB%D1%8F%D1%85/%D0%BA%D1%80%D0%B8%D1%97%D0%B2%D0%BA%D0%B0" "?%D0%BA%D0%BB%D1%8E%D1%87=%D0%B7%D0%BD%D0%B0%D1%87" "#%D1%84%D1%80%D0%B0%D0%B3" ) @@ -161,13 +189,13 @@ def test_build_already_encoded(): # resulting URL is invalid but not encoded u = URL.build( scheme="http", - host="историк.рф", - path="/путь/файл", + host="оун-упа.укр", + path="/шлях/криївка", query_string="ключ=знач", fragment="фраг", encoded=True, ) - assert str(u) == "http://историк.рф/путь/файл?ключ=знач#фраг" + assert str(u) == "http://оун-упа.укр/шлях/криївка?ключ=знач#фраг" def test_build_percent_encoded(): diff --git a/contrib/python/yarl/tests/test_url_parsing.py b/contrib/python/yarl/tests/test_url_parsing.py index cc753fcd0c..11aa8e92a4 100644 --- a/contrib/python/yarl/tests/test_url_parsing.py +++ b/contrib/python/yarl/tests/test_url_parsing.py @@ -214,15 +214,19 @@ class TestPort: assert u.query_string == "" assert u.fragment == "" - @pytest.mark.xfail(reason="https://github.com/aio-libs/yarl/issues/821") + @pytest.mark.xfail( + # FIXME: remove "no cover" pragmas upon xfail marker deletion + reason="https://github.com/aio-libs/yarl/issues/821", + raises=ValueError, + ) def test_no_host(self): u = URL("//:80") - assert u.scheme == "" - assert u.host == "" - assert u.port == 80 - assert u.path == "/" - assert u.query_string == "" - assert u.fragment == "" + assert u.scheme == "" # pragma: no cover + assert u.host == "" # pragma: no cover + assert u.port == 80 # pragma: no cover + assert u.path == "/" # pragma: no cover + assert u.query_string == "" # pragma: no cover + assert u.fragment == "" # pragma: no cover def test_double_port(self): with pytest.raises(ValueError): diff --git a/contrib/python/yarl/tests/test_url_update_netloc.py b/contrib/python/yarl/tests/test_url_update_netloc.py index cf0cc1c44c..47d13bcd60 100644 --- a/contrib/python/yarl/tests/test_url_update_netloc.py +++ b/contrib/python/yarl/tests/test_url_update_netloc.py @@ -33,11 +33,11 @@ def test_with_user(): def test_with_user_non_ascii(): url = URL("http://example.com") - url2 = url.with_user("вася") - assert url2.raw_user == "%D0%B2%D0%B0%D1%81%D1%8F" - assert url2.user == "вася" - assert url2.raw_authority == "%D0%B2%D0%B0%D1%81%D1%8F@example.com" - assert url2.authority == "вася@example.com:80" + url2 = url.with_user("бажан") + assert url2.raw_user == "%D0%B1%D0%B0%D0%B6%D0%B0%D0%BD" + assert url2.user == "бажан" + assert url2.raw_authority == "%D0%B1%D0%B0%D0%B6%D0%B0%D0%BD@example.com" + assert url2.authority == "бажан@example.com:80" def test_with_user_percent_encoded(): @@ -159,11 +159,11 @@ def test_with_host_empty(): def test_with_host_non_ascii(): url = URL("http://example.com:123") - url2 = url.with_host("историк.рф") - assert url2.raw_host == "xn--h1aagokeh.xn--p1ai" - assert url2.host == "историк.рф" - assert url2.raw_authority == "xn--h1aagokeh.xn--p1ai:123" - assert url2.authority == "историк.рф:123" + url2 = url.with_host("оун-упа.укр") + assert url2.raw_host == "xn----8sb1bdhvc.xn--j1amh" + assert url2.host == "оун-упа.укр" + assert url2.raw_authority == "xn----8sb1bdhvc.xn--j1amh:123" + assert url2.authority == "оун-упа.укр:123" def test_with_host_percent_encoded(): diff --git a/contrib/python/yarl/ya.make b/contrib/python/yarl/ya.make index 0c3d0ce434..e1a242b811 100644 --- a/contrib/python/yarl/ya.make +++ b/contrib/python/yarl/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(1.9.3) +VERSION(1.9.4) LICENSE(Apache-2.0) diff --git a/contrib/python/yarl/yarl/__init__.py b/contrib/python/yarl/yarl/__init__.py index f43aecbc92..127721ad09 100644 --- a/contrib/python/yarl/yarl/__init__.py +++ b/contrib/python/yarl/yarl/__init__.py @@ -1,5 +1,5 @@ from ._url import URL, cache_clear, cache_configure, cache_info -__version__ = "1.9.3" +__version__ = "1.9.4" __all__ = ("URL", "cache_clear", "cache_configure", "cache_info") diff --git a/contrib/python/yarl/yarl/_quoting_c.pyx b/contrib/python/yarl/yarl/_quoting_c.pyx index 5335d17365..96f69c14e2 100644 --- a/contrib/python/yarl/yarl/_quoting_c.pyx +++ b/contrib/python/yarl/yarl/_quoting_c.pyx @@ -145,7 +145,7 @@ cdef inline int _write_utf8(Writer* writer, Py_UCS4 symbol): if _write_pct(writer, <uint8_t>(0xe0 | (utf >> 12)), True) < 0: return -1 if _write_pct(writer, <uint8_t>(0x80 | ((utf >> 6) & 0x3f)), - True) < 0: + True) < 0: return -1 return _write_pct(writer, <uint8_t>(0x80 | (utf & 0x3f)), True) elif utf > 0x10FFFF: @@ -155,10 +155,10 @@ cdef inline int _write_utf8(Writer* writer, Py_UCS4 symbol): if _write_pct(writer, <uint8_t>(0xf0 | (utf >> 18)), True) < 0: return -1 if _write_pct(writer, <uint8_t>(0x80 | ((utf >> 12) & 0x3f)), - True) < 0: - return -1 + True) < 0: + return -1 if _write_pct(writer, <uint8_t>(0x80 | ((utf >> 6) & 0x3f)), - True) < 0: + True) < 0: return -1 return _write_pct(writer, <uint8_t>(0x80 | (utf & 0x3f)), True) diff --git a/contrib/python/yarl/yarl/_url.py b/contrib/python/yarl/yarl/_url.py index c8f2acb39b..9cca27ef86 100644 --- a/contrib/python/yarl/yarl/_url.py +++ b/contrib/python/yarl/yarl/_url.py @@ -233,6 +233,8 @@ class URL: raise ValueError( 'Can\'t mix "authority" with "user", "password", "host" or "port".' ) + if not isinstance(port, (int, type(None))): + raise TypeError("The port is required to be int.") if port and not host: raise ValueError('Can\'t build URL with "port" but without "host".') if query and query_string: @@ -507,7 +509,7 @@ class URL: return None if "%" in raw: # Hack for scoped IPv6 addresses like - # fe80::2%Проверка + # fe80::2%Перевірка # presence of '%' sign means only IPv6 address, so idna is useless. return raw return _idna_decode(raw) |