Skip to content

Commit

Permalink
Fix incompatibility with "freezegun" library to simulate time (Delgan…
Browse files Browse the repository at this point in the history
  • Loading branch information
Delgan committed May 4, 2022
1 parent 4c9b7e0 commit 21cc3a5
Show file tree
Hide file tree
Showing 10 changed files with 417 additions and 400 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- Fix logs colorization not automatically enabled for Jupyter Notebook and Google Colab (`#494 <https://github.com/Delgan/loguru/issues/494>`_).
- Fix logs colorization not automatically enabled for Github Actions and others CI platforms (`#604 <https://github.com/Delgan/loguru/issues/604>`_).
- Fix ``logger.complete()`` possibly hanging forever when ``enqueue=True`` and ``catch=False`` if internal thread killed due to ``Exception`` raised by sink (`#647 <https://github.com/Delgan/loguru/issues/647>`_).
- Fix incompatibility with ``freezegun`` library used to simulate time (`#600 <https://github.com/Delgan/loguru/issues/600>`_).
- Raise exception if ``logger.catch()`` is used to wrap a class instead of a function to avoid unexpected behavior (`#623 <https://github.com/Delgan/loguru/issues/623>`_).


Expand Down
7 changes: 4 additions & 3 deletions loguru/_datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,17 +79,18 @@ def get(m):


def aware_now():
now = datetime.now()
now = datetime_.now()
timestamp = now.timestamp()
local = localtime(timestamp)

try:
seconds = local.tm_gmtoff
zone = local.tm_zone
except AttributeError:
offset = datetime.fromtimestamp(timestamp) - datetime.utcfromtimestamp(timestamp)
offset = datetime_.fromtimestamp(timestamp) - datetime_.utcfromtimestamp(timestamp)
seconds = offset.total_seconds()
zone = strftime("%Z")

tzinfo = timezone(timedelta(seconds=seconds), zone)
return now.replace(tzinfo=tzinfo)

return datetime.combine(now.date(), now.time().replace(tzinfo=tzinfo))
24 changes: 12 additions & 12 deletions loguru/_file_sink.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import datetime as datetime_
import datetime
import decimal
import glob
import numbers
Expand All @@ -10,11 +10,11 @@

from . import _string_parsers as string_parsers
from ._ctime_functions import get_ctime, set_ctime
from ._datetime import aware_now, datetime
from ._datetime import aware_now


def generate_rename_path(root, ext, creation_time):
creation_datetime = datetime.fromtimestamp(creation_time)
creation_datetime = datetime.datetime.fromtimestamp(creation_time)
date = FileDateFormatter(creation_datetime)

renamed_path = "{}.{}{}".format(root, date, ext)
Expand Down Expand Up @@ -78,7 +78,7 @@ def key_log(log):

@staticmethod
def retention_age(logs, seconds):
t = datetime.now().timestamp()
t = datetime.datetime.now().timestamp()
for log in logs:
if os.stat(log).st_mtime <= t - seconds:
os.remove(log)
Expand All @@ -87,12 +87,12 @@ def retention_age(logs, seconds):
class Rotation:
@staticmethod
def forward_day(t):
return t + datetime_.timedelta(days=1)
return t + datetime.timedelta(days=1)

@staticmethod
def forward_weekday(t, weekday):
while True:
t += datetime_.timedelta(days=1)
t += datetime.timedelta(days=1)
if t.weekday() == weekday:
return t

Expand All @@ -116,7 +116,7 @@ def __call__(self, message, file):
filepath = os.path.realpath(file.name)
creation_time = get_ctime(filepath)
set_ctime(filepath, creation_time)
start_time = limit = datetime.fromtimestamp(creation_time)
start_time = limit = datetime.datetime.fromtimestamp(creation_time)
if self._time_init is not None:
limit = limit.replace(
hour=self._time_init.hour,
Expand Down Expand Up @@ -273,7 +273,7 @@ def _terminate_file(self, *, is_rotating=False):

if is_rotating:
self._create_file(new_path)
set_ctime(new_path, datetime.now().timestamp())
set_ctime(new_path, datetime.datetime.now().timestamp())

@staticmethod
def _make_glob_patterns(path):
Expand Down Expand Up @@ -308,15 +308,15 @@ def _make_rotation_function(rotation):
if day is None:
return FileSink._make_rotation_function(time)
if time is None:
time = datetime_.time(0, 0, 0)
time = datetime.time(0, 0, 0)
step_forward = partial(Rotation.forward_weekday, weekday=day)
return Rotation.RotationTime(step_forward, time)
raise ValueError("Cannot parse rotation from: '%s'" % rotation)
elif isinstance(rotation, (numbers.Real, decimal.Decimal)):
return partial(Rotation.rotation_size, size_limit=rotation)
elif isinstance(rotation, datetime_.time):
elif isinstance(rotation, datetime.time):
return Rotation.RotationTime(Rotation.forward_day, rotation)
elif isinstance(rotation, datetime_.timedelta):
elif isinstance(rotation, datetime.timedelta):
step_forward = partial(Rotation.forward_interval, interval=rotation)
return Rotation.RotationTime(step_forward)
elif callable(rotation):
Expand All @@ -337,7 +337,7 @@ def _make_retention_function(retention):
return FileSink._make_retention_function(interval)
elif isinstance(retention, int):
return partial(Retention.retention_count, number=retention)
elif isinstance(retention, datetime_.timedelta):
elif isinstance(retention, datetime.timedelta):
return partial(Retention.retention_age, seconds=retention.total_seconds())
elif callable(retention):
return retention
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"colorama>=0.3.4",
"docutils==0.16",
"flake8>=3.7.7",
"freezegun==1.1.0",
"isort>=5.1.1 ; python_version>='3.6'",
"tox>=3.9.0",
"pytest>=4.6.2",
Expand Down
77 changes: 47 additions & 30 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
import time
import traceback
import warnings
from collections import namedtuple

import freezegun
import loguru
import pytest

Expand Down Expand Up @@ -118,41 +120,56 @@ def write(self, message):


@pytest.fixture
def monkeypatch_date(monkeypatch):

fix_struct = os.name == "nt" and sys.version_info < (3, 6)

def monkeypatch_date(year, month, day, hour, minute, second, microsecond, zone="UTC", offset=0):
dt = loguru._datetime.datetime(year, month, day, hour, minute, second, microsecond)

if fix_struct:

class StructTime:
def __init__(self, struct, tm_zone, tm_gmtoff):
self._struct = struct
self.tm_zone = tm_zone
self.tm_gmtoff = tm_gmtoff

def __getattr__(self, name):
return getattr(self._struct, name)

def __iter__(self):
return iter(self._struct)

struct = StructTime(time.struct_time([*dt.timetuple()]), zone, offset)
def freeze_time(monkeypatch):
@contextlib.contextmanager
def freeze_time(date, timezone=("UTC", 0), *, include_tm_zone=True):
zone, offset = timezone
fix_struct = os.name == "nt" and sys.version_info < (3, 6)

struct_time_attributes = [
"tm_year",
"tm_mon",
"tm_mday",
"tm_hour",
"tm_min",
"tm_sec",
"tm_wday",
"tm_yday",
"tm_isdst",
"tm_zone",
"tm_gmtoff",
]

if not include_tm_zone:
struct_time_attributes.remove("tm_zone")
struct_time_attributes.remove("tm_gmtoff")
struct_time = namedtuple("struct_time", struct_time_attributes)._make
elif fix_struct:
struct_time = namedtuple("struct_time", struct_time_attributes)._make
else:
struct = time.struct_time([*dt.timetuple()] + [zone, offset])
struct_time = time.struct_time

freezegun_localtime = freezegun.api.fake_localtime

def patched_now(tz=None):
return dt
def fake_localtime(t=None):
struct = freezegun_localtime(t)
override = {"tm_zone": zone, "tm_gmtoff": offset}
attributes = []
for attribute in struct_time_attributes:
if attribute in override:
value = override[attribute]
else:
value = getattr(struct, attribute)
attributes.append(value)
return struct_time(attributes)

def patched_localtime(t):
return struct
# Freezegun does not permit to override timezone name.
monkeypatch.setattr(freezegun.api, "fake_localtime", fake_localtime)

monkeypatch.setattr(loguru._datetime.datetime, "now", patched_now)
monkeypatch.setattr(loguru._datetime, "localtime", patched_localtime)
with freezegun.freeze_time(date) as frozen:
yield frozen

return monkeypatch_date
return freeze_time


@contextlib.contextmanager
Expand Down
4 changes: 1 addition & 3 deletions tests/test_coroutine_sink.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import asyncio
import logging
import multiprocessing
import platform
import re
import sys
import threading

import pytest

import loguru
import pytest
from loguru import logger


Expand Down
Loading

0 comments on commit 21cc3a5

Please sign in to comment.