diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ca6559031..865772a0e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,7 @@ jobs: cache: 'pip' cache-dependency-path: 'requirements/*.txt' - name: Install dependencies - uses: py-actions/py-dependency-install@v3 + uses: py-actions/py-dependency-install@v4 with: path: requirements/lint.txt - name: Install itself @@ -48,7 +48,7 @@ jobs: make lint - name: Install spell checker run: | - sudo apt install libenchant-dev + sudo apt install libenchant-2-dev pip install -r requirements/doc-spelling.txt - name: Run docs spelling run: | @@ -67,31 +67,23 @@ jobs: name: Test strategy: matrix: - pyver: ['3.7', '3.8', '3.9', '3.10'] + pyver: ['3.7', '3.8', '3.9', '3.10', '3.11'] no-extensions: ['', 'Y'] os: [ubuntu, macos, windows] - experimental: [false] exclude: - os: macos no-extensions: 'Y' - os: windows no-extensions: 'Y' include: - - pyver: pypy-3.8 + - pyver: pypy-3.9 no-extensions: 'Y' os: ubuntu - experimental: false - - os: ubuntu - pyver: "3.11.0-alpha - 3.11.0" - experimental: true - no-extensions: '' - - os: ubuntu - pyver: "3.11.0-alpha - 3.11.0" - experimental: true + - pyver: pypy-3.8 no-extensions: 'Y' + os: ubuntu fail-fast: false runs-on: ${{ matrix.os }}-latest - continue-on-error: ${{ matrix.experimental }} timeout-minutes: 15 steps: - name: Checkout @@ -104,7 +96,7 @@ jobs: cache-dependency-path: 'requirements/*.txt' - name: Install cython if: ${{ matrix.no-extensions == '' }} - uses: py-actions/py-dependency-install@v3 + uses: py-actions/py-dependency-install@v4 with: path: requirements/cython.txt - name: Cythonize @@ -112,7 +104,7 @@ jobs: run: | make cythonize - name: Install dependencies - uses: py-actions/py-dependency-install@v3 + uses: py-actions/py-dependency-install@v4 with: path: requirements/ci.txt env: @@ -125,7 +117,7 @@ jobs: python -m pytest tests -vv python -m coverage xml - name: Upload coverage - uses: codecov/codecov-action@v3.1.0 + uses: codecov/codecov-action@v3.1.1 with: token: ${{ secrets.CODECOV_TOKEN }} file: ./coverage.xml @@ -164,7 +156,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v4 - name: Install cython - uses: py-actions/py-dependency-install@v3 + uses: py-actions/py-dependency-install@v4 with: path: requirements/cython.txt - name: Cythonize @@ -215,14 +207,14 @@ jobs: - name: Setup Python uses: actions/setup-python@v4 - name: Install cython - uses: py-actions/py-dependency-install@v3 + uses: py-actions/py-dependency-install@v4 with: path: requirements/cython.txt - name: Cythonize run: | make cythonize - name: Build wheels - uses: pypa/cibuildwheel@2.6.1 + uses: pypa/cibuildwheel@v2.11.3 env: CIBW_ARCHS_MACOS: x86_64 arm64 universal2 - uses: actions/upload-artifact@v3 @@ -247,7 +239,7 @@ jobs: name: dist path: dist - name: Make Release - uses: aio-libs/create-release@v1.6.4 + uses: aio-libs/create-release@v1.6.6 with: changes_file: CHANGES.rst name: yarl diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7b0e05c81..1c237abee 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,6 +16,9 @@ repos: hooks: - id: black language_version: python3 # Should be a command that runs python3.6+ + # Black misbehaved and broke when click 8.1.0 was released with an internal + # module removed. See https://github.com/psf/black/issues/2964 + additional_dependencies: ['click<8.1.0'] - repo: https://github.com/pre-commit/pre-commit-hooks rev: 'v4.0.1' hooks: @@ -57,5 +60,6 @@ repos: rev: v1.0.1 hooks: - id: rst-linter + exclude: ^CHANGES\.rst$ files: >- ^[^/]+[.]rst$ diff --git a/CHANGES.rst b/CHANGES.rst index 3f873d207..f6937809c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -14,6 +14,49 @@ Changelog .. towncrier release notes start +1.8.2 (2022-12-03) +================== + +This is the first release that started shipping wheels for Python 3.11. + + +1.8.1 (2022-08-01) +================== + +Misc +---- + +- `#694 `_, `#699 `_, `#700 `_, `#701 `_, `#702 `_, `#703 `_, `#739 `_ + + +1.8.0 (2022-08-01) +================== + +Features +-------- + +- Added ``URL.raw_suffix``, ``URL.suffix``, ``URL.raw_suffixes``, ``URL.suffixes``, ``URL.with_suffix``. (`#613 `_) + + +Improved Documentation +---------------------- + +- Fixed broken internal references to :meth:`~URL.human_repr`. (`#665 `_) +- Fixed broken external references to :doc:`multidict:index` docs. (`#665 `_) + + +Deprecations and Removals +------------------------- + +- Dropped Python 3.6 support. (`#672 `_) + + +Misc +---- + +- `#646 `_, `#699 `_, `#701 `_ + + 1.7.2 (2021-11-01) ================== diff --git a/CHANGES/613.feature.rst b/CHANGES/613.feature.rst deleted file mode 100644 index 9ccbf9f5e..000000000 --- a/CHANGES/613.feature.rst +++ /dev/null @@ -1 +0,0 @@ -Added ``URL.raw_suffix``, ``URL.suffix``, ``URL.raw_suffixes``, ``URL.suffixes``, ``URL.with_suffix``. diff --git a/CHANGES/646.misc b/CHANGES/646.misc deleted file mode 100644 index 2b1f355ba..000000000 --- a/CHANGES/646.misc +++ /dev/null @@ -1 +0,0 @@ - Do not install C sources with binary distributions. diff --git a/CHANGES/665.doc.1 b/CHANGES/665.doc.1 deleted file mode 100644 index c9f9bde6c..000000000 --- a/CHANGES/665.doc.1 +++ /dev/null @@ -1 +0,0 @@ -Fixed broken internal references to :meth:`~URL.human_repr`. diff --git a/CHANGES/665.doc.2 b/CHANGES/665.doc.2 deleted file mode 100644 index 41486577a..000000000 --- a/CHANGES/665.doc.2 +++ /dev/null @@ -1 +0,0 @@ -Fixed broken external references to :doc:`multidict:index` docs. diff --git a/CHANGES/672.removal b/CHANGES/672.removal deleted file mode 100644 index 825500d64..000000000 --- a/CHANGES/672.removal +++ /dev/null @@ -1 +0,0 @@ -Dropped Python 3.6 support. diff --git a/CHANGES/694.misc.rst b/CHANGES/694.misc.rst deleted file mode 100644 index d9388a95a..000000000 --- a/CHANGES/694.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Simplified cache handling in GitHub workflows. diff --git a/CHANGES/699.misc.rst b/CHANGES/699.misc.rst deleted file mode 100644 index 4acfde27c..000000000 --- a/CHANGES/699.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Replaced git:// URL in pre-commit hook configuration with https:// diff --git a/CHANGES/700.misc.rst b/CHANGES/700.misc.rst deleted file mode 100644 index e1bd2c38d..000000000 --- a/CHANGES/700.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated the GitHub workflow to avoid running unnecessary extra jobs. diff --git a/CHANGES/701.misc.rst b/CHANGES/701.misc.rst deleted file mode 100644 index e7ebfaf82..000000000 --- a/CHANGES/701.misc.rst +++ /dev/null @@ -1,2 +0,0 @@ -Updated the black .pre-commit-hook configuration to avoid it breaking due to -`Black issue #2964 `_. diff --git a/CHANGES/702.misc.rst b/CHANGES/702.misc.rst deleted file mode 100644 index bb84ada4e..000000000 --- a/CHANGES/702.misc.rst +++ /dev/null @@ -1,2 +0,0 @@ -Updated pre-commit-hooks black to a newer release with a fix for black#2964, -dropping the need for a work-around. diff --git a/CHANGES/703.misc.rst b/CHANGES/703.misc.rst deleted file mode 100644 index b861473dd..000000000 --- a/CHANGES/703.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Furter cleaned up github workflow event matching. diff --git a/CHANGES/739.misc.rst b/CHANGES/739.misc.rst deleted file mode 100644 index a8bdedf02..000000000 --- a/CHANGES/739.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Changed the Sphinx ``language`` parameter to ``"en"`` to allow for Sphinx 5.0. diff --git a/CHANGES/792.bugfix.rst b/CHANGES/792.bugfix.rst new file mode 100644 index 000000000..3f8c697e0 --- /dev/null +++ b/CHANGES/792.bugfix.rst @@ -0,0 +1 @@ +Fixed an issue with update_query() not getting rid of the query when argument is None. diff --git a/CHANGES/793.bugfix.rst b/CHANGES/793.bugfix.rst new file mode 100644 index 000000000..81fe36659 --- /dev/null +++ b/CHANGES/793.bugfix.rst @@ -0,0 +1 @@ +Added some input restrictions on with_port() function to prevent invalid boolean inputs or out of valid port inputs; handled incorrect 0 port representation. diff --git a/README.rst b/README.rst index 6347ece2b..7ee33bc33 100644 --- a/README.rst +++ b/README.rst @@ -1,6 +1,8 @@ yarl ==== +The module provides handy URL class for URL parsing and changing. + .. image:: https://github.com/aio-libs/yarl/workflows/CI/badge.svg :target: https://github.com/aio-libs/yarl/actions?query=workflow%3ACI :align: right diff --git a/requirements/cython.txt b/requirements/cython.txt index 132b8e779..cb44b5ab2 100644 --- a/requirements/cython.txt +++ b/requirements/cython.txt @@ -1 +1 @@ -cython==0.29.30 +cython==0.29.32 diff --git a/requirements/dev.txt b/requirements/dev.txt index 3ef56aa02..bda06e1d6 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -1,2 +1,2 @@ -r ci.txt -towncrier==21.9.0 +towncrier==22.8.0 diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index 786eb9d89..5dff220fb 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -1,2 +1,2 @@ -r doc.txt -sphinxcontrib-spelling==7.5.0; platform_system!="Windows" # We only use it in Azure CI +sphinxcontrib-spelling==7.7.0; platform_system!="Windows" # We only use it in Azure CI diff --git a/requirements/doc.txt b/requirements/doc.txt index 13f5d350e..5388c8836 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -1 +1 @@ -sphinx==5.0.1 +sphinx==5.3.0 diff --git a/requirements/lint.txt b/requirements/lint.txt index c66def1fb..a87465afe 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -1,2 +1,2 @@ -mypy==0.961; implementation_name=="cpython" -pre-commit==2.19.0 +mypy==0.971; implementation_name=="cpython" +pre-commit==2.20.0 diff --git a/requirements/test.txt b/requirements/test.txt index b7e75ca7e..e3c07bfd0 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -1,6 +1,6 @@ -e . -idna==3.3 -multidict==6.0.2 -pytest==7.1.2 +idna==3.4 +multidict==6.0.3 +pytest==7.2.0 pytest-cov>=2.3.1 -typing_extensions==4.2.0 +typing_extensions==4.4.0 diff --git a/requirements/towncrier.txt b/requirements/towncrier.txt index 6c31b0cf7..6928c85b6 100644 --- a/requirements/towncrier.txt +++ b/requirements/towncrier.txt @@ -1 +1 @@ -towncrier==21.9.0 +towncrier==22.8.0 diff --git a/setup.py b/setup.py index 6a3766133..d470e14e8 100644 --- a/setup.py +++ b/setup.py @@ -38,11 +38,22 @@ def read(name): return f.read() +# $ echo ':something:`test `' | sed 's/:\w\+:`\(\w\+\)\(\s\+\(.*\)\)\?`/``\1``/g' +# ``test`` +def sanitize_rst_roles(rst_source_text: str) -> str: + """Replace RST roles with inline highlighting.""" + role_regex = r":\w+:`(?P[^`]+)(\s+(.*))?`" + substitution_pattern = r"``(?P=rendered_text)``" + return re.sub(role_regex, substitution_pattern, rst_source_text) + + args = dict( name="yarl", version=version, description=("Yet another URL library"), - long_description="\n\n".join([read("README.rst"), read("CHANGES.rst")]), + long_description="\n\n".join( + [read("README.rst"), sanitize_rst_roles(read("CHANGES.rst"))] + ), long_description_content_type="text/x-rst", classifiers=[ "License :: OSI Approved :: Apache Software License", @@ -53,12 +64,13 @@ def read(name): "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", "Topic :: Internet :: WWW/HTTP", ], author="Andrew Svetlov", author_email="andrew.svetlov@gmail.com", url="https://github.com/aio-libs/yarl/", - license="Apache 2", + license="Apache-2.0", packages=["yarl"], install_requires=install_requires, python_requires=">=3.7", diff --git a/tests/test_update_query.py b/tests/test_update_query.py index 3c8de49d9..bbd3972d8 100644 --- a/tests/test_update_query.py +++ b/tests/test_update_query.py @@ -40,6 +40,12 @@ def test_update_query_with_multiple_args(): url.update_query("a", "b") +def test_update_query_with_none_arg(): + url = URL("http://example.com/?foo=bar&baz=foo") + expected_url = URL("http://example.com/") + assert url.update_query(None) == expected_url + + def test_with_query_list_of_pairs(): url = URL("http://example.com") assert str(url.with_query([("a", "1")])) == "http://example.com/?a=1" diff --git a/tests/test_url_update_netloc.py b/tests/test_url_update_netloc.py index a731305ee..cf0cc1c44 100644 --- a/tests/test_url_update_netloc.py +++ b/tests/test_url_update_netloc.py @@ -191,6 +191,11 @@ def test_with_port(): assert str(url.with_port(8888)) == "http://example.com:8888" +def test_with_port_with_no_port(): + url = URL("http://example.com") + assert str(url.with_port(None)) == "http://example.com" + + def test_with_port_ipv6(): url = URL("http://[::1]:8080/") assert str(url.with_port(80)) == "http://[::1]:80/" @@ -214,3 +219,10 @@ def test_with_port_for_relative_url(): def test_with_port_invalid_type(): with pytest.raises(TypeError): URL("http://example.com").with_port("123") + with pytest.raises(TypeError): + URL("http://example.com").with_port(True) + + +def test_with_port_invalid_range(): + with pytest.raises(ValueError): + URL("http://example.com").with_port(-1) diff --git a/yarl/__init__.py b/yarl/__init__.py index 8f617b0f6..c2df0c6d9 100644 --- a/yarl/__init__.py +++ b/yarl/__init__.py @@ -1,5 +1,5 @@ from ._url import URL, cache_clear, cache_configure, cache_info -__version__ = "1.7.2" +__version__ = "1.8.2" __all__ = ("URL", "cache_clear", "cache_configure", "cache_info") diff --git a/yarl/_url.py b/yarl/_url.py index be6498de7..e86b4b10c 100644 --- a/yarl/_url.py +++ b/yarl/_url.py @@ -765,7 +765,7 @@ def _make_netloc( ret = cls._encode_host(host) else: ret = host - if port: + if port is not None: ret = ret + ":" + str(port) if password is not None: if not user: @@ -873,8 +873,11 @@ def with_port(self, port): """ # N.B. doesn't cleanup query/fragment - if port is not None and not isinstance(port, int): - raise TypeError(f"port should be int or None, got {type(port)}") + if port is not None: + if isinstance(port, bool) or not isinstance(port, int): + raise TypeError(f"port should be int or None, got {type(port)}") + if port < 0 or port > 65535: + raise ValueError(f"port must be between 0 and 65535, got {port}") if not self.is_absolute(): raise ValueError("port replacement is not allowed for relative URLs") val = self._val @@ -986,9 +989,11 @@ def with_query(self, *args, **kwargs): def update_query(self, *args, **kwargs): """Return a new URL with query part updated.""" s = self._get_str_query(*args, **kwargs) - new_query = MultiDict(parse_qsl(s, keep_blank_values=True)) - query = MultiDict(self.query) - query.update(new_query) + query = None + if s: + new_query = MultiDict(parse_qsl(s, keep_blank_values=True)) + query = MultiDict(self.query) + query.update(new_query) return URL(self._val._replace(query=self._get_str_query(query)), encoded=True)