Skip to content

Commit cc8372a

Browse files
committed
Rearrange dates.py to avoid unnecessary warnings.
1 parent 4b71a52 commit cc8372a

File tree

5 files changed

+216
-185
lines changed

5 files changed

+216
-185
lines changed

domdf_python_tools/dates.py

Lines changed: 82 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,6 @@
88
99
pytz >=2019.1
1010
11-
12-
.. warning:: This module has not been fully tested. Use with caution.
13-
1411
"""
1512
#
1613
# Copyright © 2020 Dominic Davis-Foster <[email protected]>
@@ -47,6 +44,7 @@
4744
import sys
4845
import typing
4946
from collections import OrderedDict
47+
from types import ModuleType
5048
from typing import Optional, Union
5149

5250
__all__ = [
@@ -60,67 +58,6 @@
6058
"calc_easter",
6159
]
6260

63-
try:
64-
65-
# 3rd party
66-
import pytz
67-
68-
def get_utc_offset(
69-
tz: Union[datetime.tzinfo, str],
70-
date: Optional[datetime.datetime] = None,
71-
) -> Optional[datetime.timedelta]:
72-
"""
73-
Returns the offset between UTC and the requested timezone on the given date.
74-
If ``date`` is :py:obj:`None` then the current date is used.
75-
76-
:param tz: ``pytz.timezone`` or a string representing the timezone
77-
:param date: The date to obtain the UTC offset for
78-
"""
79-
80-
if date is None:
81-
date = datetime.datetime.utcnow()
82-
83-
timezone: Optional[datetime.tzinfo]
84-
85-
if isinstance(tz, str):
86-
timezone = get_timezone(tz, date)
87-
else:
88-
timezone = tz # pragma: no cover (hard to test)
89-
90-
return date.replace(tzinfo=pytz.utc).astimezone(timezone).utcoffset()
91-
92-
def get_timezone(tz: str, date: Optional[datetime.datetime] = None) -> Optional[datetime.tzinfo]:
93-
"""
94-
Returns a localized ``pytz.timezone`` object for the given date.
95-
If ``date`` is :py:obj:`None` then the current date is used.
96-
97-
:param tz: A string representing a pytz timezone
98-
:param date: The date to obtain the timezone for
99-
"""
100-
101-
if date is None:
102-
date = datetime.datetime.utcnow() # pragma: no cover (hard to test)
103-
104-
d = date.replace(tzinfo=None)
105-
106-
return pytz.timezone(tz).localize(d).tzinfo
107-
108-
__all__.extend(["get_utc_offset", "get_timezone"])
109-
110-
except ImportError as e: # pragma: no cover
111-
112-
# stdlib
113-
import warnings
114-
115-
warnings.warn(
116-
f"""\
117-
Some functions in 'domdf_python_tools.dates' require pytz (https://pypi.org/project/pytz/), \
118-
but it could not be imported.
119-
120-
The error was: {e}.
121-
""",
122-
)
123-
12461

12562
def current_tzinfo() -> Optional[datetime.tzinfo]:
12663
"""
@@ -192,9 +129,9 @@ def utc_timestamp_to_datetime(
192129
return new_datetime.astimezone(output_tz)
193130

194131

195-
if sys.version_info <= (3, 7, 2):
132+
if sys.version_info <= (3, 7, 2): # pragma: no cover (py37+)
196133
MonthsType = OrderedDict
197-
else:
134+
else: # pragma: no cover (<py37)
198135
MonthsType = typing.OrderedDict[str, str] # type: ignore # noqa: TYP006
199136

200137
#: Mapping of 3-character shortcodes to full month names.
@@ -302,3 +239,82 @@ def calc_easter(year: int) -> datetime.date:
302239
day = f % 31 + 1
303240

304241
return datetime.date(year, month, day)
242+
243+
244+
def get_utc_offset(
245+
tz: Union[datetime.tzinfo, str],
246+
date: Optional[datetime.datetime] = None,
247+
) -> Optional[datetime.timedelta]:
248+
"""
249+
Returns the offset between UTC and the requested timezone on the given date.
250+
If ``date`` is :py:obj:`None` then the current date is used.
251+
252+
:param tz: ``pytz.timezone`` or a string representing the timezone
253+
:param date: The date to obtain the UTC offset for
254+
"""
255+
256+
if date is None:
257+
date = datetime.datetime.utcnow()
258+
259+
timezone: Optional[datetime.tzinfo]
260+
261+
if isinstance(tz, str):
262+
timezone = get_timezone(tz, date)
263+
else:
264+
timezone = tz # pragma: no cover (hard to test)
265+
266+
return date.replace(tzinfo=pytz.utc).astimezone(timezone).utcoffset()
267+
268+
269+
def get_timezone(tz: str, date: Optional[datetime.datetime] = None) -> Optional[datetime.tzinfo]:
270+
"""
271+
Returns a localized ``pytz.timezone`` object for the given date.
272+
If ``date`` is :py:obj:`None` then the current date is used.
273+
274+
:param tz: A string representing a pytz timezone
275+
:param date: The date to obtain the timezone for
276+
"""
277+
278+
if date is None:
279+
date = datetime.datetime.utcnow() # pragma: no cover (hard to test)
280+
281+
d = date.replace(tzinfo=None)
282+
283+
return pytz.timezone(tz).localize(d).tzinfo
284+
285+
286+
_pytz_functions = ["get_utc_offset", "get_timezone"]
287+
288+
try:
289+
290+
# 3rd party
291+
import pytz
292+
293+
__all__.extend(_pytz_functions)
294+
295+
except ImportError as e:
296+
297+
if __name__ == "__main__":
298+
299+
import warnings
300+
from domdf_python_tools.words import word_join
301+
302+
warnings.warn(f"""\
303+
'{word_join(_pytz_functions)}' require pytz (https://pypi.org/project/pytz/), but it could not be imported.
304+
305+
The error was: {e}.
306+
""")
307+
308+
else:
309+
_actual_module = sys.modules[__name__]
310+
311+
class SelfWrapper(ModuleType):
312+
def __getattr__(self, name):
313+
if name in _pytz_functions:
314+
raise ImportError(
315+
f"{name!r} requires pytz (https://pypi.org/project/pytz/), but it could not be imported."
316+
)
317+
else:
318+
return getattr(_actual_module, name)
319+
320+
sys.modules[__name__] = SelfWrapper(__name__)

repo_helper.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,6 @@ sphinx_conf_preamble:
5656
sphinx_conf_epilogue:
5757
- manpages_url = "https://manpages.debian.org/{path}"
5858

59-
tox_testenv_extras: all
60-
6159
intersphinx_mapping:
6260
- "'pandas': ('https://pandas.pydata.org/docs/', None)"
6361
- "'consolekit': ('https://consolekit.readthedocs.io/en/latest/', None)"
@@ -68,3 +66,6 @@ mypy_deps:
6866

6967
extra_sphinx_extensions:
7068
- sphinx_autofixture
69+
70+
exclude_files:
71+
- tox

tests/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ pytest-randomly>=3.3.1
99
pytest-regressions>=2.0.1
1010
pytest-rerunfailures>=9.1.1
1111
pytest-timeout>=1.4.2
12-
pytz>=2019.1
12+

0 commit comments

Comments
 (0)