From 1d2bf8e96efa624743453f4bf8108f413e5c60f9 Mon Sep 17 00:00:00 2001 From: Will Thong Date: Wed, 26 Jul 2023 16:29:43 +0100 Subject: [PATCH] Replace `pytz` dependency with `zoneinfo`. Fix #2958 (#3161) --- pelican/contents.py | 15 ++++++++++----- pelican/tools/pelican_quickstart.py | 20 +++++++++++--------- pelican/utils.py | 15 +++++++++------ pyproject.toml | 2 +- setup.py | 4 ++-- 5 files changed, 33 insertions(+), 23 deletions(-) diff --git a/pelican/contents.py b/pelican/contents.py index c979dd0a1..b756f92db 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -4,10 +4,15 @@ import logging import os import re +from datetime import timezone from html import unescape from urllib.parse import unquote, urljoin, urlparse, urlunparse -import pytz +try: + import zoneinfo +except ModuleNotFoundError: + from backports import zoneinfo + from pelican.plugins import signals from pelican.settings import DEFAULT_CONFIG @@ -120,9 +125,9 @@ def __init__(self, content, metadata=None, settings=None, self.date_format = self.date_format[1] # manage timezone - default_timezone = settings.get('TIMEZONE', 'UTC') - timezone = getattr(self, 'timezone', default_timezone) - self.timezone = pytz.timezone(timezone) + default_timezone = settings.get("TIMEZONE", "UTC") + timezone = getattr(self, "timezone", default_timezone) + self.timezone = zoneinfo.ZoneInfo(timezone) if hasattr(self, 'date'): self.date = set_date_tzinfo(self.date, timezone) @@ -525,7 +530,7 @@ def __init__(self, *args, **kwargs): if self.date.tzinfo is None: now = datetime.datetime.now() else: - now = datetime.datetime.utcnow().replace(tzinfo=pytz.utc) + now = datetime.datetime.utcnow().replace(tzinfo=timezone.utc) if self.date > now: self.status = 'draft' diff --git a/pelican/tools/pelican_quickstart.py b/pelican/tools/pelican_quickstart.py index 2d5629efa..7bdf8538c 100755 --- a/pelican/tools/pelican_quickstart.py +++ b/pelican/tools/pelican_quickstart.py @@ -7,7 +7,10 @@ from jinja2 import Environment, FileSystemLoader -import pytz +try: + import zoneinfo +except ModuleNotFoundError: + from backports import zoneinfo try: import readline # NOQA @@ -17,8 +20,8 @@ try: import tzlocal _DEFAULT_TIMEZONE = tzlocal.get_localzone().zone -except ImportError: - _DEFAULT_TIMEZONE = 'Europe/Rome' +except ModuleNotFoundError: + _DEFAULT_TIMEZONE = "Europe/Rome" from pelican import __version__ @@ -158,16 +161,15 @@ def ask(question, answer=str, default=None, length=None): def ask_timezone(question, default, tzurl): """Prompt for time zone and validate input""" - lower_tz = [tz.lower() for tz in pytz.all_timezones] + tz_dict = {tz.lower(): tz for tz in zoneinfo.available_timezones()} while True: r = ask(question, str, default) - r = r.strip().replace(' ', '_').lower() - if r in lower_tz: - r = pytz.all_timezones[lower_tz.index(r)] + r = r.strip().replace(" ", "_").lower() + if r in tz_dict.keys(): + r = tz_dict[r] break else: - print('Please enter a valid time zone:\n' - ' (check [{}])'.format(tzurl)) + print("Please enter a valid time zone:\n" " (check [{}])".format(tzurl)) return r diff --git a/pelican/utils.py b/pelican/utils.py index de6ef9bfe..8d91c487d 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -18,10 +18,12 @@ import dateutil.parser +try: + import zoneinfo +except ModuleNotFoundError: + from backports import zoneinfo from markupsafe import Markup -import pytz - logger = logging.getLogger(__name__) @@ -919,10 +921,11 @@ def file_watcher(path): def set_date_tzinfo(d, tz_name=None): """Set the timezone for dates that don't have tzinfo""" if tz_name and not d.tzinfo: - tz = pytz.timezone(tz_name) - d = tz.localize(d) - return SafeDatetime(d.year, d.month, d.day, d.hour, d.minute, d.second, - d.microsecond, d.tzinfo) + timezone = zoneinfo.ZoneInfo(tz_name) + d = d.replace(tzinfo=timezone) + return SafeDatetime( + d.year, d.month, d.day, d.hour, d.minute, d.second, d.microsecond, d.tzinfo + ) return d diff --git a/pyproject.toml b/pyproject.toml index 4ab2650a5..826c11799 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,10 +37,10 @@ feedgenerator = ">=1.9" jinja2 = ">=2.7" pygments = ">=2.6" python-dateutil = ">=2.8" -pytz = ">=2020.1" rich = ">=10.1" unidecode = ">=1.1" markdown = {version = ">=3.1", optional = true} +backports-zoneinfo = {version = "^0.2.1", python = "<3.9"} [tool.poetry.dev-dependencies] BeautifulSoup4 = "^4.9" diff --git a/setup.py b/setup.py index da038d244..5d2023c66 100755 --- a/setup.py +++ b/setup.py @@ -9,8 +9,8 @@ version = "4.8.0" requires = ['feedgenerator >= 1.9', 'jinja2 >= 2.7', 'pygments', - 'docutils>=0.15', 'pytz >= 0a', 'blinker', 'unidecode', - 'python-dateutil', 'rich'] + 'docutils>=0.15', 'blinker', 'unidecode', 'python-dateutil', + 'rich', 'backports-zoneinfo[tzdata] >= 0.2; python_version<"3.9"'] entry_points = { 'console_scripts': [