Skip to content

Commit

Permalink
Remove Pendulum dependencie with custom datetime subclass
Browse files Browse the repository at this point in the history
  • Loading branch information
Delgan committed Dec 6, 2018
1 parent aef4571 commit c3a062c
Show file tree
Hide file tree
Showing 14 changed files with 353 additions and 165 deletions.
82 changes: 82 additions & 0 deletions loguru/_datetime.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
from datetime import datetime as datetime_, timezone, timedelta
from calendar import day_name, day_abbr, month_name, month_abbr
from time import time, localtime
import re


tokens = r"H{1,2}|h{1,2}|m{1,2}|s{1,2}|S{1,6}|YYYY|YY|M{1,4}|D{1,4}|Z{1,2}|zz|A|X|x|E|Q|dddd|ddd|d"

pattern = re.compile(r"(?:{0})|\[(?:{0})\]".format(tokens))


def now():
t = time()
local = localtime(t)
microsecond = int(abs(t) * 1e6 % 1e6)
tzinfo = timezone(timedelta(seconds=local.tm_gmtoff), local.tm_zone)
year, month, day, hour, minute, second, _, _, _ = local
return datetime(year, month, day, hour, minute, second, microsecond, tzinfo)


class datetime(datetime_):

def __format__(self, spec):
if not spec:
spec = "%Y-%m-%dT%H:%M:%S.%f%z"

if '%' in spec:
return super().__format__(spec)

year, month, day, hour, minute, second, weekday, yearday, _ = self.timetuple()
microsecond = self.microsecond
timestamp = self.timestamp()
tzinfo = self.tzinfo or timezone(timedelta(seconds=0))
offset = tzinfo.utcoffset(self).total_seconds()
sign = ('-', '+')[offset >= 0]
h, m = divmod(abs(offset // 60), 60)

rep = {
'YYYY': '%04d' % year,
'YY': '%02d' % (year % 100),
'Q': '%d' % ((month - 1) // 3 + 1),
'MMMM': month_name[month - 1],
'MMM': month_abbr[month - 1],
'MM': '%02d' % month,
'M': '%d' % month,
'DDDD': '%03d' % yearday,
'DDD': '%d' % yearday,
'DD': '%02d' % day,
'D': '%d' % day,
'dddd': day_name[weekday],
'ddd': day_abbr[weekday],
'd': '%d' % weekday,
'E': '%d' % (weekday + 1),
'HH': '%02d' % hour,
'H': '%d' % hour,
'hh': '%02d' % ((hour - 1) % 12 + 1),
'h': '%d' % ((hour - 1) % 12 + 1),
'mm': '%02d' % minute,
'm': '%d' % minute,
'ss': '%02d' % second,
's': '%d' % second,
'S': '%d' % (microsecond // 100000),
'SS': '%02d' % (microsecond // 10000),
'SSS': '%03d' % (microsecond // 1000),
'SSSS': '%04d' % (microsecond // 100),
'SSSSS': '%05d' % (microsecond // 10),
'SSSSSS': '%06d' % microsecond,
'A': ('AM', 'PM')[hour // 12],
'Z': '%s%02d:%02d' % (sign, h, m),
'ZZ': '%s%02d%02d' % (sign, h, m),
'zz': tzinfo.tzname(self) or '',
'X': '%d' % timestamp,
'x': '%d' % (int(timestamp) * 1000000 + microsecond),
}

def get(m):
try:
return rep[m[0]]
except KeyError:
return m[0][1:-1]

return pattern.sub(get, spec)
68 changes: 32 additions & 36 deletions loguru/_file_sink.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,19 @@
import shutil
import string

import pendulum
from pendulum import now as pendulum_now
from . import _string_parsers as string_parsers
from ._datetime import now

from . import _string_parsers

class FileDateFormatter:

class FileDateTime(pendulum.DateTime):
def __init__(self):
self.datetime = now()

def __format__(self, spec):
if not spec:
spec = "YYYY-MM-DD_HH-mm-ss_SSSSSS"
return super().__format__(spec)

@classmethod
def now(cls):
# TODO: Use FileDateTime.now() instead, when pendulum/#203 fixed
t = pendulum_now()
return cls(t.year, t.month, t.day, t.hour, t.minute, t.second, t.microsecond, t.tzinfo, fold=t.fold)
spec = "%Y-%m-%d_%H-%M-%S_%f"
return self.datetime.__format__(spec)


class FileSink:
Expand Down Expand Up @@ -78,14 +73,14 @@ def initialize_file(self, *, rename_existing):

if rename_existing and os.path.isfile(new_path):
root, ext = os.path.splitext(new_path)
renamed_path = "{}.{:YYYY-MM-DD_HH-mm-ss_SSSSSS}{}".format(root, pendulum_now(), ext)
renamed_path = "{}.{}{}".format(root, FileDateFormatter(), ext)
os.rename(new_path, renamed_path)

self.file = open(new_path, mode=self.mode, buffering=self.buffering, **self.kwargs)
self.file_path = new_path

def format_path(self):
path = self.path.format_map({'time': FileDateTime.now()})
path = self.path.format_map({'time': FileDateFormatter()})
return os.path.abspath(path)

@staticmethod
Expand All @@ -108,15 +103,17 @@ def rotation_function(message, file):
return rotation_function

def make_from_time(step_forward, time_init=None):
start_time = time_limit = pendulum_now()
start_time = time_limit = now().replace(tzinfo=None)
if time_init is not None:
t = time_init
time_limit = time_limit.at(t.hour, t.minute, t.second, t.microsecond)
time_limit = time_limit.replace(hour=time_init.hour,
minute=time_init.minute,
second=time_init.second,
microsecond=time_init.microsecond)
if time_limit <= start_time:
time_limit = step_forward(time_limit)
def rotation_function(message, file):
nonlocal time_limit
record_time = message.record['time']
record_time = message.record['time'].replace(tzinfo=None)
if record_time >= time_limit:
while time_limit <= record_time:
time_limit = step_forward(time_limit)
Expand All @@ -127,40 +124,38 @@ def rotation_function(message, file):
if rotation is None:
return None
elif isinstance(rotation, str):
size = _string_parsers.parse_size(rotation)
size = string_parsers.parse_size(rotation)
if size is not None:
return self.make_rotation_function(size)
interval = _string_parsers.parse_duration(rotation)
interval = string_parsers.parse_duration(rotation)
if interval is not None:
return self.make_rotation_function(interval)
frequency = _string_parsers.parse_frequency(rotation)
frequency = string_parsers.parse_frequency(rotation)
if frequency is not None:
return make_from_time(frequency)
daytime = _string_parsers.parse_daytime(rotation)
daytime = string_parsers.parse_daytime(rotation)
if daytime is not None:
day, time = daytime
if day is None:
return self.make_rotation_function(time)
if time is None:
time = pendulum.parse('00:00', exact=True, strict=False)
time = datetime.time(0, 0, 0)
def next_day(t):
return t.next(day, keep_time=True)
while True:
t += datetime.timedelta(days=1)
if t.weekday() == day:
return t
return make_from_time(next_day, time_init=time)
raise ValueError("Cannot parse rotation from: '%s'" % rotation)
elif isinstance(rotation, (numbers.Real, decimal.Decimal)):
return make_from_size(rotation)
elif isinstance(rotation, datetime.time):
time = pendulum.Time(hour=rotation.hour, minute=rotation.minute,
second=rotation.second, microsecond=rotation.microsecond,
tzinfo=rotation.tzinfo, fold=rotation.fold)
def next_day(t):
return t.add(days=1)
return make_from_time(next_day, time_init=time)
return t + datetime.timedelta(days=1)
return make_from_time(next_day, time_init=rotation)
elif isinstance(rotation, datetime.timedelta):
interval = pendulum.Duration(days=rotation.days, seconds=rotation.seconds,
microseconds=rotation.microseconds)
def add_interval(t):
return t + interval
return t + rotation
return make_from_time(add_interval)
elif callable(rotation):
return rotation
Expand All @@ -178,7 +173,7 @@ def retention_function(logs):
if retention is None:
return None
elif isinstance(retention, str):
interval = _string_parsers.parse_duration(retention)
interval = string_parsers.parse_duration(retention)
if interval is None:
raise ValueError("Cannot parse retention from: '%s'" % retention)
return self.make_retention_function(interval)
Expand All @@ -191,7 +186,7 @@ def filter_logs(logs):
elif isinstance(retention, datetime.timedelta):
seconds = retention.total_seconds()
def filter_logs(logs):
t = pendulum_now().timestamp()
t = now().timestamp()
return [log for log in logs if os.stat(log).st_mtime <= t - seconds]
return make_from_filter(filter_logs)
elif callable(retention):
Expand Down Expand Up @@ -259,8 +254,9 @@ def compression_function(path_in):
path_out = "{}.{}".format(path_in, ext)
if os.path.isfile(path_out):
root, ext_before = os.path.splitext(path_in)
renamed_template = "{}.{:YYYY-MM-DD_HH-mm-ss_SSSSSS}{}.{}"
renamed_path = renamed_template.format(root, pendulum_now(), ext_before, ext)
renamed_template = "{}.{}{}.{}"
date = FileDateFormatter()
renamed_path = renamed_template.format(root, date, ext_before, ext)
os.rename(path_out, renamed_path)
compress(path_in, path_out)
os.remove(path_in)
Expand Down
2 changes: 1 addition & 1 deletion loguru/_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def serialize_record(text, record):
'name': record['name'],
'process': dict(id=record['process'].id, name=record['process'].name),
'thread': dict(id=record['thread'].id, name=record['thread'].name),
'time': dict(repr=record['time'], timestamp=record['time'].float_timestamp),
'time': dict(repr=record['time'], timestamp=record['time'].timestamp()),
}
}

Expand Down
14 changes: 7 additions & 7 deletions loguru/_logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,25 @@
import os
import threading
from collections import namedtuple
from datetime import timedelta
from inspect import isclass
from multiprocessing import current_process
from os import PathLike
from os.path import basename, normcase, splitext
from threading import current_thread

import pendulum
from pendulum import now as pendulum_now
from colorama import AnsiToWin32

from . import _defaults
from ._datetime import now
from ._file_sink import FileSink
from ._get_frame import get_frame
from ._handler import Handler
from ._recattrs import LevelRecattr, FileRecattr, ThreadRecattr, ProcessRecattr, ExceptionRecattr

Level = namedtuple('Level', ['no', 'color', 'icon'])

start_time = pendulum_now()
start_time = now()


class Logger:
Expand Down Expand Up @@ -384,7 +384,7 @@ def log_function(_self, _message, *args, **kwargs):
return
_self._enabled[name] = True

now = pendulum_now()
current_datetime = now()

if level_id is None:
level_no, level_color, level_icon = level, '', ' '
Expand All @@ -402,8 +402,8 @@ def log_function(_self, _message, *args, **kwargs):
file_name = basename(file_path)
thread = current_thread()
process = current_process()
diff = now - start_time
elapsed = pendulum.Duration(microseconds=diff.microseconds)
diff = current_datetime - start_time
elapsed = timedelta(microseconds=diff.microseconds)

level_recattr = LevelRecattr(level_name)
level_recattr.no, level_recattr.name, level_recattr.icon = level_no, level_name, level_icon
Expand Down Expand Up @@ -435,7 +435,7 @@ def log_function(_self, _message, *args, **kwargs):
'name': name,
'process': process_recattr,
'thread': thread_recattr,
'time': now,
'time': current_datetime,
}

if _self._lazy:
Expand Down
Loading

0 comments on commit c3a062c

Please sign in to comment.