dbt-selly/dbt-env/lib/python3.8/site-packages/logbook/helpers.py

310 lines
8.2 KiB
Python
Raw Normal View History

2022-03-22 15:13:27 +00:00
# -*- coding: utf-8 -*-
"""
logbook.helpers
~~~~~~~~~~~~~~~
Various helper functions
:copyright: (c) 2010 by Armin Ronacher, Georg Brandl.
:license: BSD, see LICENSE for more details.
"""
import os
import re
import sys
import errno
import time
import random
from datetime import datetime, timedelta
PY2 = sys.version_info[0] == 2
if PY2:
import __builtin__ as _builtins
import collections as collections_abc
else:
import builtins as _builtins
import collections.abc as collections_abc
try:
import json
except ImportError:
import simplejson as json
if PY2:
from cStringIO import StringIO
iteritems = dict.iteritems
from itertools import izip as zip
xrange = _builtins.xrange
else:
from io import StringIO
zip = _builtins.zip
xrange = range
iteritems = dict.items
_IDENTITY = lambda obj: obj
if PY2:
def u(s):
return unicode(s, "unicode_escape")
else:
u = _IDENTITY
if PY2:
integer_types = (int, long)
string_types = (basestring,)
else:
integer_types = (int,)
string_types = (str,)
if PY2:
import httplib as http_client
else:
from http import client as http_client
if PY2:
# Yucky, but apparently that's the only way to do this
exec("""
def reraise(tp, value, tb=None):
raise tp, value, tb
""", locals(), globals())
else:
def reraise(tp, value, tb=None):
if value.__traceback__ is not tb:
raise value.with_traceback(tb)
raise value
# this regexp also matches incompatible dates like 20070101 because
# some libraries (like the python xmlrpclib modules) use this
_iso8601_re = re.compile(
# date
r'(\d{4})(?:-?(\d{2})(?:-?(\d{2}))?)?'
# time
r'(?:T(\d{2}):(\d{2})(?::(\d{2}(?:\.\d+)?))?(Z|[+-]\d{2}:\d{2})?)?$'
)
_missing = object()
if PY2:
def b(x):
return x
def _is_text_stream(x):
return True
else:
import io
def b(x):
return x.encode('ascii')
def _is_text_stream(stream):
return isinstance(stream, io.TextIOBase)
can_rename_open_file = False
if os.name == 'nt':
try:
import ctypes
_MOVEFILE_REPLACE_EXISTING = 0x1
_MOVEFILE_WRITE_THROUGH = 0x8
_MoveFileEx = ctypes.windll.kernel32.MoveFileExW
def _rename(src, dst):
if PY2:
if not isinstance(src, unicode):
src = unicode(src, sys.getfilesystemencoding())
if not isinstance(dst, unicode):
dst = unicode(dst, sys.getfilesystemencoding())
if _rename_atomic(src, dst):
return True
retry = 0
rv = False
while not rv and retry < 100:
rv = _MoveFileEx(src, dst, _MOVEFILE_REPLACE_EXISTING |
_MOVEFILE_WRITE_THROUGH)
if not rv:
time.sleep(0.001)
retry += 1
return rv
# new in Vista and Windows Server 2008
_CreateTransaction = ctypes.windll.ktmw32.CreateTransaction
_CommitTransaction = ctypes.windll.ktmw32.CommitTransaction
_MoveFileTransacted = ctypes.windll.kernel32.MoveFileTransactedW
_CloseHandle = ctypes.windll.kernel32.CloseHandle
can_rename_open_file = True
def _rename_atomic(src, dst):
ta = _CreateTransaction(None, 0, 0, 0, 0, 1000, 'Logbook rename')
if ta == -1:
return False
try:
retry = 0
rv = False
while not rv and retry < 100:
rv = _MoveFileTransacted(src, dst, None, None,
_MOVEFILE_REPLACE_EXISTING |
_MOVEFILE_WRITE_THROUGH, ta)
if rv:
rv = _CommitTransaction(ta)
break
else:
time.sleep(0.001)
retry += 1
return rv
finally:
_CloseHandle(ta)
except Exception:
def _rename(src, dst):
return False
def _rename_atomic(src, dst):
return False
def rename(src, dst):
# Try atomic or pseudo-atomic rename
if _rename(src, dst):
return
# Fall back to "move away and replace"
try:
os.rename(src, dst)
except OSError:
e = sys.exc_info()[1]
if e.errno not in (errno.EEXIST, errno.EACCES):
raise
old = "%s-%08x" % (dst, random.randint(0, 2 ** 31 - 1))
os.rename(dst, old)
os.rename(src, dst)
try:
os.unlink(old)
except Exception:
pass
else:
rename = os.rename
can_rename_open_file = True
_JSON_SIMPLE_TYPES = (bool, float) + integer_types + string_types
def to_safe_json(data):
"""Makes a data structure safe for JSON silently discarding invalid
objects from nested structures. This also converts dates.
"""
def _convert(obj):
if obj is None:
return None
elif PY2 and isinstance(obj, str):
return obj.decode('utf-8', 'replace')
elif isinstance(obj, _JSON_SIMPLE_TYPES):
return obj
elif isinstance(obj, datetime):
return format_iso8601(obj)
elif isinstance(obj, list):
return [_convert(x) for x in obj]
elif isinstance(obj, tuple):
return tuple(_convert(x) for x in obj)
elif isinstance(obj, dict):
rv = {}
for key, value in iteritems(obj):
if not isinstance(key, string_types):
key = str(key)
if not is_unicode(key):
key = u(key)
rv[key] = _convert(value)
return rv
return _convert(data)
def format_iso8601(d=None):
"""Returns a date in iso8601 format."""
if d is None:
d = datetime.utcnow()
rv = d.strftime('%Y-%m-%dT%H:%M:%S')
if d.microsecond:
rv += '.' + str(d.microsecond)
return rv + 'Z'
def parse_iso8601(value):
"""Parse an iso8601 date into a datetime object. The timezone is
normalized to UTC.
"""
m = _iso8601_re.match(value)
if m is None:
raise ValueError('not a valid iso8601 date value')
groups = m.groups()
args = []
for group in groups[:-2]:
if group is not None:
group = int(group)
args.append(group)
seconds = groups[-2]
if seconds is not None:
if '.' in seconds:
sec, usec = seconds.split('.')
args.append(int(sec))
args.append(int(usec.ljust(6, '0')))
else:
args.append(int(seconds))
rv = datetime(*args)
tz = groups[-1]
if tz and tz != 'Z':
args = [int(x) for x in tz[1:].split(':')]
delta = timedelta(hours=args[0], minutes=args[1])
if tz[0] == '+':
rv -= delta
else:
rv += delta
return rv
def get_application_name():
if not sys.argv or not sys.argv[0]:
return 'Python'
return os.path.basename(sys.argv[0]).title()
class cached_property(object):
"""A property that is lazily calculated and then cached."""
def __init__(self, func, name=None, doc=None):
self.__name__ = name or func.__name__
self.__module__ = func.__module__
self.__doc__ = doc or func.__doc__
self.func = func
def __get__(self, obj, type=None):
if obj is None:
return self
value = obj.__dict__.get(self.__name__, _missing)
if value is _missing:
value = self.func(obj)
obj.__dict__[self.__name__] = value
return value
def get_iterator_next_method(it):
return lambda: next(it)
# python 2 support functions and aliases
def is_unicode(x):
if PY2:
return isinstance(x, unicode)
return isinstance(x, str)
if PY2:
exec("""def with_metaclass(meta):
class _WithMetaclassBase(object):
__metaclass__ = meta
return _WithMetaclassBase
""")
else:
exec("""def with_metaclass(meta):
class _WithMetaclassBase(object, metaclass=meta):
pass
return _WithMetaclassBase
""")