mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-06 05:01:14 -07:00
Revert "Bump apscheduler from 3.10.1 to 3.10.4 (#2133)"
This reverts commit 2c42150799
.
This commit is contained in:
parent
1b26775ec6
commit
80e6131a0d
11 changed files with 825 additions and 855 deletions
|
@ -1,15 +1,10 @@
|
||||||
import sys
|
from pkg_resources import get_distribution, DistributionNotFound
|
||||||
if sys.version_info >= (3, 8):
|
|
||||||
import importlib.metadata as importlib_metadata
|
|
||||||
else:
|
|
||||||
import importlib_metadata
|
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
release = importlib_metadata.version('APScheduler').split('-')[0]
|
release = get_distribution('APScheduler').version.split('-')[0]
|
||||||
except importlib_metadata.PackageNotFoundError:
|
except DistributionNotFound:
|
||||||
release = '3.5.0'
|
release = '3.5.0'
|
||||||
|
|
||||||
version_info = tuple(int(x) if x.isdigit() else x for x in release.split('.'))
|
version_info = tuple(int(x) if x.isdigit() else x for x in release.split('.'))
|
||||||
version = __version__ = '.'.join(str(x) for x in version_info[:3])
|
version = __version__ = '.'.join(str(x) for x in version_info[:3])
|
||||||
del sys, importlib_metadata
|
del get_distribution, DistributionNotFound
|
||||||
|
|
|
@ -7,6 +7,7 @@ from logging import getLogger
|
||||||
import warnings
|
import warnings
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from pkg_resources import iter_entry_points
|
||||||
from tzlocal import get_localzone
|
from tzlocal import get_localzone
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
@ -30,11 +31,6 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from collections import MutableMapping
|
from collections import MutableMapping
|
||||||
|
|
||||||
try:
|
|
||||||
from importlib.metadata import entry_points
|
|
||||||
except ModuleNotFoundError:
|
|
||||||
from importlib_metadata import entry_points
|
|
||||||
|
|
||||||
#: constant indicating a scheduler's stopped state
|
#: constant indicating a scheduler's stopped state
|
||||||
STATE_STOPPED = 0
|
STATE_STOPPED = 0
|
||||||
#: constant indicating a scheduler's running state (started and processing jobs)
|
#: constant indicating a scheduler's running state (started and processing jobs)
|
||||||
|
@ -66,18 +62,12 @@ class BaseScheduler(six.with_metaclass(ABCMeta)):
|
||||||
|
|
||||||
.. seealso:: :ref:`scheduler-config`
|
.. seealso:: :ref:`scheduler-config`
|
||||||
"""
|
"""
|
||||||
# The `group=...` API is only available in the backport, used in <=3.7, and in std>=3.10.
|
|
||||||
if (3, 8) <= sys.version_info < (3, 10):
|
|
||||||
_trigger_plugins = {ep.name: ep for ep in entry_points()['apscheduler.triggers']}
|
|
||||||
_executor_plugins = {ep.name: ep for ep in entry_points()['apscheduler.executors']}
|
|
||||||
_jobstore_plugins = {ep.name: ep for ep in entry_points()['apscheduler.jobstores']}
|
|
||||||
else:
|
|
||||||
_trigger_plugins = {ep.name: ep for ep in entry_points(group='apscheduler.triggers')}
|
|
||||||
_executor_plugins = {ep.name: ep for ep in entry_points(group='apscheduler.executors')}
|
|
||||||
_jobstore_plugins = {ep.name: ep for ep in entry_points(group='apscheduler.jobstores')}
|
|
||||||
|
|
||||||
|
_trigger_plugins = dict((ep.name, ep) for ep in iter_entry_points('apscheduler.triggers'))
|
||||||
_trigger_classes = {}
|
_trigger_classes = {}
|
||||||
|
_executor_plugins = dict((ep.name, ep) for ep in iter_entry_points('apscheduler.executors'))
|
||||||
_executor_classes = {}
|
_executor_classes = {}
|
||||||
|
_jobstore_plugins = dict((ep.name, ep) for ep in iter_entry_points('apscheduler.jobstores'))
|
||||||
_jobstore_classes = {}
|
_jobstore_classes = {}
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -1029,7 +1019,6 @@ class BaseScheduler(six.with_metaclass(ABCMeta)):
|
||||||
wait_seconds = None
|
wait_seconds = None
|
||||||
self._logger.debug('No jobs; waiting until a job is added')
|
self._logger.debug('No jobs; waiting until a job is added')
|
||||||
else:
|
else:
|
||||||
now = datetime.now(self.timezone)
|
|
||||||
wait_seconds = min(max(timedelta_seconds(next_wakeup_time - now), 0), TIMEOUT_MAX)
|
wait_seconds = min(max(timedelta_seconds(next_wakeup_time - now), 0), TIMEOUT_MAX)
|
||||||
self._logger.debug('Next wakeup is due at %s (in %f seconds)', next_wakeup_time,
|
self._logger.debug('Next wakeup is due at %s (in %f seconds)', next_wakeup_time,
|
||||||
wait_seconds)
|
wait_seconds)
|
||||||
|
|
|
@ -1,22 +1,24 @@
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
from importlib import import_module
|
|
||||||
from itertools import product
|
|
||||||
|
|
||||||
from apscheduler.schedulers.base import BaseScheduler
|
from apscheduler.schedulers.base import BaseScheduler
|
||||||
|
|
||||||
for version, pkgname in product(range(6, 1, -1), ("PySide", "PyQt")):
|
try:
|
||||||
|
from PyQt5.QtCore import QObject, QTimer
|
||||||
|
except (ImportError, RuntimeError): # pragma: nocover
|
||||||
try:
|
try:
|
||||||
qtcore = import_module(pkgname + str(version) + ".QtCore")
|
from PyQt4.QtCore import QObject, QTimer
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
try:
|
||||||
else:
|
from PySide6.QtCore import QObject, QTimer # noqa
|
||||||
QTimer = qtcore.QTimer
|
except ImportError:
|
||||||
break
|
try:
|
||||||
else:
|
from PySide2.QtCore import QObject, QTimer # noqa
|
||||||
raise ImportError(
|
except ImportError:
|
||||||
"QtScheduler requires either PySide/PyQt (v6 to v2) installed"
|
try:
|
||||||
)
|
from PySide.QtCore import QObject, QTimer # noqa
|
||||||
|
except ImportError:
|
||||||
|
raise ImportError('QtScheduler requires either PyQt5, PyQt4, PySide6, PySide2 '
|
||||||
|
'or PySide installed')
|
||||||
|
|
||||||
|
|
||||||
class QtScheduler(BaseScheduler):
|
class QtScheduler(BaseScheduler):
|
||||||
|
|
|
@ -6,7 +6,7 @@ from asyncio import iscoroutinefunction
|
||||||
from datetime import date, datetime, time, timedelta, tzinfo
|
from datetime import date, datetime, time, timedelta, tzinfo
|
||||||
from calendar import timegm
|
from calendar import timegm
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from inspect import isbuiltin, isclass, isfunction, ismethod
|
from inspect import isclass, ismethod
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
@ -214,15 +214,28 @@ def get_callable_name(func):
|
||||||
:rtype: str
|
:rtype: str
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if ismethod(func):
|
# the easy case (on Python 3.3+)
|
||||||
self = func.__self__
|
if hasattr(func, '__qualname__'):
|
||||||
cls = self if isclass(self) else type(self)
|
|
||||||
return f"{cls.__qualname__}.{func.__name__}"
|
|
||||||
elif isclass(func) or isfunction(func) or isbuiltin(func):
|
|
||||||
return func.__qualname__
|
return func.__qualname__
|
||||||
elif hasattr(func, '__call__') and callable(func.__call__):
|
|
||||||
|
# class methods, bound and unbound methods
|
||||||
|
f_self = getattr(func, '__self__', None) or getattr(func, 'im_self', None)
|
||||||
|
if f_self and hasattr(func, '__name__'):
|
||||||
|
f_class = f_self if isclass(f_self) else f_self.__class__
|
||||||
|
else:
|
||||||
|
f_class = getattr(func, 'im_class', None)
|
||||||
|
|
||||||
|
if f_class and hasattr(func, '__name__'):
|
||||||
|
return '%s.%s' % (f_class.__name__, func.__name__)
|
||||||
|
|
||||||
|
# class or class instance
|
||||||
|
if hasattr(func, '__call__'):
|
||||||
|
# class
|
||||||
|
if hasattr(func, '__name__'):
|
||||||
|
return func.__name__
|
||||||
|
|
||||||
# instance of a class with a __call__ method
|
# instance of a class with a __call__ method
|
||||||
return type(func).__qualname__
|
return func.__class__.__name__
|
||||||
|
|
||||||
raise TypeError('Unable to determine a name for %r -- maybe it is not a callable?' % func)
|
raise TypeError('Unable to determine a name for %r -- maybe it is not a callable?' % func)
|
||||||
|
|
||||||
|
@ -247,10 +260,16 @@ def obj_to_ref(obj):
|
||||||
raise ValueError('Cannot create a reference to a nested function')
|
raise ValueError('Cannot create a reference to a nested function')
|
||||||
|
|
||||||
if ismethod(obj):
|
if ismethod(obj):
|
||||||
module = obj.__self__.__module__
|
if hasattr(obj, 'im_self') and obj.im_self:
|
||||||
|
# bound method
|
||||||
|
module = obj.im_self.__module__
|
||||||
|
elif hasattr(obj, 'im_class') and obj.im_class:
|
||||||
|
# unbound method
|
||||||
|
module = obj.im_class.__module__
|
||||||
|
else:
|
||||||
|
module = obj.__module__
|
||||||
else:
|
else:
|
||||||
module = obj.__module__
|
module = obj.__module__
|
||||||
|
|
||||||
return '%s:%s' % (module, name)
|
return '%s:%s' % (module, name)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,12 +9,5 @@ if sys.platform == "win32":
|
||||||
else:
|
else:
|
||||||
from tzlocal.unix import get_localzone, get_localzone_name, reload_localzone
|
from tzlocal.unix import get_localzone, get_localzone_name, reload_localzone
|
||||||
|
|
||||||
from tzlocal.utils import assert_tz_offset
|
|
||||||
|
|
||||||
|
__all__ = ["get_localzone", "get_localzone_name", "reload_localzone"]
|
||||||
__all__ = [
|
|
||||||
"get_localzone",
|
|
||||||
"get_localzone_name",
|
|
||||||
"reload_localzone",
|
|
||||||
"assert_tz_offset",
|
|
||||||
]
|
|
||||||
|
|
|
@ -1,21 +1,20 @@
|
||||||
import logging
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import warnings
|
import warnings
|
||||||
from datetime import timezone
|
from datetime import timezone
|
||||||
|
import pytz_deprecation_shim as pds
|
||||||
|
|
||||||
from tzlocal import utils
|
from tzlocal import utils
|
||||||
|
|
||||||
if sys.version_info >= (3, 9):
|
if sys.version_info >= (3, 9):
|
||||||
import zoneinfo # pragma: no cover
|
from zoneinfo import ZoneInfo # pragma: no cover
|
||||||
else:
|
else:
|
||||||
from backports import zoneinfo # pragma: no cover
|
from backports.zoneinfo import ZoneInfo # pragma: no cover
|
||||||
|
|
||||||
_cache_tz = None
|
_cache_tz = None
|
||||||
_cache_tz_name = None
|
_cache_tz_name = None
|
||||||
|
|
||||||
log = logging.getLogger("tzlocal")
|
|
||||||
|
|
||||||
def _get_localzone_name(_root="/"):
|
def _get_localzone_name(_root="/"):
|
||||||
"""Tries to find the local timezone configuration.
|
"""Tries to find the local timezone configuration.
|
||||||
|
@ -33,21 +32,14 @@ def _get_localzone_name(_root="/"):
|
||||||
|
|
||||||
# Are we under Termux on Android?
|
# Are we under Termux on Android?
|
||||||
if os.path.exists(os.path.join(_root, "system/bin/getprop")):
|
if os.path.exists(os.path.join(_root, "system/bin/getprop")):
|
||||||
log.debug("This looks like Termux")
|
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
try:
|
androidtz = (
|
||||||
androidtz = (
|
subprocess.check_output(["getprop", "persist.sys.timezone"])
|
||||||
subprocess.check_output(["getprop", "persist.sys.timezone"])
|
.strip()
|
||||||
.strip()
|
.decode()
|
||||||
.decode()
|
)
|
||||||
)
|
return androidtz
|
||||||
return androidtz
|
|
||||||
except (OSError, subprocess.CalledProcessError):
|
|
||||||
# proot environment or failed to getprop
|
|
||||||
log.debug("It's not termux?")
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Now look for distribution specific configuration files
|
# Now look for distribution specific configuration files
|
||||||
# that contain the timezone name.
|
# that contain the timezone name.
|
||||||
|
@ -58,11 +50,10 @@ def _get_localzone_name(_root="/"):
|
||||||
for configfile in ("etc/timezone", "var/db/zoneinfo"):
|
for configfile in ("etc/timezone", "var/db/zoneinfo"):
|
||||||
tzpath = os.path.join(_root, configfile)
|
tzpath = os.path.join(_root, configfile)
|
||||||
try:
|
try:
|
||||||
with open(tzpath) as tzfile:
|
with open(tzpath, "rt") as tzfile:
|
||||||
data = tzfile.read()
|
data = tzfile.read()
|
||||||
log.debug(f"{tzpath} found, contents:\n {data}")
|
|
||||||
|
|
||||||
etctz = data.strip("/ \t\r\n")
|
etctz = data.strip('/ \t\r\n')
|
||||||
if not etctz:
|
if not etctz:
|
||||||
# Empty file, skip
|
# Empty file, skip
|
||||||
continue
|
continue
|
||||||
|
@ -77,7 +68,7 @@ def _get_localzone_name(_root="/"):
|
||||||
|
|
||||||
found_configs[tzpath] = etctz.replace(" ", "_")
|
found_configs[tzpath] = etctz.replace(" ", "_")
|
||||||
|
|
||||||
except (OSError, UnicodeDecodeError):
|
except (IOError, UnicodeDecodeError):
|
||||||
# File doesn't exist or is a directory, or it's a binary file.
|
# File doesn't exist or is a directory, or it's a binary file.
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -95,7 +86,6 @@ def _get_localzone_name(_root="/"):
|
||||||
try:
|
try:
|
||||||
with open(tzpath, "rt") as tzfile:
|
with open(tzpath, "rt") as tzfile:
|
||||||
data = tzfile.readlines()
|
data = tzfile.readlines()
|
||||||
log.debug(f"{tzpath} found, contents:\n {data}")
|
|
||||||
|
|
||||||
for line in data:
|
for line in data:
|
||||||
# Look for the ZONE= setting.
|
# Look for the ZONE= setting.
|
||||||
|
@ -105,13 +95,13 @@ def _get_localzone_name(_root="/"):
|
||||||
match = timezone_re.match(line)
|
match = timezone_re.match(line)
|
||||||
if match is not None:
|
if match is not None:
|
||||||
# Some setting existed
|
# Some setting existed
|
||||||
line = line[match.end() :]
|
line = line[match.end():]
|
||||||
etctz = line[: end_re.search(line).start()]
|
etctz = line[: end_re.search(line).start()]
|
||||||
|
|
||||||
# We found a timezone
|
# We found a timezone
|
||||||
found_configs[tzpath] = etctz.replace(" ", "_")
|
found_configs[tzpath] = etctz.replace(" ", "_")
|
||||||
|
|
||||||
except (OSError, UnicodeDecodeError):
|
except (IOError, UnicodeDecodeError):
|
||||||
# UnicodeDecode handles when clock is symlink to /etc/localtime
|
# UnicodeDecode handles when clock is symlink to /etc/localtime
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -119,34 +109,30 @@ def _get_localzone_name(_root="/"):
|
||||||
# see manpage of localtime(5) and timedatectl(1)
|
# see manpage of localtime(5) and timedatectl(1)
|
||||||
tzpath = os.path.join(_root, "etc/localtime")
|
tzpath = os.path.join(_root, "etc/localtime")
|
||||||
if os.path.exists(tzpath) and os.path.islink(tzpath):
|
if os.path.exists(tzpath) and os.path.islink(tzpath):
|
||||||
log.debug(f"{tzpath} found")
|
etctz = realtzpath = os.path.realpath(tzpath)
|
||||||
etctz = os.path.realpath(tzpath)
|
|
||||||
start = etctz.find("/") + 1
|
start = etctz.find("/") + 1
|
||||||
while start != 0:
|
while start != 0:
|
||||||
etctz = etctz[start:]
|
etctz = etctz[start:]
|
||||||
try:
|
try:
|
||||||
zoneinfo.ZoneInfo(etctz)
|
pds.timezone(etctz)
|
||||||
tzinfo = f"{tzpath} is a symlink to"
|
tzinfo = f"{tzpath} is a symlink to"
|
||||||
found_configs[tzinfo] = etctz.replace(" ", "_")
|
found_configs[tzinfo] = etctz.replace(" ", "_")
|
||||||
# Only need first valid relative path in simlink.
|
except pds.UnknownTimeZoneError:
|
||||||
break
|
|
||||||
except zoneinfo.ZoneInfoNotFoundError:
|
|
||||||
pass
|
pass
|
||||||
start = etctz.find("/") + 1
|
start = etctz.find("/") + 1
|
||||||
|
|
||||||
if len(found_configs) > 0:
|
if len(found_configs) > 0:
|
||||||
log.debug(f"{len(found_configs)} found:\n {found_configs}")
|
|
||||||
# We found some explicit config of some sort!
|
# We found some explicit config of some sort!
|
||||||
if len(found_configs) > 1:
|
if len(found_configs) > 1:
|
||||||
# Uh-oh, multiple configs. See if they match:
|
# Uh-oh, multiple configs. See if they match:
|
||||||
unique_tzs = set()
|
unique_tzs = set()
|
||||||
zoneinfopath = os.path.join(_root, "usr", "share", "zoneinfo")
|
zoneinfo = os.path.join(_root, "usr", "share", "zoneinfo")
|
||||||
directory_depth = len(zoneinfopath.split(os.path.sep))
|
directory_depth = len(zoneinfo.split(os.path.sep))
|
||||||
|
|
||||||
for tzname in found_configs.values():
|
for tzname in found_configs.values():
|
||||||
# Look them up in /usr/share/zoneinfo, and find what they
|
# Look them up in /usr/share/zoneinfo, and find what they
|
||||||
# really point to:
|
# really point to:
|
||||||
path = os.path.realpath(os.path.join(zoneinfopath, *tzname.split("/")))
|
path = os.path.realpath(os.path.join(zoneinfo, *tzname.split("/")))
|
||||||
real_zone_name = "/".join(path.split(os.path.sep)[directory_depth:])
|
real_zone_name = "/".join(path.split(os.path.sep)[directory_depth:])
|
||||||
unique_tzs.add(real_zone_name)
|
unique_tzs.add(real_zone_name)
|
||||||
|
|
||||||
|
@ -155,7 +141,7 @@ def _get_localzone_name(_root="/"):
|
||||||
for key, value in found_configs.items():
|
for key, value in found_configs.items():
|
||||||
message += f"{key}: {value}\n"
|
message += f"{key}: {value}\n"
|
||||||
message += "Fix the configuration, or set the time zone in a TZ environment variable.\n"
|
message += "Fix the configuration, or set the time zone in a TZ environment variable.\n"
|
||||||
raise zoneinfo.ZoneInfoNotFoundError(message)
|
raise utils.ZoneInfoNotFoundError(message)
|
||||||
|
|
||||||
# We found exactly one config! Use it.
|
# We found exactly one config! Use it.
|
||||||
return list(found_configs.values())[0]
|
return list(found_configs.values())[0]
|
||||||
|
@ -179,25 +165,24 @@ def _get_localzone(_root="/"):
|
||||||
tzname = _get_localzone_name(_root)
|
tzname = _get_localzone_name(_root)
|
||||||
if tzname is None:
|
if tzname is None:
|
||||||
# No explicit setting existed. Use localtime
|
# No explicit setting existed. Use localtime
|
||||||
log.debug("No explicit setting existed. Use localtime")
|
|
||||||
for filename in ("etc/localtime", "usr/local/etc/localtime"):
|
for filename in ("etc/localtime", "usr/local/etc/localtime"):
|
||||||
tzpath = os.path.join(_root, filename)
|
tzpath = os.path.join(_root, filename)
|
||||||
|
|
||||||
if not os.path.exists(tzpath):
|
if not os.path.exists(tzpath):
|
||||||
continue
|
continue
|
||||||
with open(tzpath, "rb") as tzfile:
|
with open(tzpath, "rb") as tzfile:
|
||||||
tz = zoneinfo.ZoneInfo.from_file(tzfile, key="local")
|
tz = pds.wrap_zone(ZoneInfo.from_file(tzfile, key="local"))
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
warnings.warn("Can not find any timezone configuration, defaulting to UTC.")
|
warnings.warn("Can not find any timezone configuration, defaulting to UTC.")
|
||||||
tz = timezone.utc
|
tz = timezone.utc
|
||||||
else:
|
else:
|
||||||
tz = zoneinfo.ZoneInfo(tzname)
|
tz = pds.timezone(tzname)
|
||||||
|
|
||||||
if _root == "/":
|
if _root == "/":
|
||||||
# We are using a file in etc to name the timezone.
|
# We are using a file in etc to name the timezone.
|
||||||
# Verify that the timezone specified there is actually used:
|
# Verify that the timezone specified there is actually used:
|
||||||
utils.assert_tz_offset(tz, error=False)
|
utils.assert_tz_offset(tz)
|
||||||
return tz
|
return tz
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import logging
|
# -*- coding: utf-8 -*-
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
import datetime
|
import datetime
|
||||||
import calendar
|
import calendar
|
||||||
import warnings
|
import pytz_deprecation_shim as pds
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import zoneinfo # pragma: no cover
|
import zoneinfo # pragma: no cover
|
||||||
|
@ -12,7 +12,35 @@ except ImportError:
|
||||||
|
|
||||||
from tzlocal import windows_tz
|
from tzlocal import windows_tz
|
||||||
|
|
||||||
log = logging.getLogger("tzlocal")
|
|
||||||
|
class ZoneInfoNotFoundError(pds.UnknownTimeZoneError, zoneinfo.ZoneInfoNotFoundError):
|
||||||
|
"""An exception derived from both pytz and zoneinfo
|
||||||
|
|
||||||
|
This exception will be trappable both by pytz expecting clients and
|
||||||
|
zoneinfo expecting clients.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def get_system_offset():
|
||||||
|
"""Get system's timezone offset using built-in library time.
|
||||||
|
|
||||||
|
For the Timezone constants (altzone, daylight, timezone, and tzname), the
|
||||||
|
value is determined by the timezone rules in effect at module load time or
|
||||||
|
the last time tzset() is called and may be incorrect for times in the past.
|
||||||
|
|
||||||
|
To keep compatibility with Windows, we're always importing time module here.
|
||||||
|
"""
|
||||||
|
|
||||||
|
localtime = calendar.timegm(time.localtime())
|
||||||
|
gmtime = calendar.timegm(time.gmtime())
|
||||||
|
offset = gmtime - localtime
|
||||||
|
# We could get the localtime and gmtime on either side of a second switch
|
||||||
|
# so we check that the difference is less than one minute, because nobody
|
||||||
|
# has that small DST differences.
|
||||||
|
if abs(offset - time.altzone) < 60:
|
||||||
|
return -time.altzone # pragma: no cover
|
||||||
|
else:
|
||||||
|
return -time.timezone # pragma: no cover
|
||||||
|
|
||||||
|
|
||||||
def get_tz_offset(tz):
|
def get_tz_offset(tz):
|
||||||
|
@ -20,27 +48,19 @@ def get_tz_offset(tz):
|
||||||
return int(datetime.datetime.now(tz).utcoffset().total_seconds())
|
return int(datetime.datetime.now(tz).utcoffset().total_seconds())
|
||||||
|
|
||||||
|
|
||||||
def assert_tz_offset(tz, error=True):
|
def assert_tz_offset(tz):
|
||||||
"""Assert that system's timezone offset equals to the timezone offset found.
|
"""Assert that system's timezone offset equals to the timezone offset found.
|
||||||
|
|
||||||
If they don't match, we probably have a misconfiguration, for example, an
|
If they don't match, we probably have a misconfiguration, for example, an
|
||||||
incorrect timezone set in /etc/timezone file in systemd distributions.
|
incorrect timezone set in /etc/timezone file in systemd distributions."""
|
||||||
|
|
||||||
If error is True, this method will raise a ValueError, otherwise it will
|
|
||||||
emit a warning.
|
|
||||||
"""
|
|
||||||
|
|
||||||
tz_offset = get_tz_offset(tz)
|
tz_offset = get_tz_offset(tz)
|
||||||
system_offset = calendar.timegm(time.localtime()) - calendar.timegm(time.gmtime())
|
system_offset = get_system_offset()
|
||||||
# No one has timezone offsets less than a minute, so this should be close enough:
|
if tz_offset != system_offset:
|
||||||
if abs(tz_offset - system_offset) > 60:
|
|
||||||
msg = (
|
msg = (
|
||||||
"Timezone offset does not match system offset: {} != {}. "
|
"Timezone offset does not match system offset: {} != {}. "
|
||||||
"Please, check your config files."
|
"Please, check your config files."
|
||||||
).format(tz_offset, system_offset)
|
).format(tz_offset, system_offset)
|
||||||
if error:
|
raise ValueError(msg)
|
||||||
raise ValueError(msg)
|
|
||||||
warnings.warn(msg)
|
|
||||||
|
|
||||||
|
|
||||||
def _tz_name_from_env(tzenv=None):
|
def _tz_name_from_env(tzenv=None):
|
||||||
|
@ -50,8 +70,6 @@ def _tz_name_from_env(tzenv=None):
|
||||||
if not tzenv:
|
if not tzenv:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
log.debug(f"Found a TZ environment: {tzenv}")
|
|
||||||
|
|
||||||
if tzenv[0] == ":":
|
if tzenv[0] == ":":
|
||||||
tzenv = tzenv[1:]
|
tzenv = tzenv[1:]
|
||||||
|
|
||||||
|
@ -74,9 +92,6 @@ def _tz_name_from_env(tzenv=None):
|
||||||
# Indeed
|
# Indeed
|
||||||
return parts[-1]
|
return parts[-1]
|
||||||
|
|
||||||
log.debug("TZ does not contain a time zone name")
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def _tz_from_env(tzenv=None):
|
def _tz_from_env(tzenv=None):
|
||||||
if tzenv is None:
|
if tzenv is None:
|
||||||
|
@ -97,16 +112,17 @@ def _tz_from_env(tzenv=None):
|
||||||
# Nope, not a standard timezone name, just take the filename
|
# Nope, not a standard timezone name, just take the filename
|
||||||
tzname = tzenv.split(os.sep)[-1]
|
tzname = tzenv.split(os.sep)[-1]
|
||||||
with open(tzenv, "rb") as tzfile:
|
with open(tzenv, "rb") as tzfile:
|
||||||
return zoneinfo.ZoneInfo.from_file(tzfile, key=tzname)
|
zone = zoneinfo.ZoneInfo.from_file(tzfile, key=tzname)
|
||||||
|
return pds.wrap_zone(zone)
|
||||||
|
|
||||||
# TZ must specify a zoneinfo zone.
|
# TZ must specify a zoneinfo zone.
|
||||||
try:
|
try:
|
||||||
tz = zoneinfo.ZoneInfo(tzenv)
|
tz = pds.timezone(tzenv)
|
||||||
# That worked, so we return this:
|
# That worked, so we return this:
|
||||||
return tz
|
return tz
|
||||||
except zoneinfo.ZoneInfoNotFoundError:
|
except pds.UnknownTimeZoneError:
|
||||||
# Nope, it's something like "PST4DST" etc, we can't handle that.
|
# Nope, it's something like "PST4DST" etc, we can't handle that.
|
||||||
raise zoneinfo.ZoneInfoNotFoundError(
|
raise ZoneInfoNotFoundError(
|
||||||
"tzlocal() does not support non-zoneinfo timezones like %s. \n"
|
"tzlocal() does not support non-zoneinfo timezones like %s. \n"
|
||||||
"Please use a timezone in the form of Continent/City" % tzenv
|
"Please use a timezone in the form of Continent/City"
|
||||||
) from None
|
) from None
|
||||||
|
|
|
@ -1,24 +1,17 @@
|
||||||
import logging
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import pytz_deprecation_shim as pds
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import _winreg as winreg
|
import _winreg as winreg
|
||||||
except ImportError:
|
except ImportError:
|
||||||
import winreg
|
import winreg
|
||||||
|
|
||||||
try:
|
|
||||||
import zoneinfo # pragma: no cover
|
|
||||||
except ImportError:
|
|
||||||
from backports import zoneinfo # pragma: no cover
|
|
||||||
|
|
||||||
from tzlocal.windows_tz import win_tz
|
from tzlocal.windows_tz import win_tz
|
||||||
from tzlocal import utils
|
from tzlocal import utils
|
||||||
|
|
||||||
_cache_tz = None
|
_cache_tz = None
|
||||||
_cache_tz_name = None
|
_cache_tz_name = None
|
||||||
|
|
||||||
log = logging.getLogger("tzlocal")
|
|
||||||
|
|
||||||
|
|
||||||
def valuestodict(key):
|
def valuestodict(key):
|
||||||
"""Convert a registry key's values to a dictionary."""
|
"""Convert a registry key's values to a dictionary."""
|
||||||
|
@ -55,7 +48,6 @@ def _get_localzone_name():
|
||||||
if tzenv:
|
if tzenv:
|
||||||
return tzenv
|
return tzenv
|
||||||
|
|
||||||
log.debug("Looking up time zone info from registry")
|
|
||||||
handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
|
handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
|
||||||
|
|
||||||
TZLOCALKEYNAME = r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation"
|
TZLOCALKEYNAME = r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation"
|
||||||
|
@ -82,13 +74,13 @@ def _get_localzone_name():
|
||||||
|
|
||||||
# Return what we have.
|
# Return what we have.
|
||||||
if timezone is None:
|
if timezone is None:
|
||||||
raise zoneinfo.ZoneInfoNotFoundError(tzkeyname)
|
raise utils.ZoneInfoNotFoundError(tzkeyname)
|
||||||
|
|
||||||
if keyvalues.get("DynamicDaylightTimeDisabled", 0) == 1:
|
if keyvalues.get("DynamicDaylightTimeDisabled", 0) == 1:
|
||||||
# DST is disabled, so don't return the timezone name,
|
# DST is disabled, so don't return the timezone name,
|
||||||
# instead return Etc/GMT+offset
|
# instead return Etc/GMT+offset
|
||||||
|
|
||||||
tz = zoneinfo.ZoneInfo(timezone)
|
tz = pds.timezone(timezone)
|
||||||
has_dst, std_offset, dst_offset = _get_dst_info(tz)
|
has_dst, std_offset, dst_offset = _get_dst_info(tz)
|
||||||
if not has_dst:
|
if not has_dst:
|
||||||
# The DST is turned off in the windows configuration,
|
# The DST is turned off in the windows configuration,
|
||||||
|
@ -96,15 +88,13 @@ def _get_localzone_name():
|
||||||
return timezone
|
return timezone
|
||||||
|
|
||||||
if std_offset is None:
|
if std_offset is None:
|
||||||
raise zoneinfo.ZoneInfoNotFoundError(
|
raise utils.ZoneInfoNotFoundError(
|
||||||
f"{tzkeyname} claims to not have a non-DST time!?"
|
f"{tzkeyname} claims to not have a non-DST time!?")
|
||||||
)
|
|
||||||
|
|
||||||
if std_offset % 3600:
|
if std_offset % 3600:
|
||||||
# I can't convert this to an hourly offset
|
# I can't convert this to an hourly offset
|
||||||
raise zoneinfo.ZoneInfoNotFoundError(
|
raise utils.ZoneInfoNotFoundError(
|
||||||
f"tzlocal can't support disabling DST in the {timezone} zone."
|
f"tzlocal can't support disabling DST in the {timezone} zone.")
|
||||||
)
|
|
||||||
|
|
||||||
# This has whole hours as offset, return it as Etc/GMT
|
# This has whole hours as offset, return it as Etc/GMT
|
||||||
return f"Etc/GMT{-std_offset//3600:+.0f}"
|
return f"Etc/GMT{-std_offset//3600:+.0f}"
|
||||||
|
@ -126,13 +116,13 @@ def get_localzone():
|
||||||
|
|
||||||
global _cache_tz
|
global _cache_tz
|
||||||
if _cache_tz is None:
|
if _cache_tz is None:
|
||||||
_cache_tz = zoneinfo.ZoneInfo(get_localzone_name())
|
_cache_tz = pds.timezone(get_localzone_name())
|
||||||
|
|
||||||
if not utils._tz_name_from_env():
|
if not utils._tz_name_from_env():
|
||||||
# If the timezone does NOT come from a TZ environment variable,
|
# If the timezone does NOT come from a TZ environment variable,
|
||||||
# verify that it's correct. If it's from the environment,
|
# verify that it's correct. If it's from the environment,
|
||||||
# we accept it, this is so you can run tests with different timezones.
|
# we accept it, this is so you can run tests with different timezones.
|
||||||
utils.assert_tz_offset(_cache_tz, error=False)
|
utils.assert_tz_offset(_cache_tz)
|
||||||
|
|
||||||
return _cache_tz
|
return _cache_tz
|
||||||
|
|
||||||
|
@ -142,6 +132,6 @@ def reload_localzone():
|
||||||
global _cache_tz
|
global _cache_tz
|
||||||
global _cache_tz_name
|
global _cache_tz_name
|
||||||
_cache_tz_name = _get_localzone_name()
|
_cache_tz_name = _get_localzone_name()
|
||||||
_cache_tz = zoneinfo.ZoneInfo(_cache_tz_name)
|
_cache_tz = pds.timezone(_cache_tz_name)
|
||||||
utils.assert_tz_offset(_cache_tz, error=False)
|
utils.assert_tz_offset(_cache_tz)
|
||||||
return _cache_tz
|
return _cache_tz
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,4 @@
|
||||||
apscheduler==3.10.4
|
apscheduler==3.10.1
|
||||||
importlib-metadata==6.8.0
|
importlib-metadata==6.8.0
|
||||||
importlib-resources==6.0.1
|
importlib-resources==6.0.1
|
||||||
pyinstaller==5.13.0
|
pyinstaller==5.13.0
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
appdirs==1.4.4
|
appdirs==1.4.4
|
||||||
apscheduler==3.10.4
|
apscheduler==3.10.1
|
||||||
arrow==1.2.3
|
arrow==1.2.3
|
||||||
backports.csv==1.0.7
|
backports.csv==1.0.7
|
||||||
backports.functools-lru-cache==1.6.6
|
backports.functools-lru-cache==1.6.6
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue