From 8cfe60b485ec0f885644e057051c5d41aba647e1 Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Wed, 21 Dec 2022 14:55:33 -0800 Subject: [PATCH 01/14] Add started and stopped notification parameters * Closes #1931 --- plexpy/common.py | 6 ++++++ plexpy/notification_handler.py | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/plexpy/common.py b/plexpy/common.py index b6800d3c..33b4cc00 100644 --- a/plexpy/common.py +++ b/plexpy/common.py @@ -400,6 +400,12 @@ NOTIFICATION_PARAMETERS = [ {'name': 'Player', 'type': 'str', 'value': 'player', 'description': 'The name of the player being used for playback.'}, {'name': 'Initial Stream', 'type': 'int', 'value': 'initial_stream', 'description': 'If the stream is the initial stream of a continuous streaming session.', 'example': '0 or 1'}, {'name': 'IP Address', 'type': 'str', 'value': 'ip_address', 'description': 'The IP address of the device being used for playback.'}, + {'name': 'Started Datestamp', 'type': 'str', 'value': 'started_datestamp', 'description': 'The date (in date format) when the stream started.'}, + {'name': 'Started Timestamp', 'type': 'str', 'value': 'started_timestamp', 'description': 'The time (in time format) when the stream started.'}, + {'name': 'Started Unix Time', 'type': 'int', 'value': 'started_unixtime', 'description': 'The unix timestamp when the stream started.'}, + {'name': 'Stopped Datestamp', 'type': 'str', 'value': 'stopped_datestamp', 'description': 'The date (in date format) when the stream stopped.'}, + {'name': 'Stopped Timestamp', 'type': 'str', 'value': 'stopped_timestamp', 'description': 'The time (in time format) when the stream stopped.'}, + {'name': 'Stopped Unix Time', 'type': 'int', 'value': 'stopped_unixtime', 'description': 'The unix timestamp when the stream stopped.'}, {'name': 'Stream Duration', 'type': 'int', 'value': 'stream_duration', 'description': 'The duration (in minutes) for the stream.'}, {'name': 'Stream Duration (sec)', 'type': 'int', 'value': 'stream_duration_sec', 'description': 'The duration (in seconds) for the stream.'}, {'name': 'Stream Time', 'type': 'str', 'value': 'stream_time', 'description': 'The duration (in time format) of the stream.'}, diff --git a/plexpy/notification_handler.py b/plexpy/notification_handler.py index 315bcb52..7c16de00 100644 --- a/plexpy/notification_handler.py +++ b/plexpy/notification_handler.py @@ -985,6 +985,12 @@ def build_media_notify_params(notify_action=None, session=None, timeline=None, m 'product': notify_params['product'], 'player': notify_params['player'], 'ip_address': notify_params.get('ip_address', 'N/A'), + 'started_datestamp': arrow.get(notify_params['started']).format(date_format), + 'started_timestamp': arrow.get(notify_params['started']).format(time_format), + 'started_unixtime': notify_params['started'], + 'stopped_datestamp': arrow.get(notify_params['stopped']).format(date_format) if notify_params['stopped'] else '', + 'stopped_timestamp': arrow.get(notify_params['stopped']).format(time_format) if notify_params['stopped'] else '', + 'stopped_unixtime': notify_params['stopped'], 'stream_duration': stream_duration, 'stream_duration_sec': stream_duration_sec, 'stream_time': arrow.get(stream_duration_sec).format(duration_format), From 5f8fbe1230d1e35e013b29b0ee9d896d09572048 Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Wed, 21 Dec 2022 15:13:14 -0800 Subject: [PATCH 02/14] Add meta color-scheme to newsletter template * Allows CSS to support light and dark themes --- data/interfaces/newsletters/recently_added.html | 1 + data/interfaces/newsletters/recently_added.internal.html | 1 + 2 files changed, 2 insertions(+) diff --git a/data/interfaces/newsletters/recently_added.html b/data/interfaces/newsletters/recently_added.html index f4f6e8e8..e74d8463 100644 --- a/data/interfaces/newsletters/recently_added.html +++ b/data/interfaces/newsletters/recently_added.html @@ -24,6 +24,7 @@ + Tautulli Newsletter - ${subject} diff --git a/data/interfaces/newsletters/recently_added.internal.html b/data/interfaces/newsletters/recently_added.internal.html index 8253e281..edf7edc6 100644 --- a/data/interfaces/newsletters/recently_added.internal.html +++ b/data/interfaces/newsletters/recently_added.internal.html @@ -24,6 +24,7 @@ + Tautulli Newsletter - ${subject} From 8a1cd2841b907a8bd15b31a4d6de4ddebf6ac39d Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Tue, 29 Nov 2022 20:55:14 -0800 Subject: [PATCH 03/14] Revert "Pin diddlesnaps/snapcraft-multiarch-action@v1.5.0" This reverts commit 6156edf5feed9f9e3d9b6ef9bbb924407d469ed2. --- .github/workflows/publish-snap.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-snap.yml b/.github/workflows/publish-snap.yml index 5cd43a1b..da1959ea 100644 --- a/.github/workflows/publish-snap.yml +++ b/.github/workflows/publish-snap.yml @@ -38,7 +38,7 @@ jobs: uses: docker/setup-qemu-action@v2 - name: Build Snap Package - uses: diddlesnaps/snapcraft-multiarch-action@v1.5.0 + uses: diddlesnaps/snapcraft-multiarch-action@v1 id: build with: architecture: ${{ matrix.architecture }} From d7aa1ced53687bee7d3616cba925cd74deb39a7b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Dec 2022 15:41:04 -0800 Subject: [PATCH 04/14] Bump dessant/label-actions from 2 to 3 (#1916) Bumps [dessant/label-actions](https://github.com/dessant/label-actions) from 2 to 3. - [Release notes](https://github.com/dessant/label-actions/releases) - [Changelog](https://github.com/dessant/label-actions/blob/master/CHANGELOG.md) - [Commits](https://github.com/dessant/label-actions/compare/v2...v3) --- updated-dependencies: - dependency-name: dessant/label-actions dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> [skip ci] --- .github/workflows/issues.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/issues.yml b/.github/workflows/issues.yml index 728cd8c9..34ceb357 100644 --- a/.github/workflows/issues.yml +++ b/.github/workflows/issues.yml @@ -10,6 +10,6 @@ jobs: runs-on: ubuntu-latest steps: - name: Label Issues - uses: dessant/label-actions@v2 + uses: dessant/label-actions@v3 with: github-token: ${{ github.token }} From ec08c887f44b745df0626b7207d630fdd3a34a81 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Dec 2022 15:41:32 -0800 Subject: [PATCH 05/14] Bump actions/setup-python from 4.3.0 to 4.3.1 (#1921) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4.3.0 to 4.3.1. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v4.3.0...v4.3.1) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> [skip ci] --- .github/workflows/publish-installers.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-installers.yml b/.github/workflows/publish-installers.yml index 1c5f075b..a5190f79 100644 --- a/.github/workflows/publish-installers.yml +++ b/.github/workflows/publish-installers.yml @@ -52,7 +52,7 @@ jobs: echo $GITHUB_SHA > version.txt - name: Set Up Python - uses: actions/setup-python@v4.3.0 + uses: actions/setup-python@v4.3.1 with: python-version: '3.9' cache: pip From d00916331da81fee7767abc528029c0fa4e804c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Dec 2022 15:41:48 -0800 Subject: [PATCH 06/14] Bump actions/checkout from 3.1.0 to 3.2.0 (#1924) Bumps [actions/checkout](https://github.com/actions/checkout) from 3.1.0 to 3.2.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3.1.0...v3.2.0) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> [skip ci] --- .github/workflows/publish-docker.yml | 2 +- .github/workflows/publish-installers.yml | 4 ++-- .github/workflows/publish-snap.yml | 2 +- .github/workflows/pull-requests.yml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/publish-docker.yml b/.github/workflows/publish-docker.yml index 3f6af320..aca4efed 100644 --- a/.github/workflows/publish-docker.yml +++ b/.github/workflows/publish-docker.yml @@ -13,7 +13,7 @@ jobs: if: ${{ !contains(github.event.head_commit.message, '[skip ci]') }} steps: - name: Checkout Code - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v3.2.0 - name: Prepare id: prepare diff --git a/.github/workflows/publish-installers.yml b/.github/workflows/publish-installers.yml index a5190f79..7002834b 100644 --- a/.github/workflows/publish-installers.yml +++ b/.github/workflows/publish-installers.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Checkout Code - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v3.2.0 - name: Set Release Version id: get_version @@ -103,7 +103,7 @@ jobs: uses: technote-space/workflow-conclusion-action@v3.0 - name: Checkout Code - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v3.2.0 - name: Set Release Version id: get_version diff --git a/.github/workflows/publish-snap.yml b/.github/workflows/publish-snap.yml index da1959ea..7ad8fe95 100644 --- a/.github/workflows/publish-snap.yml +++ b/.github/workflows/publish-snap.yml @@ -20,7 +20,7 @@ jobs: - armhf steps: - name: Checkout Code - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v3.2.0 - name: Prepare id: prepare diff --git a/.github/workflows/pull-requests.yml b/.github/workflows/pull-requests.yml index d15b9140..d7c8e45d 100644 --- a/.github/workflows/pull-requests.yml +++ b/.github/workflows/pull-requests.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Code - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v3.2.0 - name: Comment on Pull Request uses: mshick/add-pr-comment@v2 From ff81b0849119f0f806fe86fbf026977d35478f21 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Dec 2022 15:42:02 -0800 Subject: [PATCH 07/14] Bump actions/cache from 3.0.11 to 3.2.0 (#1936) Bumps [actions/cache](https://github.com/actions/cache) from 3.0.11 to 3.2.0. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3.0.11...v3.2.0) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> [skip ci] --- .github/workflows/publish-docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-docker.yml b/.github/workflows/publish-docker.yml index aca4efed..125cae51 100644 --- a/.github/workflows/publish-docker.yml +++ b/.github/workflows/publish-docker.yml @@ -47,7 +47,7 @@ jobs: version: latest - name: Cache Docker Layers - uses: actions/cache@v3.0.11 + uses: actions/cache@v3.2.0 with: path: /tmp/.buildx-cache key: ${{ runner.os }}-buildx-${{ github.sha }} From d736fab432303ea48e58bf9fa2829f17604c6a31 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Dec 2022 15:42:30 -0800 Subject: [PATCH 08/14] Bump actions/stale from 6 to 7 (#1937) Bumps [actions/stale](https://github.com/actions/stale) from 6 to 7. - [Release notes](https://github.com/actions/stale/releases) - [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/stale/compare/v6...v7) --- updated-dependencies: - dependency-name: actions/stale dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> [skip ci] --- .github/workflows/issues-stale.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/issues-stale.yml b/.github/workflows/issues-stale.yml index 75c1e08f..0643cb0a 100644 --- a/.github/workflows/issues-stale.yml +++ b/.github/workflows/issues-stale.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Stale - uses: actions/stale@v6 + uses: actions/stale@v7 with: stale-issue-message: > This issue is stale because it has been open for 30 days with no activity. @@ -30,7 +30,7 @@ jobs: days-before-close: 5 - name: Invalid Template - uses: actions/stale@v6 + uses: actions/stale@v7 with: stale-issue-message: > Invalid issues template. From 0a5edebea33855330ad98f14dd3a703daf940295 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Dec 2022 15:58:39 -0800 Subject: [PATCH 09/14] Bump tempora from 5.0.2 to 5.1.0 (#1902) * Bump tempora from 5.0.2 to 5.1.0 Bumps [tempora](https://github.com/jaraco/tempora) from 5.0.2 to 5.1.0. - [Release notes](https://github.com/jaraco/tempora/releases) - [Changelog](https://github.com/jaraco/tempora/blob/main/CHANGES.rst) - [Commits](https://github.com/jaraco/tempora/compare/v5.0.2...v5.1.0) --- updated-dependencies: - dependency-name: tempora dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Update tempora==5.1.0 Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> [skip ci] --- lib/more_itertools/more.py | 0 lib/tempora/__init__.py | 38 +++++++++++++++++++++++++------------- lib/tempora/schedule.py | 2 +- lib/tempora/timing.py | 2 +- requirements.txt | 2 +- 5 files changed, 28 insertions(+), 16 deletions(-) mode change 100644 => 100755 lib/more_itertools/more.py diff --git a/lib/more_itertools/more.py b/lib/more_itertools/more.py old mode 100644 new mode 100755 diff --git a/lib/tempora/__init__.py b/lib/tempora/__init__.py index 6652b5ae..cece8bb7 100644 --- a/lib/tempora/__init__.py +++ b/lib/tempora/__init__.py @@ -6,6 +6,10 @@ import re import numbers import functools import contextlib +from numbers import Number +from typing import Union, Tuple, Iterable +from typing import cast + from jaraco.functools import once @@ -33,7 +37,7 @@ hours_per_month = hours_per_day * days_per_year / 12 @once -def _needs_year_help(): +def _needs_year_help() -> bool: """ Some versions of Python render %Y with only three characters :( https://bugs.python.org/issue39103 @@ -41,14 +45,19 @@ def _needs_year_help(): return len(datetime.date(900, 1, 1).strftime('%Y')) != 4 -def ensure_datetime(ob): +AnyDatetime = Union[datetime.datetime, datetime.date, datetime.time] +StructDatetime = Union[Tuple[int, ...], time.struct_time] + + +def ensure_datetime(ob: AnyDatetime) -> datetime.datetime: """ Given a datetime or date or time object from the ``datetime`` module, always return a datetime using default values. """ if isinstance(ob, datetime.datetime): return ob - date = time = ob + date = cast(datetime.date, ob) + time = cast(datetime.time, ob) if isinstance(ob, datetime.date): time = datetime.time() if isinstance(ob, datetime.time): @@ -56,7 +65,13 @@ def ensure_datetime(ob): return datetime.datetime.combine(date, time) -def strftime(fmt, t): +def infer_datetime(ob: Union[AnyDatetime, StructDatetime]) -> datetime.datetime: + if isinstance(ob, (time.struct_time, tuple)): + ob = datetime.datetime(*ob[:6]) # type: ignore + return ensure_datetime(ob) + + +def strftime(fmt: str, t: Union[AnyDatetime, tuple, time.struct_time]) -> str: """ Portable strftime. @@ -115,15 +130,11 @@ def strftime(fmt, t): >>> strftime('%Y', datetime.time()) '1900' """ - if isinstance(t, (time.struct_time, tuple)): - t = datetime.datetime(*t[:6]) - t = ensure_datetime(t) + t = infer_datetime(t) subs = ( ('%s', '%03d' % (t.microsecond // 1000)), ('%µ', '%03d' % (t.microsecond % 1000)), - ) - if _needs_year_help(): # pragma: nocover - subs += (('%Y', '%04d' % t.year),) + ) + (('%Y', '%04d' % t.year),) * _needs_year_help() def doSub(s, sub): return s.replace(*sub) @@ -324,10 +335,10 @@ def calculate_prorated_values(): """ rate = input("Enter the rate (3/hour, 50/month)> ") for period, value in _prorated_values(rate): - print("per {period}: {value}".format(**locals())) + print(f"per {period}: {value}") -def _prorated_values(rate): +def _prorated_values(rate: str) -> Iterable[Tuple[str, Number]]: """ Given a rate (a string in units per unit time), and return that same rate for various time periods. @@ -341,7 +352,8 @@ def _prorated_values(rate): year: 175316.333 """ - res = re.match(r'(?P[\d.]+)/(?P\w+)$', rate).groupdict() + match = re.match(r'(?P[\d.]+)/(?P\w+)$', rate) + res = cast(re.Match, match).groupdict() value = float(res['value']) value_per_second = value / get_period_seconds(res['period']) for period in ('minute', 'hour', 'day', 'month', 'year'): diff --git a/lib/tempora/schedule.py b/lib/tempora/schedule.py index a94c9819..b6ad8aac 100644 --- a/lib/tempora/schedule.py +++ b/lib/tempora/schedule.py @@ -130,7 +130,7 @@ class PeriodicCommand(DelayedCommand): raise ValueError( "A PeriodicCommand must have a positive, " "non-zero delay." ) - super(PeriodicCommand, self).__setattr__(key, value) + super().__setattr__(key, value) class PeriodicCommandFixedDelay(PeriodicCommand): diff --git a/lib/tempora/timing.py b/lib/tempora/timing.py index 6b3147a9..c43a3d94 100644 --- a/lib/tempora/timing.py +++ b/lib/tempora/timing.py @@ -115,7 +115,7 @@ class Timer(Stopwatch): def __init__(self, target=float('Inf')): self.target = self._accept(target) - super(Timer, self).__init__() + super().__init__() @staticmethod def _accept(target): diff --git a/requirements.txt b/requirements.txt index 98316136..2a5b1836 100644 --- a/requirements.txt +++ b/requirements.txt @@ -41,7 +41,7 @@ rumps==0.4.0; platform_system == "Darwin" simplejson==3.18.0 six==1.16.0 soupsieve==2.3.2.post1 -tempora==5.0.2 +tempora==5.1.0 tokenize-rt==5.0.0 tzdata==2022.6 tzlocal==4.2 From 3d378eb583cac54ba09d0c770034aa9855b58ee1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Dec 2022 15:58:54 -0800 Subject: [PATCH 10/14] Bump cheroot from 8.6.0 to 9.0.0 (#1903) * Bump cheroot from 8.6.0 to 9.0.0 Bumps [cheroot](https://github.com/cherrypy/cheroot) from 8.6.0 to 9.0.0. - [Release notes](https://github.com/cherrypy/cheroot/releases) - [Changelog](https://github.com/cherrypy/cheroot/blob/main/CHANGES.rst) - [Commits](https://github.com/cherrypy/cheroot/compare/v8.6.0...v9.0.0) --- updated-dependencies: - dependency-name: cheroot dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * Update cheroot==9.0.0 Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> [skip ci] --- lib/cheroot/__init__.py | 9 +- lib/cheroot/_compat.py | 93 +----- lib/cheroot/_compat.pyi | 21 ++ lib/cheroot/cli.py | 12 +- lib/cheroot/connections.py | 17 +- lib/cheroot/errors.py | 7 +- lib/cheroot/errors.pyi | 4 +- lib/cheroot/makefile.py | 439 +++-------------------------- lib/cheroot/makefile.pyi | 13 - lib/cheroot/server.py | 52 +--- lib/cheroot/ssl/__init__.py | 8 +- lib/cheroot/ssl/builtin.py | 13 +- lib/cheroot/ssl/builtin.pyi | 3 +- lib/cheroot/ssl/pyopenssl.py | 8 +- lib/cheroot/ssl/pyopenssl.pyi | 11 +- lib/cheroot/test/_pytest_plugin.py | 18 +- lib/cheroot/test/conftest.py | 18 +- lib/cheroot/test/helper.py | 22 +- lib/cheroot/test/test__compat.py | 9 +- lib/cheroot/test/test_cli.py | 9 - lib/cheroot/test/test_conn.py | 48 ++-- lib/cheroot/test/test_core.py | 25 +- lib/cheroot/test/test_dispatch.py | 4 - lib/cheroot/test/test_makefile.py | 3 - lib/cheroot/test/test_server.py | 40 ++- lib/cheroot/test/test_ssl.py | 101 ++----- lib/cheroot/test/test_wsgi.py | 2 + lib/cheroot/test/webtest.py | 52 +--- lib/cheroot/testing.py | 16 +- lib/cheroot/workers/threadpool.py | 11 +- lib/cheroot/wsgi.py | 41 +-- lib/cheroot/wsgi.pyi | 7 + requirements.txt | 2 +- 33 files changed, 287 insertions(+), 851 deletions(-) create mode 100644 lib/cheroot/_compat.pyi diff --git a/lib/cheroot/__init__.py b/lib/cheroot/__init__.py index 30d38cab..aac9cd98 100644 --- a/lib/cheroot/__init__.py +++ b/lib/cheroot/__init__.py @@ -1,15 +1,12 @@ """High-performance, pure-Python HTTP server used by CherryPy.""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - try: - import pkg_resources + from importlib import metadata except ImportError: - pass + import importlib_metadata as metadata # noqa: WPS440 try: - __version__ = pkg_resources.get_distribution('cheroot').version + __version__ = metadata.version('cheroot') except Exception: __version__ = 'unknown' diff --git a/lib/cheroot/_compat.py b/lib/cheroot/_compat.py index 10dcdefa..20c993de 100644 --- a/lib/cheroot/_compat.py +++ b/lib/cheroot/_compat.py @@ -1,19 +1,9 @@ # pylint: disable=unused-import """Compatibility code for using Cheroot with various versions of Python.""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - import os import platform -import re -import six - -try: - import selectors # lgtm [py/unused-import] -except ImportError: - import selectors2 as selectors # noqa: F401 # lgtm [py/unused-import] try: import ssl @@ -22,20 +12,6 @@ try: except ImportError: IS_ABOVE_OPENSSL10 = None -# contextlib.suppress was added in Python 3.4 -try: - from contextlib import suppress -except ImportError: - from contextlib import contextmanager - - @contextmanager - def suppress(*exceptions): - """Return a context manager that suppresses the `exceptions`.""" - try: - yield - except exceptions: - pass - IS_CI = bool(os.getenv('CI')) IS_GITHUB_ACTIONS_WORKFLOW = bool(os.getenv('GITHUB_WORKFLOW')) @@ -53,53 +29,23 @@ PLATFORM_ARCH = platform.machine() IS_PPC = PLATFORM_ARCH.startswith('ppc') -if not six.PY2: - def ntob(n, encoding='ISO-8859-1'): - """Return the native string as bytes in the given encoding.""" - assert_native(n) - # In Python 3, the native string type is unicode - return n.encode(encoding) +def ntob(n, encoding='ISO-8859-1'): + """Return the native string as bytes in the given encoding.""" + assert_native(n) + # In Python 3, the native string type is unicode + return n.encode(encoding) - def ntou(n, encoding='ISO-8859-1'): - """Return the native string as Unicode with the given encoding.""" - assert_native(n) - # In Python 3, the native string type is unicode - return n - def bton(b, encoding='ISO-8859-1'): - """Return the byte string as native string in the given encoding.""" - return b.decode(encoding) -else: - # Python 2 - def ntob(n, encoding='ISO-8859-1'): - """Return the native string as bytes in the given encoding.""" - assert_native(n) - # In Python 2, the native string type is bytes. Assume it's already - # in the given encoding, which for ISO-8859-1 is almost always what - # was intended. - return n +def ntou(n, encoding='ISO-8859-1'): + """Return the native string as Unicode with the given encoding.""" + assert_native(n) + # In Python 3, the native string type is unicode + return n - def ntou(n, encoding='ISO-8859-1'): - """Return the native string as Unicode with the given encoding.""" - assert_native(n) - # In Python 2, the native string type is bytes. - # First, check for the special encoding 'escape'. The test suite uses - # this to signal that it wants to pass a string with embedded \uXXXX - # escapes, but without having to prefix it with u'' for Python 2, - # but no prefix for Python 3. - if encoding == 'escape': - return re.sub( - r'\\u([0-9a-zA-Z]{4})', - lambda m: six.unichr(int(m.group(1), 16)), - n.decode('ISO-8859-1'), - ) - # Assume it's already in the given encoding, which for ISO-8859-1 - # is almost always what was intended. - return n.decode(encoding) - def bton(b, encoding='ISO-8859-1'): - """Return the byte string as native string in the given encoding.""" - return b +def bton(b, encoding='ISO-8859-1'): + """Return the byte string as native string in the given encoding.""" + return b.decode(encoding) def assert_native(n): @@ -113,17 +59,6 @@ def assert_native(n): raise TypeError('n must be a native str (got %s)' % type(n).__name__) -if not six.PY2: - """Python 3 has :py:class:`memoryview` builtin.""" - # Python 2.7 has it backported, but socket.write() does - # str(memoryview(b'0' * 100)) -> - # instead of accessing it correctly. - memoryview = memoryview -else: - """Link :py:class:`memoryview` to buffer under Python 2.""" - memoryview = buffer # noqa: F821 - - def extract_bytes(mv): r"""Retrieve bytes out of the given input buffer. @@ -138,7 +73,7 @@ def extract_bytes(mv): or :py:class:`bytes` """ if isinstance(mv, memoryview): - return bytes(mv) if six.PY2 else mv.tobytes() + return mv.tobytes() if isinstance(mv, bytes): return mv diff --git a/lib/cheroot/_compat.pyi b/lib/cheroot/_compat.pyi new file mode 100644 index 00000000..023bad8c --- /dev/null +++ b/lib/cheroot/_compat.pyi @@ -0,0 +1,21 @@ +from typing import Any, ContextManager, Optional, Type, Union + +def suppress(*exceptions: Type[BaseException]) -> ContextManager[None]: ... + +IS_ABOVE_OPENSSL10: Optional[bool] +IS_CI: bool +IS_GITHUB_ACTIONS_WORKFLOW: bool +IS_PYPY: bool +SYS_PLATFORM: str +IS_WINDOWS: bool +IS_LINUX: bool +IS_MACOS: bool +PLATFORM_ARCH: str +IS_PPC: bool + +def ntob(n: str, encoding: str = ...) -> bytes: ... +def ntou(n: str, encoding: str = ...) -> str: ... +def bton(b: bytes, encoding: str = ...) -> str: ... +def assert_native(n: str) -> None: ... + +def extract_bytes(mv: Union[memoryview, bytes]) -> bytes: ... diff --git a/lib/cheroot/cli.py b/lib/cheroot/cli.py index 4607e226..cd168e91 100644 --- a/lib/cheroot/cli.py +++ b/lib/cheroot/cli.py @@ -28,18 +28,14 @@ Basic usage: """ import argparse -from importlib import import_module import os import sys - -import six +import urllib.parse # noqa: WPS301 +from importlib import import_module +from contextlib import suppress from . import server from . import wsgi -from ._compat import suppress - - -__metaclass__ = type class BindLocation: @@ -143,7 +139,7 @@ def parse_wsgi_bind_location(bind_addr_string): return AbstractSocket(bind_addr_string[1:]) # try and match for an IP/hostname and port - match = six.moves.urllib.parse.urlparse( + match = urllib.parse.urlparse( '//{addr}'.format(addr=bind_addr_string), ) try: diff --git a/lib/cheroot/connections.py b/lib/cheroot/connections.py index 181e3731..9b6366e5 100644 --- a/lib/cheroot/connections.py +++ b/lib/cheroot/connections.py @@ -1,22 +1,17 @@ """Utilities to manage open connections.""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - import io import os import socket import threading import time +import selectors +from contextlib import suppress from . import errors -from ._compat import selectors -from ._compat import suppress from ._compat import IS_WINDOWS from .makefile import MakeFile -import six - try: import fcntl except ImportError: @@ -310,8 +305,7 @@ class ConnectionManager: msg, ] - sock_to_make = s if not six.PY2 else s._sock - wfile = mf(sock_to_make, 'wb', io.DEFAULT_BUFFER_SIZE) + wfile = mf(s, 'wb', io.DEFAULT_BUFFER_SIZE) try: wfile.write(''.join(buf).encode('ISO-8859-1')) except socket.error as ex: @@ -327,10 +321,7 @@ class ConnectionManager: conn = self.server.ConnectionClass(self.server, s, mf) - if not isinstance( - self.server.bind_addr, - (six.text_type, six.binary_type), - ): + if not isinstance(self.server.bind_addr, (str, bytes)): # optional values # Until we do DNS lookups, omit REMOTE_HOST if addr is None: # sometimes this can happen diff --git a/lib/cheroot/errors.py b/lib/cheroot/errors.py index e00629f8..046263ad 100644 --- a/lib/cheroot/errors.py +++ b/lib/cheroot/errors.py @@ -1,17 +1,14 @@ # -*- coding: utf-8 -*- """Collection of exceptions raised and/or processed by Cheroot.""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - import errno import sys class MaxSizeExceeded(Exception): - """Exception raised when a client sends more data then acceptable within limit. + """Exception raised when a client sends more data then allowed under limit. - Depends on ``request.body.maxbytes`` config option if used within CherryPy + Depends on ``request.body.maxbytes`` config option if used within CherryPy. """ diff --git a/lib/cheroot/errors.pyi b/lib/cheroot/errors.pyi index e78a7585..18669568 100644 --- a/lib/cheroot/errors.pyi +++ b/lib/cheroot/errors.pyi @@ -1,4 +1,4 @@ -from typing import Any, List, Set, Tuple +from typing import List, Set, Tuple, Type class MaxSizeExceeded(Exception): ... class NoSSLError(Exception): ... @@ -10,4 +10,4 @@ socket_error_eintr: List[int] socket_errors_to_ignore: List[int] socket_errors_nonblocking: List[int] acceptable_sock_shutdown_error_codes: Set[int] -acceptable_sock_shutdown_exceptions: Tuple[Exception] +acceptable_sock_shutdown_exceptions: Tuple[Type[Exception], ...] diff --git a/lib/cheroot/makefile.py b/lib/cheroot/makefile.py index 1383c658..77878c13 100644 --- a/lib/cheroot/makefile.py +++ b/lib/cheroot/makefile.py @@ -1,21 +1,9 @@ """Socket file object.""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - import socket -try: - # prefer slower Python-based io module - import _pyio as io -except ImportError: - # Python 2.6 - import io - -import six - -from . import errors -from ._compat import extract_bytes, memoryview +# prefer slower Python-based io module +import _pyio as io # Write only 16K at a time to sockets @@ -48,400 +36,41 @@ class BufferedWriter(io.BufferedWriter): del self._write_buf[:n] -class MakeFile_PY2(getattr(socket, '_fileobject', object)): - """Faux file object attached to a socket object.""" +class StreamReader(io.BufferedReader): + """Socket stream reader.""" - def __init__(self, *args, **kwargs): - """Initialize faux file object.""" + def __init__(self, sock, mode='r', bufsize=io.DEFAULT_BUFFER_SIZE): + """Initialize socket stream reader.""" + super().__init__(socket.SocketIO(sock, mode), bufsize) self.bytes_read = 0 + + def read(self, *args, **kwargs): + """Capture bytes read.""" + val = super().read(*args, **kwargs) + self.bytes_read += len(val) + return val + + def has_data(self): + """Return true if there is buffered data to read.""" + return len(self._read_buf) > self._read_pos + + +class StreamWriter(BufferedWriter): + """Socket stream writer.""" + + def __init__(self, sock, mode='w', bufsize=io.DEFAULT_BUFFER_SIZE): + """Initialize socket stream writer.""" + super().__init__(socket.SocketIO(sock, mode), bufsize) self.bytes_written = 0 - socket._fileobject.__init__(self, *args, **kwargs) - self._refcount = 0 - def _reuse(self): - self._refcount += 1 - - def _drop(self): - if self._refcount < 0: - self.close() - else: - self._refcount -= 1 - - def write(self, data): - """Send entire data contents for non-blocking sockets.""" - bytes_sent = 0 - data_mv = memoryview(data) - payload_size = len(data_mv) - while bytes_sent < payload_size: - try: - bytes_sent += self.send( - data_mv[bytes_sent:bytes_sent + SOCK_WRITE_BLOCKSIZE], - ) - except socket.error as e: - if e.args[0] not in errors.socket_errors_nonblocking: - raise - - def send(self, data): - """Send some part of message to the socket.""" - bytes_sent = self._sock.send(extract_bytes(data)) - self.bytes_written += bytes_sent - return bytes_sent - - def flush(self): - """Write all data from buffer to socket and reset write buffer.""" - if self._wbuf: - buffer = ''.join(self._wbuf) - self._wbuf = [] - self.write(buffer) - - def recv(self, size): - """Receive message of a size from the socket.""" - while True: - try: - data = self._sock.recv(size) - self.bytes_read += len(data) - return data - except socket.error as e: - what = ( - e.args[0] not in errors.socket_errors_nonblocking - and e.args[0] not in errors.socket_error_eintr - ) - if what: - raise - - class FauxSocket: - """Faux socket with the minimal interface required by pypy.""" - - def _reuse(self): - pass - - _fileobject_uses_str_type = six.PY2 and isinstance( - socket._fileobject(FauxSocket())._rbuf, six.string_types, - ) - - # FauxSocket is no longer needed - del FauxSocket - - if not _fileobject_uses_str_type: # noqa: C901 # FIXME - def read(self, size=-1): - """Read data from the socket to buffer.""" - # Use max, disallow tiny reads in a loop as they are very - # inefficient. - # We never leave read() with any leftover data from a new recv() - # call in our internal buffer. - rbufsize = max(self._rbufsize, self.default_bufsize) - # Our use of StringIO rather than lists of string objects returned - # by recv() minimizes memory usage and fragmentation that occurs - # when rbufsize is large compared to the typical return value of - # recv(). - buf = self._rbuf - buf.seek(0, 2) # seek end - if size < 0: - # Read until EOF - # reset _rbuf. we consume it via buf. - self._rbuf = io.BytesIO() - while True: - data = self.recv(rbufsize) - if not data: - break - buf.write(data) - return buf.getvalue() - else: - # Read until size bytes or EOF seen, whichever comes first - buf_len = buf.tell() - if buf_len >= size: - # Already have size bytes in our buffer? Extract and - # return. - buf.seek(0) - rv = buf.read(size) - self._rbuf = io.BytesIO() - self._rbuf.write(buf.read()) - return rv - - # reset _rbuf. we consume it via buf. - self._rbuf = io.BytesIO() - while True: - left = size - buf_len - # recv() will malloc the amount of memory given as its - # parameter even though it often returns much less data - # than that. The returned data string is short lived - # as we copy it into a StringIO and free it. This avoids - # fragmentation issues on many platforms. - data = self.recv(left) - if not data: - break - n = len(data) - if n == size and not buf_len: - # Shortcut. Avoid buffer data copies when: - # - We have no data in our buffer. - # AND - # - Our call to recv returned exactly the - # number of bytes we were asked to read. - return data - if n == left: - buf.write(data) - del data # explicit free - break - assert n <= left, 'recv(%d) returned %d bytes' % (left, n) - buf.write(data) - buf_len += n - del data # explicit free - # assert buf_len == buf.tell() - return buf.getvalue() - - def readline(self, size=-1): - """Read line from the socket to buffer.""" - buf = self._rbuf - buf.seek(0, 2) # seek end - if buf.tell() > 0: - # check if we already have it in our buffer - buf.seek(0) - bline = buf.readline(size) - if bline.endswith('\n') or len(bline) == size: - self._rbuf = io.BytesIO() - self._rbuf.write(buf.read()) - return bline - del bline - if size < 0: - # Read until \n or EOF, whichever comes first - if self._rbufsize <= 1: - # Speed up unbuffered case - buf.seek(0) - buffers = [buf.read()] - # reset _rbuf. we consume it via buf. - self._rbuf = io.BytesIO() - data = None - recv = self.recv - while data != '\n': - data = recv(1) - if not data: - break - buffers.append(data) - return ''.join(buffers) - - buf.seek(0, 2) # seek end - # reset _rbuf. we consume it via buf. - self._rbuf = io.BytesIO() - while True: - data = self.recv(self._rbufsize) - if not data: - break - nl = data.find('\n') - if nl >= 0: - nl += 1 - buf.write(data[:nl]) - self._rbuf.write(data[nl:]) - del data - break - buf.write(data) - return buf.getvalue() - - else: - # Read until size bytes or \n or EOF seen, whichever comes - # first - buf.seek(0, 2) # seek end - buf_len = buf.tell() - if buf_len >= size: - buf.seek(0) - rv = buf.read(size) - self._rbuf = io.BytesIO() - self._rbuf.write(buf.read()) - return rv - # reset _rbuf. we consume it via buf. - self._rbuf = io.BytesIO() - while True: - data = self.recv(self._rbufsize) - if not data: - break - left = size - buf_len - # did we just receive a newline? - nl = data.find('\n', 0, left) - if nl >= 0: - nl += 1 - # save the excess data to _rbuf - self._rbuf.write(data[nl:]) - if buf_len: - buf.write(data[:nl]) - break - else: - # Shortcut. Avoid data copy through buf when - # returning a substring of our first recv(). - return data[:nl] - n = len(data) - if n == size and not buf_len: - # Shortcut. Avoid data copy through buf when - # returning exactly all of our first recv(). - return data - if n >= left: - buf.write(data[:left]) - self._rbuf.write(data[left:]) - break - buf.write(data) - buf_len += n - # assert buf_len == buf.tell() - return buf.getvalue() - - def has_data(self): - """Return true if there is buffered data to read.""" - return bool(self._rbuf.getvalue()) - - else: - def read(self, size=-1): - """Read data from the socket to buffer.""" - if size < 0: - # Read until EOF - buffers = [self._rbuf] - self._rbuf = '' - if self._rbufsize <= 1: - recv_size = self.default_bufsize - else: - recv_size = self._rbufsize - - while True: - data = self.recv(recv_size) - if not data: - break - buffers.append(data) - return ''.join(buffers) - else: - # Read until size bytes or EOF seen, whichever comes first - data = self._rbuf - buf_len = len(data) - if buf_len >= size: - self._rbuf = data[size:] - return data[:size] - buffers = [] - if data: - buffers.append(data) - self._rbuf = '' - while True: - left = size - buf_len - recv_size = max(self._rbufsize, left) - data = self.recv(recv_size) - if not data: - break - buffers.append(data) - n = len(data) - if n >= left: - self._rbuf = data[left:] - buffers[-1] = data[:left] - break - buf_len += n - return ''.join(buffers) - - def readline(self, size=-1): - """Read line from the socket to buffer.""" - data = self._rbuf - if size < 0: - # Read until \n or EOF, whichever comes first - if self._rbufsize <= 1: - # Speed up unbuffered case - assert data == '' - buffers = [] - while data != '\n': - data = self.recv(1) - if not data: - break - buffers.append(data) - return ''.join(buffers) - nl = data.find('\n') - if nl >= 0: - nl += 1 - self._rbuf = data[nl:] - return data[:nl] - buffers = [] - if data: - buffers.append(data) - self._rbuf = '' - while True: - data = self.recv(self._rbufsize) - if not data: - break - buffers.append(data) - nl = data.find('\n') - if nl >= 0: - nl += 1 - self._rbuf = data[nl:] - buffers[-1] = data[:nl] - break - return ''.join(buffers) - else: - # Read until size bytes or \n or EOF seen, whichever comes - # first - nl = data.find('\n', 0, size) - if nl >= 0: - nl += 1 - self._rbuf = data[nl:] - return data[:nl] - buf_len = len(data) - if buf_len >= size: - self._rbuf = data[size:] - return data[:size] - buffers = [] - if data: - buffers.append(data) - self._rbuf = '' - while True: - data = self.recv(self._rbufsize) - if not data: - break - buffers.append(data) - left = size - buf_len - nl = data.find('\n', 0, left) - if nl >= 0: - nl += 1 - self._rbuf = data[nl:] - buffers[-1] = data[:nl] - break - n = len(data) - if n >= left: - self._rbuf = data[left:] - buffers[-1] = data[:left] - break - buf_len += n - return ''.join(buffers) - - def has_data(self): - """Return true if there is buffered data to read.""" - return bool(self._rbuf) + def write(self, val, *args, **kwargs): + """Capture bytes written.""" + res = super().write(val, *args, **kwargs) + self.bytes_written += len(val) + return res -if not six.PY2: - class StreamReader(io.BufferedReader): - """Socket stream reader.""" - - def __init__(self, sock, mode='r', bufsize=io.DEFAULT_BUFFER_SIZE): - """Initialize socket stream reader.""" - super().__init__(socket.SocketIO(sock, mode), bufsize) - self.bytes_read = 0 - - def read(self, *args, **kwargs): - """Capture bytes read.""" - val = super().read(*args, **kwargs) - self.bytes_read += len(val) - return val - - def has_data(self): - """Return true if there is buffered data to read.""" - return len(self._read_buf) > self._read_pos - - class StreamWriter(BufferedWriter): - """Socket stream writer.""" - - def __init__(self, sock, mode='w', bufsize=io.DEFAULT_BUFFER_SIZE): - """Initialize socket stream writer.""" - super().__init__(socket.SocketIO(sock, mode), bufsize) - self.bytes_written = 0 - - def write(self, val, *args, **kwargs): - """Capture bytes written.""" - res = super().write(val, *args, **kwargs) - self.bytes_written += len(val) - return res - - def MakeFile(sock, mode='r', bufsize=io.DEFAULT_BUFFER_SIZE): - """File object attached to a socket object.""" - cls = StreamReader if 'r' in mode else StreamWriter - return cls(sock, mode, bufsize) -else: - StreamReader = StreamWriter = MakeFile = MakeFile_PY2 +def MakeFile(sock, mode='r', bufsize=io.DEFAULT_BUFFER_SIZE): + """File object attached to a socket object.""" + cls = StreamReader if 'r' in mode else StreamWriter + return cls(sock, mode, bufsize) diff --git a/lib/cheroot/makefile.pyi b/lib/cheroot/makefile.pyi index 11748505..3f5ea275 100644 --- a/lib/cheroot/makefile.pyi +++ b/lib/cheroot/makefile.pyi @@ -5,19 +5,6 @@ SOCK_WRITE_BLOCKSIZE: int class BufferedWriter(io.BufferedWriter): def write(self, b): ... -class MakeFile_PY2: - bytes_read: int - bytes_written: int - def __init__(self, *args, **kwargs) -> None: ... - def write(self, data) -> None: ... - def send(self, data): ... - def flush(self) -> None: ... - def recv(self, size): ... - class FauxSocket: ... - def read(self, size: int = ...): ... - def readline(self, size: int = ...): ... - def has_data(self): ... - class StreamReader(io.BufferedReader): bytes_read: int def __init__(self, sock, mode: str = ..., bufsize=...) -> None: ... diff --git a/lib/cheroot/server.py b/lib/cheroot/server.py index d92988ab..6b8e37a9 100644 --- a/lib/cheroot/server.py +++ b/lib/cheroot/server.py @@ -65,9 +65,6 @@ And now for a trivial doctest to exercise the test suite True """ -from __future__ import absolute_import, division, print_function -__metaclass__ = type - import os import io import re @@ -78,20 +75,14 @@ import time import traceback as traceback_ import logging import platform +import queue import contextlib import threading - -try: - from functools import lru_cache -except ImportError: - from backports.functools_lru_cache import lru_cache - -import six -from six.moves import queue -from six.moves import urllib +import urllib.parse +from functools import lru_cache from . import connections, errors, __version__ -from ._compat import bton, ntou +from ._compat import bton from ._compat import IS_PPC from .workers import threadpool from .makefile import MakeFile, StreamWriter @@ -606,8 +597,8 @@ class ChunkedRFile: def read_trailer_lines(self): """Read HTTP headers and yield them. - Returns: - Generator: yields CRLF separated lines. + :yields: CRLF separated lines + :ytype: bytes """ if not self.closed: @@ -817,10 +808,6 @@ class HTTPRequest: return False try: - if six.PY2: # FIXME: Figure out better way to do this - # Ref: https://stackoverflow.com/a/196392/595220 (like this?) - """This is a dummy check for unicode in URI.""" - ntou(bton(uri, 'ascii'), 'ascii') scheme, authority, path, qs, fragment = urllib.parse.urlsplit(uri) except UnicodeError: self.simple_response('400 Bad Request', 'Malformed Request-URI') @@ -1120,7 +1107,7 @@ class HTTPRequest: buf.append(CRLF) if msg: - if isinstance(msg, six.text_type): + if isinstance(msg, str): msg = msg.encode('ISO-8859-1') buf.append(msg) @@ -1422,10 +1409,7 @@ class HTTPConnection: https://github.com/daveti/tcpSockHack msdn.microsoft.com/en-us/commandline/wsl/release_notes#build-15025 """ - six.raise_from( # 3.6+: raise RuntimeError from socket_err - RuntimeError, - socket_err, - ) + raise RuntimeError from socket_err else: pid, uid, gid = struct.unpack(PEERCRED_STRUCT_DEF, peer_creds) return pid, uid, gid @@ -1589,7 +1573,7 @@ class HTTPServer: """ keep_alive_conn_limit = 10 - """The maximum number of waiting keep-alive connections that will be kept open. + """Maximum number of waiting keep-alive connections that will be kept open. Default is 10. Set to None to have unlimited connections.""" @@ -1762,13 +1746,13 @@ class HTTPServer: if os.getenv('LISTEN_PID', None): # systemd socket activation self.socket = socket.fromfd(3, socket.AF_INET, socket.SOCK_STREAM) - elif isinstance(self.bind_addr, (six.text_type, six.binary_type)): + elif isinstance(self.bind_addr, (str, bytes)): # AF_UNIX socket try: self.bind_unix_socket(self.bind_addr) except socket.error as serr: msg = '%s -- (%s: %s)' % (msg, self.bind_addr, serr) - six.raise_from(socket.error(msg), serr) + raise socket.error(msg) from serr else: # AF_INET or AF_INET6 socket # Get the correct address family for our host (allows IPv6 @@ -2007,10 +1991,7 @@ class HTTPServer: * https://gavv.github.io/blog/ephemeral-port-reuse/ """ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - if nodelay and not isinstance( - bind_addr, - (six.text_type, six.binary_type), - ): + if nodelay and not isinstance(bind_addr, (str, bytes)): sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) if ssl_adapter is not None: @@ -2059,7 +2040,7 @@ class HTTPServer: """ return bind_addr[:2] - if isinstance(bind_addr, six.binary_type): + if isinstance(bind_addr, bytes): bind_addr = bton(bind_addr) return bind_addr @@ -2109,10 +2090,7 @@ class HTTPServer: sock = getattr(self, 'socket', None) if sock: - if not isinstance( - self.bind_addr, - (six.text_type, six.binary_type), - ): + if not isinstance(self.bind_addr, (str, bytes)): # Touch our own socket to make accept() return immediately. try: host, port = sock.getsockname()[:2] @@ -2179,7 +2157,7 @@ ssl_adapters = { def get_ssl_adapter_class(name='builtin'): """Return an SSL adapter class for the given name.""" adapter = ssl_adapters[name.lower()] - if isinstance(adapter, six.string_types): + if isinstance(adapter, str): last_dot = adapter.rfind('.') attr_name = adapter[last_dot + 1:] mod_path = adapter[:last_dot] diff --git a/lib/cheroot/ssl/__init__.py b/lib/cheroot/ssl/__init__.py index d45fd7f1..19b587d0 100644 --- a/lib/cheroot/ssl/__init__.py +++ b/lib/cheroot/ssl/__init__.py @@ -1,15 +1,9 @@ """Implementation of the SSL adapter base interface.""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - from abc import ABCMeta, abstractmethod -from six import add_metaclass - -@add_metaclass(ABCMeta) -class Adapter: +class Adapter(metaclass=ABCMeta): """Base class for SSL driver library adapters. Required methods: diff --git a/lib/cheroot/ssl/builtin.py b/lib/cheroot/ssl/builtin.py index ff987a71..b22d4ae6 100644 --- a/lib/cheroot/ssl/builtin.py +++ b/lib/cheroot/ssl/builtin.py @@ -7,12 +7,10 @@ To use this module, set ``HTTPServer.ssl_adapter`` to an instance of ``BuiltinSSLAdapter``. """ -from __future__ import absolute_import, division, print_function -__metaclass__ = type - import socket import sys import threading +from contextlib import suppress try: import ssl @@ -27,18 +25,13 @@ except ImportError: except ImportError: DEFAULT_BUFFER_SIZE = -1 -import six - from . import Adapter from .. import errors -from .._compat import IS_ABOVE_OPENSSL10, suppress +from .._compat import IS_ABOVE_OPENSSL10 from ..makefile import StreamReader, StreamWriter from ..server import HTTPServer -if six.PY2: - generic_socket_error = socket.error -else: - generic_socket_error = OSError +generic_socket_error = OSError def _assert_ssl_exc_contains(exc, *msgs): diff --git a/lib/cheroot/ssl/builtin.pyi b/lib/cheroot/ssl/builtin.pyi index fdc656e0..72e45001 100644 --- a/lib/cheroot/ssl/builtin.pyi +++ b/lib/cheroot/ssl/builtin.pyi @@ -1,7 +1,6 @@ from typing import Any from . import Adapter -generic_socket_error: OSError DEFAULT_BUFFER_SIZE: int class BuiltinSSLAdapter(Adapter): @@ -14,5 +13,5 @@ class BuiltinSSLAdapter(Adapter): def context(self, context) -> None: ... def bind(self, sock): ... def wrap(self, sock): ... - def get_environ(self): ... + def get_environ(self, sock): ... def makefile(self, sock, mode: str = ..., bufsize: int = ...): ... diff --git a/lib/cheroot/ssl/pyopenssl.py b/lib/cheroot/ssl/pyopenssl.py index adc9a1ba..548200f7 100644 --- a/lib/cheroot/ssl/pyopenssl.py +++ b/lib/cheroot/ssl/pyopenssl.py @@ -50,16 +50,11 @@ will be read, and the context will be automatically created from them. pyopenssl """ -from __future__ import absolute_import, division, print_function -__metaclass__ = type - import socket import sys import threading import time -import six - try: import OpenSSL.version from OpenSSL import SSL @@ -229,8 +224,7 @@ class SSLConnectionProxyMeta: return type(name, bases, nmspc) -@six.add_metaclass(SSLConnectionProxyMeta) -class SSLConnection: +class SSLConnection(metaclass=SSLConnectionProxyMeta): r"""A thread-safe wrapper for an ``SSL.Connection``. :param tuple args: the arguments to create the wrapped \ diff --git a/lib/cheroot/ssl/pyopenssl.pyi b/lib/cheroot/ssl/pyopenssl.pyi index d5b93471..107675c9 100644 --- a/lib/cheroot/ssl/pyopenssl.pyi +++ b/lib/cheroot/ssl/pyopenssl.pyi @@ -1,9 +1,9 @@ from . import Adapter from ..makefile import StreamReader, StreamWriter from OpenSSL import SSL -from typing import Any +from typing import Any, Type -ssl_conn_type: SSL.Connection +ssl_conn_type: Type[SSL.Connection] class SSLFileobjectMixin: ssl_timeout: int @@ -13,13 +13,13 @@ class SSLFileobjectMixin: def sendall(self, *args, **kwargs): ... def send(self, *args, **kwargs): ... -class SSLFileobjectStreamReader(SSLFileobjectMixin, StreamReader): ... # type:ignore -class SSLFileobjectStreamWriter(SSLFileobjectMixin, StreamWriter): ... # type:ignore +class SSLFileobjectStreamReader(SSLFileobjectMixin, StreamReader): ... # type:ignore[misc] +class SSLFileobjectStreamWriter(SSLFileobjectMixin, StreamWriter): ... # type:ignore[misc] class SSLConnectionProxyMeta: def __new__(mcl, name, bases, nmspc): ... -class SSLConnection(): +class SSLConnection: def __init__(self, *args) -> None: ... class pyOpenSSLAdapter(Adapter): @@ -28,3 +28,4 @@ class pyOpenSSLAdapter(Adapter): def wrap(self, sock): ... def get_environ(self): ... def makefile(self, sock, mode: str = ..., bufsize: int = ...): ... + def get_context(self) -> SSL.Context: ... diff --git a/lib/cheroot/test/_pytest_plugin.py b/lib/cheroot/test/_pytest_plugin.py index 012211df..8ff3b02c 100644 --- a/lib/cheroot/test/_pytest_plugin.py +++ b/lib/cheroot/test/_pytest_plugin.py @@ -8,6 +8,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type import pytest +import six pytest_version = tuple(map(int, pytest.__version__.split('.'))) @@ -43,8 +44,17 @@ def pytest_load_initial_conftests(early_config, parser, args): '= 2.15.4. + # Refs: + # * https://github.com/PyCQA/pylint/issues/6592 + # * https://github.com/PyCQA/pylint/pull/7395 + # pylint: disable-next=too-many-function-args _munge('/привіт'): hello, + # pylint: disable-next=too-many-function-args _munge('/Юххууу'): hello, '/\xa0Ðblah key 0 900 4 data': hello, '/*': asterisk, @@ -151,7 +149,6 @@ def test_parse_acceptable_uri(test_client, uri): assert actual_status == HTTP_OK -@pytest.mark.xfail(six.PY2, reason='Fails on Python 2') def test_parse_uri_unsafe_uri(test_client): """Test that malicious URI does not allow HTTP injection. @@ -263,6 +260,8 @@ def test_no_content_length(test_client): assert actual_status == HTTP_OK assert actual_resp_body == b'Hello world!' + c.close() # deal with the resource warning + def test_content_length_required(test_client): """Test POST query with body failing because of missing Content-Length.""" @@ -278,6 +277,8 @@ def test_content_length_required(test_client): actual_status = response.status assert actual_status == HTTP_LENGTH_REQUIRED + c.close() # deal with the resource warning + @pytest.mark.xfail( reason='https://github.com/cherrypy/cheroot/issues/106', @@ -350,6 +351,8 @@ def test_malformed_http_method(test_client): actual_resp_body = response.read(21) assert actual_resp_body == b'Malformed method name' + c.close() # deal with the resource warning + def test_malformed_header(test_client): """Check that broken HTTP header results in Bad Request.""" @@ -366,6 +369,8 @@ def test_malformed_header(test_client): actual_resp_body = response.read(20) assert actual_resp_body == b'Illegal header line.' + c.close() # deal with the resource warning + def test_request_line_split_issue_1220(test_client): """Check that HTTP request line of exactly 256 chars length is OK.""" diff --git a/lib/cheroot/test/test_dispatch.py b/lib/cheroot/test/test_dispatch.py index 9974fdab..c42014fa 100644 --- a/lib/cheroot/test/test_dispatch.py +++ b/lib/cheroot/test/test_dispatch.py @@ -1,8 +1,4 @@ """Tests for the HTTP server.""" -# -*- coding: utf-8 -*- -# vim: set fileencoding=utf-8 : - -from __future__ import absolute_import, division, print_function from cheroot.wsgi import PathInfoDispatcher diff --git a/lib/cheroot/test/test_makefile.py b/lib/cheroot/test/test_makefile.py index cdded07e..57f6f57e 100644 --- a/lib/cheroot/test/test_makefile.py +++ b/lib/cheroot/test/test_makefile.py @@ -3,9 +3,6 @@ from cheroot import makefile -__metaclass__ = type - - class MockSocket: """A mock socket.""" diff --git a/lib/cheroot/test/test_server.py b/lib/cheroot/test/test_server.py index 8305c78c..5e0a6832 100644 --- a/lib/cheroot/test/test_server.py +++ b/lib/cheroot/test/test_server.py @@ -1,23 +1,18 @@ """Tests for the HTTP server.""" -# -*- coding: utf-8 -*- -# vim: set fileencoding=utf-8 : - -from __future__ import absolute_import, division, print_function -__metaclass__ = type import os +import queue import socket import tempfile import threading import uuid +import urllib.parse # noqa: WPS301 import pytest import requests import requests_unixsocket -import six from pypytools.gc.custom import DefaultGc -from six.moves import queue, urllib from .._compat import bton, ntob from .._compat import IS_LINUX, IS_MACOS, IS_WINDOWS, SYS_PLATFORM @@ -259,12 +254,12 @@ def peercreds_enabled_server(http_server, unix_sock_file): @unix_only_sock_test @non_macos_sock_test -def test_peercreds_unix_sock(peercreds_enabled_server): +def test_peercreds_unix_sock(http_request_timeout, peercreds_enabled_server): """Check that ``PEERCRED`` lookup works when enabled.""" httpserver = peercreds_enabled_server bind_addr = httpserver.bind_addr - if isinstance(bind_addr, six.binary_type): + if isinstance(bind_addr, bytes): bind_addr = bind_addr.decode() # pylint: disable=possibly-unused-variable @@ -275,11 +270,17 @@ def test_peercreds_unix_sock(peercreds_enabled_server): expected_peercreds = '|'.join(map(str, expected_peercreds)) with requests_unixsocket.monkeypatch(): - peercreds_resp = requests.get(unix_base_uri + PEERCRED_IDS_URI) + peercreds_resp = requests.get( + unix_base_uri + PEERCRED_IDS_URI, + timeout=http_request_timeout, + ) peercreds_resp.raise_for_status() assert peercreds_resp.text == expected_peercreds - peercreds_text_resp = requests.get(unix_base_uri + PEERCRED_TEXTS_URI) + peercreds_text_resp = requests.get( + unix_base_uri + PEERCRED_TEXTS_URI, + timeout=http_request_timeout, + ) assert peercreds_text_resp.status_code == 500 @@ -290,14 +291,17 @@ def test_peercreds_unix_sock(peercreds_enabled_server): ) @unix_only_sock_test @non_macos_sock_test -def test_peercreds_unix_sock_with_lookup(peercreds_enabled_server): +def test_peercreds_unix_sock_with_lookup( + http_request_timeout, + peercreds_enabled_server, +): """Check that ``PEERCRED`` resolution works when enabled.""" httpserver = peercreds_enabled_server httpserver.peercreds_resolve_enabled = True bind_addr = httpserver.bind_addr - if isinstance(bind_addr, six.binary_type): + if isinstance(bind_addr, bytes): bind_addr = bind_addr.decode() # pylint: disable=possibly-unused-variable @@ -312,7 +316,10 @@ def test_peercreds_unix_sock_with_lookup(peercreds_enabled_server): ) expected_textcreds = '!'.join(map(str, expected_textcreds)) with requests_unixsocket.monkeypatch(): - peercreds_text_resp = requests.get(unix_base_uri + PEERCRED_TEXTS_URI) + peercreds_text_resp = requests.get( + unix_base_uri + PEERCRED_TEXTS_URI, + timeout=http_request_timeout, + ) peercreds_text_resp.raise_for_status() assert peercreds_text_resp.text == expected_textcreds @@ -363,7 +370,10 @@ def test_high_number_of_file_descriptors(native_server_client, resource_limit): assert any(fn >= resource_limit for fn in native_process_conn.filenos) -if not IS_WINDOWS: +ISSUE511 = IS_MACOS + + +if not IS_WINDOWS and not ISSUE511: test_high_number_of_file_descriptors = pytest.mark.forked( test_high_number_of_file_descriptors, ) diff --git a/lib/cheroot/test/test_ssl.py b/lib/cheroot/test/test_ssl.py index 8da330df..c55e156f 100644 --- a/lib/cheroot/test/test_ssl.py +++ b/lib/cheroot/test/test_ssl.py @@ -1,9 +1,4 @@ """Tests for TLS support.""" -# -*- coding: utf-8 -*- -# vim: set fileencoding=utf-8 : - -from __future__ import absolute_import, division, print_function -__metaclass__ = type import functools import json @@ -14,11 +9,11 @@ import sys import threading import time import traceback +import http.client import OpenSSL.SSL import pytest import requests -import six import trustme from .._compat import bton, ntob, ntou @@ -49,9 +44,6 @@ IS_PYOPENSSL_SSL_VERSION_1_0 = ( OpenSSL.SSL.SSLeay_version(OpenSSL.SSL.SSLEAY_VERSION). startswith(b'OpenSSL 1.0.') ) -PY27 = sys.version_info[:2] == (2, 7) -PY34 = sys.version_info[:2] == (3, 4) -PY3 = not six.PY2 PY310_PLUS = sys.version_info[:2] >= (3, 10) @@ -64,13 +56,12 @@ _stdlib_to_openssl_verify = { fails_under_py3 = pytest.mark.xfail( - not six.PY2, reason='Fails under Python 3+', ) fails_under_py3_in_pypy = pytest.mark.xfail( - not six.PY2 and IS_PYPY, + IS_PYPY, reason='Fails under PyPy3', ) @@ -213,6 +204,7 @@ def thread_exceptions(): ), ) def test_ssl_adapters( + http_request_timeout, tls_http_server, adapter_type, tls_certificate, tls_certificate_chain_pem_path, @@ -241,6 +233,7 @@ def test_ssl_adapters( resp = requests.get( 'https://{host!s}:{port!s}/'.format(host=interface, port=port), + timeout=http_request_timeout, verify=tls_ca_certificate_pem_path, ) @@ -276,8 +269,9 @@ def test_ssl_adapters( reason='Fails under PyPy in CI for unknown reason', strict=False, ) -def test_tls_client_auth( # noqa: C901 # FIXME +def test_tls_client_auth( # noqa: C901, WPS213 # FIXME # FIXME: remove twisted logic, separate tests + http_request_timeout, mocker, tls_http_server, adapter_type, ca, @@ -331,6 +325,9 @@ def test_tls_client_auth( # noqa: C901 # FIXME requests.get, 'https://{host!s}:{port!s}/'.format(host=interface, port=port), + # Don't wait for the first byte forever: + timeout=http_request_timeout, + # Server TLS certificate verification: verify=tls_ca_certificate_pem_path, @@ -348,12 +345,13 @@ def test_tls_client_auth( # noqa: C901 # FIXME and tls_verify_mode == ssl.CERT_REQUIRED and tls_client_identity == 'localhost' and is_trusted_cert - ) or PY34: + ): pytest.xfail( 'OpenSSL 1.0 has problems with verifying client certs', ) assert is_req_successful assert resp.text == 'Hello world!' + resp.close() return # xfail some flaky tests @@ -366,29 +364,16 @@ def test_tls_client_auth( # noqa: C901 # FIXME if issue_237: pytest.xfail('Test sometimes fails') - expected_ssl_errors = ( - requests.exceptions.SSLError, - OpenSSL.SSL.Error, - ) if PY34 else ( - requests.exceptions.SSLError, - ) + expected_ssl_errors = requests.exceptions.SSLError, if IS_WINDOWS or IS_GITHUB_ACTIONS_WORKFLOW: expected_ssl_errors += requests.exceptions.ConnectionError, with pytest.raises(expected_ssl_errors) as ssl_err: - make_https_request() - - if PY34 and isinstance(ssl_err, OpenSSL.SSL.Error): - pytest.xfail( - 'OpenSSL behaves wierdly under Python 3.4 ' - 'because of an outdated urllib3', - ) + make_https_request().close() try: err_text = ssl_err.value.args[0].reason.args[0].args[0] except AttributeError: - if PY34: - pytest.xfail('OpenSSL behaves wierdly under Python 3.4') - elif IS_WINDOWS or IS_GITHUB_ACTIONS_WORKFLOW: + if IS_WINDOWS or IS_GITHUB_ACTIONS_WORKFLOW: err_text = str(ssl_err.value) else: raise @@ -400,9 +385,8 @@ def test_tls_client_auth( # noqa: C901 # FIXME 'sslv3 alert bad certificate' if IS_LIBRESSL_BACKEND else 'tlsv1 alert unknown ca', ) - if not six.PY2: - if IS_MACOS and IS_PYPY and adapter_type == 'pyopenssl': - expected_substrings = ('tlsv1 alert unknown ca',) + if IS_MACOS and IS_PYPY and adapter_type == 'pyopenssl': + expected_substrings = ('tlsv1 alert unknown ca',) if ( tls_verify_mode in ( ssl.CERT_REQUIRED, @@ -469,9 +453,9 @@ def test_tls_client_auth( # noqa: C901 # FIXME pytest.param( 'builtin', marks=pytest.mark.xfail( - IS_GITHUB_ACTIONS_WORKFLOW and IS_MACOS and PY310_PLUS, + IS_MACOS and PY310_PLUS, reason='Unclosed TLS resource warnings happen on macOS ' - 'under Python 3.10', + 'under Python 3.10 (#508)', strict=False, ), ), @@ -492,6 +476,7 @@ def test_ssl_env( # noqa: C901 # FIXME thread_exceptions, recwarn, mocker, + http_request_timeout, tls_http_server, adapter_type, ca, tls_verify_mode, tls_certificate, tls_certificate_chain_pem_path, @@ -532,13 +517,10 @@ def test_ssl_env( # noqa: C901 # FIXME resp = requests.get( 'https://' + interface + ':' + str(port) + '/env', + timeout=http_request_timeout, verify=tls_ca_certificate_pem_path, cert=cl_pem if use_client_cert else None, ) - if PY34 and resp.status_code != 200: - pytest.xfail( - 'Python 3.4 has problems with verifying client certs', - ) env = json.loads(resp.content.decode('utf-8')) @@ -620,7 +602,7 @@ def test_https_over_http_error(http_server, ip_addr): httpserver = http_server.send((ip_addr, EPHEMERAL_PORT)) interface, _host, port = _get_conn_data(httpserver.bind_addr) with pytest.raises(ssl.SSLError) as ssl_err: - six.moves.http_client.HTTPSConnection( + http.client.HTTPSConnection( '{interface}:{port}'.format( interface=interface, port=port, @@ -633,20 +615,10 @@ def test_https_over_http_error(http_server, ip_addr): assert expected_substring in ssl_err.value.args[-1] -http_over_https_error_builtin_marks = [] -if IS_WINDOWS and six.PY2: - http_over_https_error_builtin_marks.append( - pytest.mark.flaky(reruns=5, reruns_delay=2), - ) - - @pytest.mark.parametrize( 'adapter_type', ( - pytest.param( - 'builtin', - marks=http_over_https_error_builtin_marks, - ), + 'builtin', 'pyopenssl', ), ) @@ -657,7 +629,9 @@ if IS_WINDOWS and six.PY2: pytest.param(ANY_INTERFACE_IPV6, marks=missing_ipv6), ), ) +@pytest.mark.flaky(reruns=3, reruns_delay=2) def test_http_over_https_error( + http_request_timeout, tls_http_server, adapter_type, ca, ip_addr, tls_certificate, @@ -697,36 +671,12 @@ def test_http_over_https_error( expect_fallback_response_over_plain_http = ( ( adapter_type == 'pyopenssl' - and (IS_ABOVE_OPENSSL10 or not six.PY2) ) - or PY27 - ) or ( - IS_GITHUB_ACTIONS_WORKFLOW - and IS_WINDOWS - and six.PY2 - and not IS_WIN2016 ) - if ( - IS_GITHUB_ACTIONS_WORKFLOW - and IS_WINDOWS - and six.PY2 - and IS_WIN2016 - and adapter_type == 'builtin' - and ip_addr is ANY_INTERFACE_IPV6 - ): - expect_fallback_response_over_plain_http = True - if ( - IS_GITHUB_ACTIONS_WORKFLOW - and IS_WINDOWS - and six.PY2 - and not IS_WIN2016 - and adapter_type == 'builtin' - and ip_addr is not ANY_INTERFACE_IPV6 - ): - expect_fallback_response_over_plain_http = False if expect_fallback_response_over_plain_http: resp = requests.get( 'http://{host!s}:{port!s}/'.format(host=fqdn, port=port), + timeout=http_request_timeout, ) assert resp.status_code == 400 assert resp.text == ( @@ -738,6 +688,7 @@ def test_http_over_https_error( with pytest.raises(requests.exceptions.ConnectionError) as ssl_err: requests.get( # FIXME: make stdlib ssl behave like PyOpenSSL 'http://{host!s}:{port!s}/'.format(host=fqdn, port=port), + timeout=http_request_timeout, ) if IS_LINUX: diff --git a/lib/cheroot/test/test_wsgi.py b/lib/cheroot/test/test_wsgi.py index 91dfb71e..14005a84 100644 --- a/lib/cheroot/test/test_wsgi.py +++ b/lib/cheroot/test/test_wsgi.py @@ -37,6 +37,7 @@ def simple_wsgi_server(): yield locals() +@pytest.mark.flaky(reruns=3, reruns_delay=2) def test_connection_keepalive(simple_wsgi_server): """Test the connection keepalive works (duh).""" session = Session(base_url=simple_wsgi_server['url']) @@ -59,6 +60,7 @@ def test_connection_keepalive(simple_wsgi_server): ] failures = sum(task.result() for task in tasks) + session.close() assert not failures diff --git a/lib/cheroot/test/webtest.py b/lib/cheroot/test/webtest.py index 118014a6..1630c8ef 100644 --- a/lib/cheroot/test/webtest.py +++ b/lib/cheroot/test/webtest.py @@ -15,9 +15,6 @@ the traceback to stdout, and keep any assertions you have from running be of further significance to your tests). """ -from __future__ import absolute_import, division, print_function -__metaclass__ = type - import pprint import re import socket @@ -29,9 +26,8 @@ import json import unittest # pylint: disable=deprecated-module,preferred-module import warnings import functools - -from six.moves import http_client, map, urllib_parse -import six +import http.client +import urllib.parse from more_itertools.more import always_iterable import jaraco.functools @@ -105,7 +101,7 @@ class WebCase(unittest.TestCase): HOST = '127.0.0.1' PORT = 8000 - HTTP_CONN = http_client.HTTPConnection + HTTP_CONN = http.client.HTTPConnection PROTOCOL = 'HTTP/1.1' scheme = 'http' @@ -127,7 +123,7 @@ class WebCase(unittest.TestCase): * from :py:mod:`python:http.client`. """ cls_name = '{scheme}Connection'.format(scheme=self.scheme.upper()) - return getattr(http_client, cls_name) + return getattr(http.client, cls_name) def get_conn(self, auto_open=False): """Return a connection to our HTTP server.""" @@ -201,9 +197,9 @@ class WebCase(unittest.TestCase): """ ServerError.on = False - if isinstance(url, six.text_type): + if isinstance(url, str): url = url.encode('utf-8') - if isinstance(body, six.text_type): + if isinstance(body, str): body = body.encode('utf-8') # for compatibility, support raise_subcls is None @@ -386,7 +382,7 @@ class WebCase(unittest.TestCase): def assertBody(self, value, msg=None): """Fail if value != self.body.""" - if isinstance(value, six.text_type): + if isinstance(value, str): value = value.encode(self.encoding) if value != self.body: if msg is None: @@ -397,7 +393,7 @@ class WebCase(unittest.TestCase): def assertInBody(self, value, msg=None): """Fail if value not in self.body.""" - if isinstance(value, six.text_type): + if isinstance(value, str): value = value.encode(self.encoding) if value not in self.body: if msg is None: @@ -406,7 +402,7 @@ class WebCase(unittest.TestCase): def assertNotInBody(self, value, msg=None): """Fail if value in self.body.""" - if isinstance(value, six.text_type): + if isinstance(value, str): value = value.encode(self.encoding) if value in self.body: if msg is None: @@ -415,7 +411,7 @@ class WebCase(unittest.TestCase): def assertMatchesBody(self, pattern, msg=None, flags=0): """Fail if value (a regex pattern) is not in self.body.""" - if isinstance(pattern, six.text_type): + if isinstance(pattern, str): pattern = pattern.encode(self.encoding) if re.search(pattern, self.body, flags) is None: if msg is None: @@ -464,25 +460,7 @@ def shb(response): """Return status, headers, body the way we like from a response.""" resp_status_line = '%s %s' % (response.status, response.reason) - if not six.PY2: - return resp_status_line, response.getheaders(), response.read() - - h = [] - key, value = None, None - for line in response.msg.headers: - if line: - if line[0] in ' \t': - value += line.strip() - else: - if key and value: - h.append((key, value)) - key, value = line.split(':', 1) - key = key.strip() - value = value.strip() - if key and value: - h.append((key, value)) - - return resp_status_line, h, response.read() + return resp_status_line, response.getheaders(), response.read() # def openURL(*args, raise_subcls=(), **kwargs): @@ -514,7 +492,7 @@ def openURL(*args, **kwargs): def _open_url_once( url, headers=None, method='GET', body=None, - host='127.0.0.1', port=8000, http_conn=http_client.HTTPConnection, + host='127.0.0.1', port=8000, http_conn=http.client.HTTPConnection, protocol='HTTP/1.1', ssl_context=None, ): """Open the given HTTP resource and return status, headers, and body.""" @@ -530,7 +508,7 @@ def _open_url_once( conn = http_conn(interface(host), port, **kw) conn._http_vsn_str = protocol conn._http_vsn = int(''.join([x for x in protocol if x.isdigit()])) - if not six.PY2 and isinstance(url, bytes): + if isinstance(url, bytes): url = url.decode() conn.putrequest( method.upper(), url, skip_host=True, @@ -572,10 +550,10 @@ def strip_netloc(url): >>> strip_netloc('/foo/bar?bing#baz') '/foo/bar?bing' """ - parsed = urllib_parse.urlparse(url) + parsed = urllib.parse.urlparse(url) _scheme, _netloc, path, params, query, _fragment = parsed stripped = '', '', path, params, query, '' - return urllib_parse.urlunparse(stripped) + return urllib.parse.urlunparse(stripped) # Add any exceptions which your web framework handles diff --git a/lib/cheroot/testing.py b/lib/cheroot/testing.py index c9a6ac99..169142bf 100644 --- a/lib/cheroot/testing.py +++ b/lib/cheroot/testing.py @@ -1,16 +1,13 @@ """Pytest fixtures and other helpers for doing testing by end-users.""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -from contextlib import closing +from contextlib import closing, contextmanager import errno import socket import threading import time +import http.client import pytest -from six.moves import http_client import cheroot.server from cheroot.test import webtest @@ -33,6 +30,7 @@ config = { } +@contextmanager def cheroot_server(server_factory): """Set up and tear down a Cheroot server instance.""" conf = config[server_factory].copy() @@ -64,14 +62,14 @@ def cheroot_server(server_factory): @pytest.fixture def wsgi_server(): """Set up and tear down a Cheroot WSGI server instance.""" - for srv in cheroot_server(cheroot.wsgi.Server): + with cheroot_server(cheroot.wsgi.Server) as srv: yield srv @pytest.fixture def native_server(): """Set up and tear down a Cheroot HTTP server instance.""" - for srv in cheroot_server(cheroot.server.HTTPServer): + with cheroot_server(cheroot.server.HTTPServer) as srv: yield srv @@ -89,9 +87,9 @@ class _TestClient: port=self._port, ) conn_cls = ( - http_client.HTTPConnection + http.client.HTTPConnection if self.server_instance.ssl_adapter is None else - http_client.HTTPSConnection + http.client.HTTPSConnection ) return conn_cls(name) diff --git a/lib/cheroot/workers/threadpool.py b/lib/cheroot/workers/threadpool.py index 795ebc6d..2a9878dc 100644 --- a/lib/cheroot/workers/threadpool.py +++ b/lib/cheroot/workers/threadpool.py @@ -5,17 +5,12 @@ joinable """ -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - import collections import threading import time import socket import warnings - -from six.moves import queue +import queue from jaraco.functools import pass_none @@ -178,7 +173,7 @@ class ThreadPool: for worker in self._threads: worker.name = ( 'CP Server {worker_name!s}'. - format(worker_name=worker.name), + format(worker_name=worker.name) ) worker.start() for worker in self._threads: @@ -228,7 +223,7 @@ class ThreadPool: worker = WorkerThread(self.server) worker.name = ( 'CP Server {worker_name!s}'. - format(worker_name=worker.name), + format(worker_name=worker.name) ) worker.start() return worker diff --git a/lib/cheroot/wsgi.py b/lib/cheroot/wsgi.py index 583d52a9..82faca3e 100644 --- a/lib/cheroot/wsgi.py +++ b/lib/cheroot/wsgi.py @@ -25,14 +25,8 @@ as you want in one instance by using a PathInfoDispatcher:: server = wsgi.Server(addr, d) """ -from __future__ import absolute_import, division, print_function -__metaclass__ = type - import sys -import six -from six.moves import filter - from . import server from .workers import threadpool from ._compat import ntob, bton @@ -140,7 +134,7 @@ class Gateway(server.Gateway): response = self.req.server.wsgi_app(self.env, self.start_response) try: for chunk in filter(None, response): - if not isinstance(chunk, six.binary_type): + if not isinstance(chunk, bytes): raise ValueError('WSGI Applications must yield bytes') self.write(chunk) finally: @@ -149,7 +143,7 @@ class Gateway(server.Gateway): if hasattr(response, 'close'): response.close() - def start_response(self, status, headers, exc_info=None): + def start_response(self, status, headers, exc_info=None): # noqa: WPS238 """WSGI callable to begin the HTTP response.""" # "The application may call start_response more than once, # if and only if the exc_info argument is provided." @@ -164,10 +158,8 @@ class Gateway(server.Gateway): # sent, start_response must raise an error, and should raise the # exc_info tuple." if self.req.sent_headers: - try: - six.reraise(*exc_info) - finally: - exc_info = None + value = exc_info[1] + raise value self.req.status = self._encode_status(status) @@ -196,8 +188,6 @@ class Gateway(server.Gateway): must be of type "str" but are restricted to code points in the "Latin-1" set. """ - if six.PY2: - return status if not isinstance(status, str): raise TypeError('WSGI response status is not of type str.') return status.encode('ISO-8859-1') @@ -273,7 +263,7 @@ class Gateway_10(Gateway): 'wsgi.version': self.version, } - if isinstance(req.server.bind_addr, six.string_types): + if isinstance(req.server.bind_addr, str): # AF_UNIX. This isn't really allowed by WSGI, which doesn't # address unix domain sockets. But it's better than nothing. env['SERVER_PORT'] = '' @@ -332,10 +322,10 @@ class Gateway_u0(Gateway_10): """Return a new environ dict targeting the given wsgi.version.""" req = self.req env_10 = super(Gateway_u0, self).get_environ() - env = dict(map(self._decode_key, env_10.items())) + env = dict(env_10.items()) # Request-URI - enc = env.setdefault(six.u('wsgi.url_encoding'), six.u('utf-8')) + enc = env.setdefault('wsgi.url_encoding', 'utf-8') try: env['PATH_INFO'] = req.path.decode(enc) env['QUERY_STRING'] = req.qs.decode(enc) @@ -345,25 +335,10 @@ class Gateway_u0(Gateway_10): env['PATH_INFO'] = env_10['PATH_INFO'] env['QUERY_STRING'] = env_10['QUERY_STRING'] - env.update(map(self._decode_value, env.items())) + env.update(env.items()) return env - @staticmethod - def _decode_key(item): - k, v = item - if six.PY2: - k = k.decode('ISO-8859-1') - return k, v - - @staticmethod - def _decode_value(item): - k, v = item - skip_keys = 'REQUEST_URI', 'wsgi.input' - if not six.PY2 or not isinstance(v, bytes) or k in skip_keys: - return k, v - return k, v.decode('ISO-8859-1') - wsgi_gateways = Gateway.gateway_map() diff --git a/lib/cheroot/wsgi.pyi b/lib/cheroot/wsgi.pyi index b4851a3d..96075633 100644 --- a/lib/cheroot/wsgi.pyi +++ b/lib/cheroot/wsgi.pyi @@ -40,3 +40,10 @@ class PathInfoDispatcher: apps: Any def __init__(self, apps): ... def __call__(self, environ, start_response): ... + + +WSGIServer = Server +WSGIGateway = Gateway +WSGIGateway_u0 = Gateway_u0 +WSGIGateway_10 = Gateway_10 +WSGIPathInfoDispatcher = PathInfoDispatcher diff --git a/requirements.txt b/requirements.txt index 2a5b1836..dfafa748 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,7 +7,7 @@ backports.zoneinfo==0.2.1 beautifulsoup4==4.11.1 bleach==5.0.1 certifi==2022.9.24 -cheroot==8.6.0 +cheroot==9.0.0 cherrypy==18.8.0 cloudinary==1.30.0 distro==1.8.0 From d596b86c8dee9b45333d887af20e6812f963f99a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Dec 2022 15:59:10 -0800 Subject: [PATCH 11/14] Bump urllib3 from 1.26.12 to 1.26.13 (#1908) * Bump urllib3 from 1.26.12 to 1.26.13 Bumps [urllib3](https://github.com/urllib3/urllib3) from 1.26.12 to 1.26.13. - [Release notes](https://github.com/urllib3/urllib3/releases) - [Changelog](https://github.com/urllib3/urllib3/blob/1.26.13/CHANGES.rst) - [Commits](https://github.com/urllib3/urllib3/compare/1.26.12...1.26.13) --- updated-dependencies: - dependency-name: urllib3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * Update urllib3==1.26.13 Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> [skip ci] --- lib/urllib3/_version.py | 2 +- lib/urllib3/connectionpool.py | 2 +- lib/urllib3/contrib/pyopenssl.py | 7 +++---- lib/urllib3/response.py | 13 +++++++++++++ lib/urllib3/util/retry.py | 2 +- lib/urllib3/util/url.py | 2 +- requirements.txt | 2 +- 7 files changed, 21 insertions(+), 9 deletions(-) diff --git a/lib/urllib3/_version.py b/lib/urllib3/_version.py index 6fbc84b3..308d7f28 100644 --- a/lib/urllib3/_version.py +++ b/lib/urllib3/_version.py @@ -1,2 +1,2 @@ # This file is protected via CODEOWNERS -__version__ = "1.26.12" +__version__ = "1.26.13" diff --git a/lib/urllib3/connectionpool.py b/lib/urllib3/connectionpool.py index 96339e90..70873927 100644 --- a/lib/urllib3/connectionpool.py +++ b/lib/urllib3/connectionpool.py @@ -862,7 +862,7 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): ) # Check if we should retry the HTTP response. - has_retry_after = bool(response.getheader("Retry-After")) + has_retry_after = bool(response.headers.get("Retry-After")) if retries.is_retry(method, response.status, has_retry_after): try: retries = retries.increment(method, url, response=response, _pool=self) diff --git a/lib/urllib3/contrib/pyopenssl.py b/lib/urllib3/contrib/pyopenssl.py index 50a07d59..1ed214b1 100644 --- a/lib/urllib3/contrib/pyopenssl.py +++ b/lib/urllib3/contrib/pyopenssl.py @@ -47,10 +47,10 @@ compression in Python 2 (see `CRIME attack`_). """ from __future__ import absolute_import +import OpenSSL.crypto import OpenSSL.SSL from cryptography import x509 from cryptography.hazmat.backends.openssl import backend as openssl_backend -from cryptography.hazmat.backends.openssl.x509 import _Certificate try: from cryptography.x509 import UnsupportedExtension @@ -228,9 +228,8 @@ def get_subj_alt_name(peer_cert): if hasattr(peer_cert, "to_cryptography"): cert = peer_cert.to_cryptography() else: - # This is technically using private APIs, but should work across all - # relevant versions before PyOpenSSL got a proper API for this. - cert = _Certificate(openssl_backend, peer_cert._x509) + der = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_ASN1, peer_cert) + cert = x509.load_der_x509_certificate(der, openssl_backend) # We want to find the SAN extension. Ask Cryptography to locate it (it's # faster than looping in Python) diff --git a/lib/urllib3/response.py b/lib/urllib3/response.py index 01f08eee..8f1b4fa8 100644 --- a/lib/urllib3/response.py +++ b/lib/urllib3/response.py @@ -3,6 +3,7 @@ from __future__ import absolute_import import io import logging import sys +import warnings import zlib from contextlib import contextmanager from socket import error as SocketError @@ -663,9 +664,21 @@ class HTTPResponse(io.IOBase): # Backwards-compatibility methods for http.client.HTTPResponse def getheaders(self): + warnings.warn( + "HTTPResponse.getheaders() is deprecated and will be removed " + "in urllib3 v2.1.0. Instead access HTTResponse.headers directly.", + category=DeprecationWarning, + stacklevel=2, + ) return self.headers def getheader(self, name, default=None): + warnings.warn( + "HTTPResponse.getheader() is deprecated and will be removed " + "in urllib3 v2.1.0. Instead use HTTResponse.headers.get(name, default).", + category=DeprecationWarning, + stacklevel=2, + ) return self.headers.get(name, default) # Backwards compatibility for http.cookiejar diff --git a/lib/urllib3/util/retry.py b/lib/urllib3/util/retry.py index 3398323f..2490d5e5 100644 --- a/lib/urllib3/util/retry.py +++ b/lib/urllib3/util/retry.py @@ -394,7 +394,7 @@ class Retry(object): def get_retry_after(self, response): """Get the value of Retry-After in seconds.""" - retry_after = response.getheader("Retry-After") + retry_after = response.headers.get("Retry-After") if retry_after is None: return None diff --git a/lib/urllib3/util/url.py b/lib/urllib3/util/url.py index b667c160..94f1b8d4 100644 --- a/lib/urllib3/util/url.py +++ b/lib/urllib3/util/url.py @@ -63,7 +63,7 @@ IPV6_ADDRZ_RE = re.compile("^" + IPV6_ADDRZ_PAT + "$") BRACELESS_IPV6_ADDRZ_RE = re.compile("^" + IPV6_ADDRZ_PAT[2:-2] + "$") ZONE_ID_RE = re.compile("(" + ZONE_ID_PAT + r")\]$") -_HOST_PORT_PAT = ("^(%s|%s|%s)(?::([0-9]{0,5}))?$") % ( +_HOST_PORT_PAT = ("^(%s|%s|%s)(?::0*([0-9]{0,5}))?$") % ( REG_NAME_PAT, IPV4_PAT, IPV6_ADDRZ_PAT, diff --git a/requirements.txt b/requirements.txt index dfafa748..727bd6db 100644 --- a/requirements.txt +++ b/requirements.txt @@ -45,7 +45,7 @@ tempora==5.1.0 tokenize-rt==5.0.0 tzdata==2022.6 tzlocal==4.2 -urllib3==1.26.12 +urllib3==1.26.13 webencodings==0.5.1 websocket-client==1.4.2 xmltodict==0.13.0 From e646c0e6ebb5f2fb7a56e05b2dec16fbc6d6e6c1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Dec 2022 15:59:38 -0800 Subject: [PATCH 12/14] Bump zipp from 3.10.0 to 3.11.0 (#1912) * Bump zipp from 3.10.0 to 3.11.0 Bumps [zipp](https://github.com/jaraco/zipp) from 3.10.0 to 3.11.0. - [Release notes](https://github.com/jaraco/zipp/releases) - [Changelog](https://github.com/jaraco/zipp/blob/main/CHANGES.rst) - [Commits](https://github.com/jaraco/zipp/compare/v3.10.0...v3.11.0) --- updated-dependencies: - dependency-name: zipp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Update zipp==3.11.0 Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> [skip ci] --- lib/zipp/__init__.py | 46 ++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 2 +- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/lib/zipp/__init__.py b/lib/zipp/__init__.py index c1f3632e..ad01e27e 100644 --- a/lib/zipp/__init__.py +++ b/lib/zipp/__init__.py @@ -4,6 +4,8 @@ import zipfile import itertools import contextlib import pathlib +import re +import fnmatch from .py310compat import text_encoding @@ -243,6 +245,18 @@ class Path: self.root = FastLookup.make(root) self.at = at + def __eq__(self, other): + """ + >>> Path(zipfile.ZipFile(io.BytesIO(), 'w')) == 'foo' + False + """ + if self.__class__ is not other.__class__: + return NotImplemented + return (self.root, self.at) == (other.root, other.at) + + def __hash__(self): + return hash((self.root, self.at)) + def open(self, mode='r', *args, pwd=None, **kwargs): """ Open this entry as text or binary following the semantics @@ -313,6 +327,38 @@ class Path: subs = map(self._next, self.root.namelist()) return filter(self._is_child, subs) + def match(self, path_pattern): + return pathlib.Path(self.at).match(path_pattern) + + def is_symlink(self): + """ + Return whether this path is a symlink. Always false (python/cpython#82102). + """ + return False + + def _descendants(self): + for child in self.iterdir(): + yield child + if child.is_dir(): + yield from child._descendants() + + def glob(self, pattern): + if not pattern: + raise ValueError("Unacceptable pattern: {!r}".format(pattern)) + + matches = re.compile(fnmatch.translate(pattern)).fullmatch + return ( + child + for child in self._descendants() + if matches(str(child.relative_to(self))) + ) + + def rglob(self, pattern): + return self.glob(f'**/{pattern}') + + def relative_to(self, other, *extra): + return posixpath.relpath(str(self), str(other.joinpath(*extra))) + def __str__(self): return posixpath.join(self.root.filename, self.at) diff --git a/requirements.txt b/requirements.txt index 727bd6db..6de9a262 100644 --- a/requirements.txt +++ b/requirements.txt @@ -49,7 +49,7 @@ urllib3==1.26.13 webencodings==0.5.1 websocket-client==1.4.2 xmltodict==0.13.0 -zipp==3.10.0 +zipp==3.11.0 # configobj==5.1.0 # sgmllib3k==1.0.0 From c61f85431822a26b4b8a4b17b153a612ee00202d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Dec 2022 15:59:53 -0800 Subject: [PATCH 13/14] Bump tzdata from 2022.6 to 2022.7 (#1915) * Bump tzdata from 2022.6 to 2022.7 Bumps [tzdata](https://github.com/python/tzdata) from 2022.6 to 2022.7. - [Release notes](https://github.com/python/tzdata/releases) - [Changelog](https://github.com/python/tzdata/blob/master/NEWS.md) - [Commits](https://github.com/python/tzdata/compare/2022.6...2022.7) --- updated-dependencies: - dependency-name: tzdata dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Update tzdata==2022.7 Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> [skip ci] --- lib/tzdata/__init__.py | 4 +- lib/tzdata/zoneinfo/America/Bogota | Bin 179 -> 179 bytes lib/tzdata/zoneinfo/America/Cambridge_Bay | Bin 768 -> 883 bytes lib/tzdata/zoneinfo/America/Ciudad_Juarez | Bin 0 -> 718 bytes lib/tzdata/zoneinfo/America/Godthab | Bin 465 -> 931 bytes lib/tzdata/zoneinfo/America/Inuvik | Bin 701 -> 817 bytes lib/tzdata/zoneinfo/America/Iqaluit | Bin 740 -> 855 bytes lib/tzdata/zoneinfo/America/Nuuk | Bin 465 -> 931 bytes lib/tzdata/zoneinfo/America/Ojinaga | Bin 691 -> 709 bytes lib/tzdata/zoneinfo/America/Pangnirtung | Bin 769 -> 855 bytes lib/tzdata/zoneinfo/America/Rankin_Inlet | Bin 692 -> 807 bytes lib/tzdata/zoneinfo/America/Resolute | Bin 692 -> 807 bytes lib/tzdata/zoneinfo/America/Whitehorse | Bin 1029 -> 1029 bytes lib/tzdata/zoneinfo/America/Yellowknife | Bin 729 -> 844 bytes lib/tzdata/zoneinfo/Asia/Kuala_Lumpur | Bin 256 -> 256 bytes lib/tzdata/zoneinfo/Asia/Singapore | Bin 256 -> 256 bytes lib/tzdata/zoneinfo/Canada/Yukon | Bin 1029 -> 1029 bytes lib/tzdata/zoneinfo/Singapore | Bin 256 -> 256 bytes lib/tzdata/zoneinfo/iso3166.tab | 6 +-- lib/tzdata/zoneinfo/tzdata.zi | 47 +++++++++++++--------- lib/tzdata/zoneinfo/zone.tab | 26 ++++++------ lib/tzdata/zoneinfo/zone1970.tab | 26 ++++++------ lib/tzdata/zones | 3 +- requirements.txt | 2 +- 24 files changed, 63 insertions(+), 51 deletions(-) create mode 100644 lib/tzdata/zoneinfo/America/Ciudad_Juarez diff --git a/lib/tzdata/__init__.py b/lib/tzdata/__init__.py index c3851325..96456857 100644 --- a/lib/tzdata/__init__.py +++ b/lib/tzdata/__init__.py @@ -1,6 +1,6 @@ # IANA versions like 2020a are not valid PEP 440 identifiers; the recommended # way to translate the version is to use YYYY.n where `n` is a 0-based index. -__version__ = "2022.6" +__version__ = "2022.7" # This exposes the original IANA version number. -IANA_VERSION = "2022f" +IANA_VERSION = "2022g" diff --git a/lib/tzdata/zoneinfo/America/Bogota b/lib/tzdata/zoneinfo/America/Bogota index 6cb53d4e6125c0541eb92871b4f03fda450a6d2e..85b903333eb6325aa8343f6e9aee38447495303f 100644 GIT binary patch delta 12 TcmdnYxS4T64O2-L!F;}*3&DK;l?wzw41t-=V7_2y6PPbl znaaqpvHuvO8w)cTkd2j{4Mp4JUM4A()e9H}{{P>(fl=iD|J4f^BtUEi8C?Sd2H)@y O2HyZ64xW6CNecj6RWauP delta 153 zcmey&*1$F)jMI*Rfq@fb_v@A^>%Ao_xYg1k1<-XG7|;a**Mry6i()5 xmeSw3fe~ox>IICV|NjHok{~vNEQrmZsB2)r;2R#o;2QwM!9eWdGTD<^3jm(^DUSdE diff --git a/lib/tzdata/zoneinfo/America/Ciudad_Juarez b/lib/tzdata/zoneinfo/America/Ciudad_Juarez new file mode 100644 index 0000000000000000000000000000000000000000..f636ee643fe49a583fb2db3ff8408c341a06e8d3 GIT binary patch literal 718 zcmb8n%PYiD6bJD88zY(~CfSh`r8I^a#``_Pc>O$oromu5A}JJEky*%OWmwpFWI+}d zCTWneV<{|zg%l!LphS6G_k8^Ua&G7MJ)hgTKc%s!%f$XHNo-J4DSto75o>{>!?~yx z|NQ{v`b*yxrN7JWqTI5aAh)%tzxg%sME^H-4^uGHVDJECvduxkQfePyb6kmzoNBso zQ>F#Q!|4%kM3#Q1g)=_R;mp@CFAZ7#V>sKVfpc~j;M}Q4IB#wY&hI~j3&!W%bWvF4 zgDst-ur+Z8F0!`5#i2v6Ew%u*y-(}JGghjOflJQ@;j)`RxO{#E`xU27SRV1fmHu3~ z%Gm=~&ql*F+2wHUunMk=On{a88(8@ah3)18*nX?mF;R}FYYOJr4}lxL_u!>8o z=k<(nzTjnQ&exV$FnuL0=Q}=0S0O7*u2Yyrv}_eAS|cvf1oU#U!DL9%%SNNYXh_oh E0Js*Lo&W#< literal 0 HcmV?d00001 diff --git a/lib/tzdata/zoneinfo/America/Godthab b/lib/tzdata/zoneinfo/America/Godthab index 4ddc99d8b74c4b525263ca1337cb57d1a15692c0..79d7a45464b3f1dac470b9ea5f6a12b1344c8173 100644 GIT binary patch literal 931 zcmcJ{-Ahwp9Ki9j=17JYr_7hy%xbM^&03q8+SF{hQEg^+s%5TaE-4sZNJJNgM$$!$ zIKc?QBBSUc%x*?qs6|9jWERAT-Y6Iqz0i$GQOM~$-~9nz^&Fn(!xzr)r?egEa!a!w zgZ_uO;wZC^g!SKY^qZ&YXR!aU7?bity)D88o_N-r$G%fvH d%F(pU2ScuSrQBdhkt$ps{kkQySAXl>=6^d@io>n)ico7HPZ1lG=MM-xd6lX B9dZBw diff --git a/lib/tzdata/zoneinfo/America/Inuvik b/lib/tzdata/zoneinfo/America/Inuvik index af3107db51e3848767a443adf8b3c87c55579061..86639f6ecb4c424911e5bdbca81a9e39c86e850c 100644 GIT binary patch delta 218 zcmdnXx{+-{SiJ`W0|P4%i~I+I2W)-~KsEzQqWS_5%^Jux0YtMoaxMVT>;`YZd=7=z zU_Ph7IWV7#;VhWX^6O1Mh)}U_Rf@^g9H%G=5Qa(XE!(m=5r_<1oJrs zmVo(O42!{ht`BWsKKFxGFrVkzi8D&* T8W=FR281xU28T@E&7=tcn1eh_ delta 172 zcmcc4_Jnmp7^e*b0|N&TD^84;5cm%S-}UVRK=cod%nKX8?_jKFWn^MzVj&K)v7@R3 z8Vm&g{~tfV$nyXH&J7G4|NmdTz{mp@5d@Q>|NpOEz#s{dWt7!5Fko;E2w`vy24WW% K5HZ=1Sq%X7!ZD2i diff --git a/lib/tzdata/zoneinfo/America/Nuuk b/lib/tzdata/zoneinfo/America/Nuuk index 4ddc99d8b74c4b525263ca1337cb57d1a15692c0..79d7a45464b3f1dac470b9ea5f6a12b1344c8173 100644 GIT binary patch literal 931 zcmcJ{-Ahwp9Ki9j=17JYr_7hy%xbM^&03q8+SF{hQEg^+s%5TaE-4sZNJJNgM$$!$ zIKc?QBBSUc%x*?qs6|9jWERAT-Y6Iqz0i$GQOM~$-~9nz^&Fn(!xzr)r?egEa!a!w zgZ_uO;wZC^g!SKY^qZ&YXR!aU7?bity)D88o_N-r$G%fvH d%F(pU2ScuSrQBdhkt$ps{kkQySAXl>=6^d@io>n)ico7HPZ1lG=MM-xd6lX B9dZBw diff --git a/lib/tzdata/zoneinfo/America/Ojinaga b/lib/tzdata/zoneinfo/America/Ojinaga index 560b8674f7e16a7e117f4e0c5f9a6a13adc1867a..2fc74e947389cf4371baece8ac66945df1b208fe 100644 GIT binary patch delta 27 icmdnYdX#m;CMIELmk=FaV?851107#OLp?)111Bk;*(-Qd5Txk+N7PC8y{8-#iKZhi9Jm_ssJ$N>il8#}15% zHhA$U2VbeCH49a`L0P#o+QIwB>^~a1x~tPSW>)`dyvNq-D(`V+Utx><61HY8!SRkU zSYS_KA=(4m^lsR`{zPY#gvBs?c)klh@~kt;=Z>KkIPuXf_-I=td@ND{CtY{L$yFkJ zyu=Ql5N)tCOH>&p#kvWn#@OK0Z9(Jnv~Qbm`sy$E8xBYLp6}icoL^A|7hK(d{jMCi(6;~=SrXvlv`;#t6#v-cjg{;! zV}Ab4D*A=bBRZd7oZixSzx1L*=lycu2e@?jCR}!R4leI{pz`lm>IUIJ^9?wd*9wP% z0XXz~GHN!(9QwZ}Hg5kt`XR+I-Y%NVhA}_GtT@~8b{NLgW0u6{CTFfJvrxUlLJd?w es%r`h&^bV7frfq>dTR3}pCo%~y30Hzb(UI5YG z^$i?A^bd{b13*0t!kLy2K(t7t%molF>M8vIM2lJc1M|f-{(|`uO40!!aY>H*V7?UN zUocrw2gg42q4dV7^l56);~p zYvBcuxQgjEFkjWH8O&D`odM>nYo&tu>i@PT0F^Ola=C-~n)jxI`C6|u!F=s~m0-Tk zWw!v3I^B8EV7}gF4KQE7&KAr!m@EON4b#&<0F^Nq75xJ9jom%Le3PI%V7{rkHkfZ_ zv=7WTXXgd;Eu?QfU;qM(Xa7JHgXQm2U~#Ks7s0gk?RgJC;x_Bw6@X~l-3<>wv|axN zFyDT55}5ChzX{BDY^hHG$vgQ^0rQ!38j%L*YD_ z&nd78%;#d*2W^W5C<0jQ0E_dqF_&$n|Dn9sj*5=fXqV5U$BSU|9| z1S}v_8TMgg`EJH~MkcZ#Gm0*-kqrO;@7%!1^8f$p1q>V@HUp2YfdPZFO9+E=a0r7d Z5OV<;WKZJp&zI!^!KI*Z}^|IW7PI delta 149 zcmZ3^wuN;k0|0BuDaQZ+ diff --git a/lib/tzdata/zoneinfo/America/Resolute b/lib/tzdata/zoneinfo/America/Resolute index a84d1dfdb3a820a10f52a8fd4d9b4bf39d663132..97eb8a9c1fbbf56b8e32a1bea34f68e263e2a9d7 100644 GIT binary patch delta 289 zcmdnOx}0r7SiJ`W0|N^X3;YLytG^o?fNTbqL~bz68t|?F#AkDO3+A&MTmbVq6wZVB zoC2G`d@hEKU_RG}DPTVLgUMh%&&>@V7=VEHKq;8dw{sGh&%bgKNSHxjrceo3K(Mm} zEFe@F_F-fBZpL~>CbA$ik}hUOu#pV^|L@$u$nyXH>IDoOAT|S!u7LrAvr7nrb8rZQ aD-d%58D>BM9baQTBRvBhU&G1knAiaMIXWKz delta 149 zcmZ3^wuN;S_P#}6>F{Qtjt0R!j%|2sD@@`6YPL0tm_24@$S5C-R9Aap9T=k=I|TLXE(S9=5r|A1@k!t z4uJVw4Ew=+t`CdAeC`Je!F-;Z`+>R{7h!!F<8aCNN*9 zGL?~GWB)$JdKP9XK(N^i|NpOEz{mn7IsX5jJAr`*%ofl!FktWv4`J{P0ODXEb_rqN N0y4~jjL9dN1OTZ3HEaL? delta 172 zcmX@Zc9V5N7^f`*0|OfnOHPbe5cm%S-}R#kK=cod)(;#1?_;cIWhM&(%>;t~|5qL|NqaOz`*nW|IQ7JK%0Q#qPhkK48GwZ488$C91O%RE+8U=feXkp2QqYgjVIeP G^8x?^5iT+S diff --git a/lib/tzdata/zoneinfo/Asia/Kuala_Lumpur b/lib/tzdata/zoneinfo/Asia/Kuala_Lumpur index 350d77e28ee770be54bff6aea7f03ebbb82effaa..dbbdea3c8149004cfd525a0fc26e5da72b20e8a1 100644 GIT binary patch delta 12 TcmZo*YG9f$m+2kD#QBK;90LTi delta 12 TcmZo*YG9f$m+33V#QBK;97Y7f diff --git a/lib/tzdata/zoneinfo/Asia/Singapore b/lib/tzdata/zoneinfo/Asia/Singapore index 350d77e28ee770be54bff6aea7f03ebbb82effaa..dbbdea3c8149004cfd525a0fc26e5da72b20e8a1 100644 GIT binary patch delta 12 TcmZo*YG9f$m+2kD#QBK;90LTi delta 12 TcmZo*YG9f$m+33V#QBK;97Y7f diff --git a/lib/tzdata/zoneinfo/Canada/Yukon b/lib/tzdata/zoneinfo/Canada/Yukon index 878b6a92f7406b2568b34de88e0c31cc4499d10e..40baa9aba2a879f7a38a5a0f67e16e7a2d677a5e 100644 GIT binary patch delta 15 XcmZqWXyur&ndQgPmI)iT`!NFmHBbiZ delta 15 WcmZqWXyur&ndP@^yuiloe#`(Zas{FQ diff --git a/lib/tzdata/zoneinfo/Singapore b/lib/tzdata/zoneinfo/Singapore index 350d77e28ee770be54bff6aea7f03ebbb82effaa..dbbdea3c8149004cfd525a0fc26e5da72b20e8a1 100644 GIT binary patch delta 12 TcmZo*YG9f$m+2kD#QBK;90LTi delta 12 TcmZo*YG9f$m+33V#QBK;97Y7f diff --git a/lib/tzdata/zoneinfo/iso3166.tab b/lib/tzdata/zoneinfo/iso3166.tab index a4ff61a4..911af5e8 100644 --- a/lib/tzdata/zoneinfo/iso3166.tab +++ b/lib/tzdata/zoneinfo/iso3166.tab @@ -3,13 +3,13 @@ # This file is in the public domain, so clarified as of # 2009-05-17 by Arthur David Olson. # -# From Paul Eggert (2015-05-02): +# From Paul Eggert (2022-11-18): # This file contains a table of two-letter country codes. Columns are # separated by a single tab. Lines beginning with '#' are comments. # All text uses UTF-8 encoding. The columns of the table are as follows: # # 1. ISO 3166-1 alpha-2 country code, current as of -# ISO 3166-1 N976 (2018-11-06). See: Updates on ISO 3166-1 +# ISO 3166-1 N1087 (2022-09-02). See: Updates on ISO 3166-1 # https://isotc.iso.org/livelink/livelink/Open/16944257 # 2. The usual English name for the coded region, # chosen so that alphabetic sorting of subsets produces helpful lists. @@ -238,7 +238,7 @@ SY Syria SZ Eswatini (Swaziland) TC Turks & Caicos Is TD Chad -TF French Southern & Antarctic Lands +TF French Southern Territories TG Togo TH Thailand TJ Tajikistan diff --git a/lib/tzdata/zoneinfo/tzdata.zi b/lib/tzdata/zoneinfo/tzdata.zi index a36449f4..3db1460e 100644 --- a/lib/tzdata/zoneinfo/tzdata.zi +++ b/lib/tzdata/zoneinfo/tzdata.zi @@ -1,4 +1,4 @@ -# version 2022f +# version 2022g # This zic input file is in the public domain. R d 1916 o - Jun 14 23s 1 S R d 1916 1919 - O Su>=1 23s 0 - @@ -1040,7 +1040,7 @@ Z Asia/Singapore 6:55:25 - LMT 1901 7:20 - +0720 1941 S 7:30 - +0730 1942 F 16 9 - +09 1945 S 12 -7:30 - +0730 1982 +7:30 - +0730 1981 D 31 16u 8 - +08 Z Asia/Colombo 5:19:24 - LMT 1880 5:19:32 - MMT 1906 @@ -1754,7 +1754,8 @@ Z America/Scoresbysund -1:27:52 - LMT 1916 Jul 28 -1 E -01/+00 Z America/Nuuk -3:26:56 - LMT 1916 Jul 28 -3 - -03 1980 Ap 6 2 --3 E -03/-02 +-3 E -03/-02 2023 Mar 25 22 +-2 - -02 Z America/Thule -4:35:8 - LMT 1916 Jul 28 -4 Th A%sT Z Europe/Tallinn 1:39 - LMT 1880 @@ -3044,16 +3045,11 @@ R Y 1919 o - N 1 0 0 S R Y 1942 o - F 9 2 1 W R Y 1945 o - Au 14 23u 1 P R Y 1945 o - S 30 2 0 S -R Y 1965 o - Ap lastSu 0 2 DD -R Y 1965 o - O lastSu 2 0 S -R Y 1980 1986 - Ap lastSu 2 1 D -R Y 1980 2006 - O lastSu 2 0 S +R Y 1972 1986 - Ap lastSu 2 1 D +R Y 1972 2006 - O lastSu 2 0 S R Y 1987 2006 - Ap Su>=1 2 1 D -Z America/Pangnirtung 0 - -00 1921 --4 Y A%sT 1995 Ap Su>=1 2 --5 C E%sT 1999 O 31 2 --6 C C%sT 2000 O 29 2 --5 C E%sT +R Yu 1965 o - Ap lastSu 0 2 DD +R Yu 1965 o - O lastSu 2 0 S Z America/Iqaluit 0 - -00 1942 Au -5 Y E%sT 1999 O 31 2 -6 C C%sT 2000 O 29 2 @@ -3082,13 +3078,15 @@ Z America/Inuvik 0 - -00 1953 -7 Y M%sT 1980 -7 C M%sT Z America/Whitehorse -9:0:12 - LMT 1900 Au 20 --9 Y Y%sT 1967 May 28 --8 Y P%sT 1980 +-9 Y Y%sT 1965 +-9 Yu Y%sT 1966 F 27 +-8 - PST 1980 -8 C P%sT 2020 N -7 - MST Z America/Dawson -9:17:40 - LMT 1900 Au 20 --9 Y Y%sT 1973 O 28 --8 Y P%sT 1980 +-9 Y Y%sT 1965 +-9 Yu Y%sT 1973 O 28 +-8 - PST 1980 -8 C P%sT 2020 N -7 - MST R m 1931 o - May 1 23 1 D @@ -3132,6 +3130,17 @@ Z America/Mexico_City -6:36:36 - LMT 1922 Ja 1 7u -6 m C%sT 2001 S 30 2 -6 - CST 2002 F 20 -6 m C%sT +Z America/Ciudad_Juarez -7:5:56 - LMT 1922 Ja 1 7u +-7 - MST 1927 Jun 10 23 +-6 - CST 1930 N 15 +-7 m M%sT 1932 Ap +-6 - CST 1996 +-6 m C%sT 1998 +-6 - CST 1998 Ap Su>=1 3 +-7 m M%sT 2010 +-7 u M%sT 2022 O 30 2 +-6 - CST 2022 N 30 +-7 u M%sT Z America/Ojinaga -6:57:40 - LMT 1922 Ja 1 7u -7 - MST 1927 Jun 10 23 -6 - CST 1930 N 15 @@ -3141,7 +3150,8 @@ Z America/Ojinaga -6:57:40 - LMT 1922 Ja 1 7u -6 - CST 1998 Ap Su>=1 3 -7 m M%sT 2010 -7 u M%sT 2022 O 30 2 --6 - CST +-6 - CST 2022 N 30 +-6 u C%sT Z America/Chihuahua -7:4:20 - LMT 1922 Ja 1 7u -7 - MST 1927 Jun 10 23 -6 - CST 1930 N 15 @@ -3771,7 +3781,7 @@ Z Antarctica/Palmer 0 - -00 1965 -4 x -04/-03 2016 D 4 -3 - -03 R CO 1992 o - May 3 0 1 - -R CO 1993 o - Ap 4 0 0 - +R CO 1993 o - F 6 24 0 - Z America/Bogota -4:56:16 - LMT 1884 Mar 13 -4:56:16 - BMT 1914 N 23 -5 CO -05/-04 @@ -4154,6 +4164,7 @@ L America/Tijuana America/Ensenada L America/Indiana/Indianapolis America/Fort_Wayne L America/Toronto America/Montreal L America/Toronto America/Nipigon +L America/Iqaluit America/Pangnirtung L America/Rio_Branco America/Porto_Acre L America/Winnipeg America/Rainy_River L America/Argentina/Cordoba America/Rosario diff --git a/lib/tzdata/zoneinfo/zone.tab b/lib/tzdata/zoneinfo/zone.tab index 2636e21a..6e5adb9f 100644 --- a/lib/tzdata/zoneinfo/zone.tab +++ b/lib/tzdata/zoneinfo/zone.tab @@ -114,8 +114,7 @@ CA +4606-06447 America/Moncton Atlantic - New Brunswick CA +5320-06025 America/Goose_Bay Atlantic - Labrador (most areas) CA +5125-05707 America/Blanc-Sablon AST - QC (Lower North Shore) CA +4339-07923 America/Toronto Eastern - ON, QC (most areas) -CA +6344-06828 America/Iqaluit Eastern - NU (most east areas) -CA +6608-06544 America/Pangnirtung Eastern - NU (Pangnirtung) +CA +6344-06828 America/Iqaluit Eastern - NU (most areas) CA +484531-0913718 America/Atikokan EST - ON (Atikokan); NU (Coral H) CA +4953-09709 America/Winnipeg Central - ON (west); Manitoba CA +744144-0944945 America/Resolute Central - NU (Resolute) @@ -277,17 +276,18 @@ MT +3554+01431 Europe/Malta MU -2010+05730 Indian/Mauritius MV +0410+07330 Indian/Maldives MW -1547+03500 Africa/Blantyre -MX +1924-09909 America/Mexico_City Central Time -MX +2105-08646 America/Cancun Eastern Standard Time - Quintana Roo -MX +2058-08937 America/Merida Central Time - Campeche, Yucatan -MX +2540-10019 America/Monterrey Central Time - Durango; Coahuila, Nuevo Leon, Tamaulipas (most areas) -MX +2550-09730 America/Matamoros Central Time US - Coahuila, Nuevo Leon, Tamaulipas (US border) -MX +2313-10625 America/Mazatlan Mountain Time - Baja California Sur, Nayarit, Sinaloa -MX +2838-10605 America/Chihuahua Mountain Time - Chihuahua (most areas) -MX +2934-10425 America/Ojinaga Mountain Time US - Chihuahua (US border) -MX +2904-11058 America/Hermosillo Mountain Standard Time - Sonora -MX +3232-11701 America/Tijuana Pacific Time US - Baja California -MX +2048-10515 America/Bahia_Banderas Central Time - Bahia de Banderas +MX +1924-09909 America/Mexico_City Central Mexico +MX +2105-08646 America/Cancun Quintana Roo +MX +2058-08937 America/Merida Campeche, Yucatan +MX +2540-10019 America/Monterrey Durango; Coahuila, Nuevo Leon, Tamaulipas (most areas) +MX +2550-09730 America/Matamoros Coahuila, Nuevo Leon, Tamaulipas (US border) +MX +2838-10605 America/Chihuahua Chihuahua (most areas) +MX +3144-10629 America/Ciudad_Juarez Chihuahua (US border - west) +MX +2934-10425 America/Ojinaga Chihuahua (US border - east) +MX +2313-10625 America/Mazatlan Baja California Sur, Nayarit (most areas), Sinaloa +MX +2048-10515 America/Bahia_Banderas Bahia de Banderas +MX +2904-11058 America/Hermosillo Sonora +MX +3232-11701 America/Tijuana Baja California MY +0310+10142 Asia/Kuala_Lumpur Malaysia (peninsula) MY +0133+11020 Asia/Kuching Sabah, Sarawak MZ -2558+03235 Africa/Maputo diff --git a/lib/tzdata/zoneinfo/zone1970.tab b/lib/tzdata/zoneinfo/zone1970.tab index 75372e3f..a9b36d36 100644 --- a/lib/tzdata/zoneinfo/zone1970.tab +++ b/lib/tzdata/zoneinfo/zone1970.tab @@ -102,8 +102,7 @@ CA +4612-05957 America/Glace_Bay Atlantic - NS (Cape Breton) CA +4606-06447 America/Moncton Atlantic - New Brunswick CA +5320-06025 America/Goose_Bay Atlantic - Labrador (most areas) CA,BS +4339-07923 America/Toronto Eastern - ON, QC (most areas), Bahamas -CA +6344-06828 America/Iqaluit Eastern - NU (most east areas) -CA +6608-06544 America/Pangnirtung Eastern - NU (Pangnirtung) +CA +6344-06828 America/Iqaluit Eastern - NU (most areas) CA +4953-09709 America/Winnipeg Central - ON (west); Manitoba CA +744144-0944945 America/Resolute Central - NU (Resolute) CA +624900-0920459 America/Rankin_Inlet Central - NU (central) @@ -214,17 +213,18 @@ MQ +1436-06105 America/Martinique MT +3554+01431 Europe/Malta MU -2010+05730 Indian/Mauritius MV,TF +0410+07330 Indian/Maldives Maldives, Kerguelen, St Paul I, Amsterdam I -MX +1924-09909 America/Mexico_City Central Time -MX +2105-08646 America/Cancun Eastern Standard Time - Quintana Roo -MX +2058-08937 America/Merida Central Time - Campeche, Yucatán -MX +2540-10019 America/Monterrey Central Time - Durango; Coahuila, Nuevo León, Tamaulipas (most areas) -MX +2550-09730 America/Matamoros Central Time US - Coahuila, Nuevo León, Tamaulipas (US border) -MX +2313-10625 America/Mazatlan Mountain Time - Baja California Sur, Nayarit, Sinaloa -MX +2838-10605 America/Chihuahua Mountain Time - Chihuahua (most areas) -MX +2934-10425 America/Ojinaga Mountain Time US - Chihuahua (US border) -MX +2904-11058 America/Hermosillo Mountain Standard Time - Sonora -MX +3232-11701 America/Tijuana Pacific Time US - Baja California -MX +2048-10515 America/Bahia_Banderas Central Time - Bahía de Banderas +MX +1924-09909 America/Mexico_City Central Mexico +MX +2105-08646 America/Cancun Quintana Roo +MX +2058-08937 America/Merida Campeche, Yucatán +MX +2540-10019 America/Monterrey Durango; Coahuila, Nuevo León, Tamaulipas (most areas) +MX +2550-09730 America/Matamoros Coahuila, Nuevo León, Tamaulipas (US border) +MX +2838-10605 America/Chihuahua Chihuahua (most areas) +MX +3144-10629 America/Ciudad_Juarez Chihuahua (US border - west) +MX +2934-10425 America/Ojinaga Chihuahua (US border - east) +MX +2313-10625 America/Mazatlan Baja California Sur, Nayarit (most areas), Sinaloa +MX +2048-10515 America/Bahia_Banderas Bahía de Banderas +MX +2904-11058 America/Hermosillo Sonora +MX +3232-11701 America/Tijuana Baja California MY,BN +0133+11020 Asia/Kuching Sabah, Sarawak, Brunei MZ,BI,BW,CD,MW,RW,ZM,ZW -2558+03235 Africa/Maputo Central Africa Time NA -2234+01706 Africa/Windhoek diff --git a/lib/tzdata/zones b/lib/tzdata/zones index d8ec444a..8d9892ed 100644 --- a/lib/tzdata/zones +++ b/lib/tzdata/zones @@ -239,7 +239,6 @@ America/Edmonton America/Vancouver America/Dawson_Creek America/Fort_Nelson -America/Pangnirtung America/Iqaluit America/Resolute America/Rankin_Inlet @@ -253,6 +252,7 @@ America/Merida America/Matamoros America/Monterrey America/Mexico_City +America/Ciudad_Juarez America/Ojinaga America/Chihuahua America/Hermosillo @@ -554,6 +554,7 @@ America/Ensenada America/Fort_Wayne America/Montreal America/Nipigon +America/Pangnirtung America/Porto_Acre America/Rainy_River America/Rosario diff --git a/requirements.txt b/requirements.txt index 6de9a262..647d8dd7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -43,7 +43,7 @@ six==1.16.0 soupsieve==2.3.2.post1 tempora==5.1.0 tokenize-rt==5.0.0 -tzdata==2022.6 +tzdata==2022.7 tzlocal==4.2 urllib3==1.26.13 webencodings==0.5.1 From 483245506b8ad8b9eb6d2a858172aa6cbabbc46e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Dec 2022 16:00:12 -0800 Subject: [PATCH 14/14] Bump importlib-resources from 5.10.0 to 5.10.1 (#1918) * Bump importlib-resources from 5.10.0 to 5.10.1 Bumps [importlib-resources](https://github.com/python/importlib_resources) from 5.10.0 to 5.10.1. - [Release notes](https://github.com/python/importlib_resources/releases) - [Changelog](https://github.com/python/importlib_resources/blob/main/CHANGES.rst) - [Commits](https://github.com/python/importlib_resources/compare/v5.10.0...v5.10.1) --- updated-dependencies: - dependency-name: importlib-resources dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * Update importlib-resources==5.10.1 Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> [skip ci] --- lib/importlib_resources/_common.py | 3 ++- package/requirements-package.txt | 2 +- requirements.txt | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/importlib_resources/_common.py b/lib/importlib_resources/_common.py index 9f19784d..6a338c61 100644 --- a/lib/importlib_resources/_common.py +++ b/lib/importlib_resources/_common.py @@ -203,5 +203,6 @@ def _write_contents(target, source): for item in source.iterdir(): _write_contents(child, item) else: - child.open('wb').write(source.read_bytes()) + with child.open('wb') as fp: + fp.write(source.read_bytes()) return child diff --git a/package/requirements-package.txt b/package/requirements-package.txt index 7dc0227b..6260d288 100644 --- a/package/requirements-package.txt +++ b/package/requirements-package.txt @@ -1,6 +1,6 @@ apscheduler==3.9.1.post1 importlib-metadata==5.0.0 -importlib-resources==5.10.0 +importlib-resources==5.10.1 pyinstaller==5.6.2 pyopenssl==22.1.0 pycryptodomex==3.15.0 diff --git a/requirements.txt b/requirements.txt index 647d8dd7..6e82f4e1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,7 +19,7 @@ html5lib==1.1 httpagentparser==1.9.5 idna==3.4 importlib-metadata==5.0.0 -importlib-resources==5.10.0 +importlib-resources==5.10.1 git+https://github.com/Tautulli/ipwhois.git@master#egg=ipwhois IPy==1.01 Mako==1.2.4