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