Skip to content

Commit 6124bfc

Browse files
author
Pietro Albini
committed
Split botogram.utils in multiple files
This will aid maintenance
1 parent bd53348 commit 6124bfc

File tree

9 files changed

+297
-245
lines changed

9 files changed

+297
-245
lines changed

botogram/utils.py

Lines changed: 0 additions & 200 deletions
This file was deleted.

botogram/utils/__init__.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
"""
2+
botogram.utils
3+
Utilities used by the rest of the code
4+
5+
Copyright (c) 2016 Pietro Albini <[email protected]>
6+
Released under the MIT license
7+
"""
8+
9+
# flake8: noqa
10+
11+
from .deprecations import deprecated, DeprecatedAttributes, warn
12+
from .strings import strip_urls, usernames_in
13+
from .startup import get_language, configure_logger
14+
from .calls import wraps, CallLazyArgument, call

botogram/utils/calls.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
"""
2+
botogram.utils.calls
3+
Utilities for dynamic function args
4+
5+
Copyright (c) 2015-2016 Pietro Albini <[email protected]>
6+
Released under the MIT license
7+
"""
8+
9+
import inspect
10+
11+
import functools
12+
13+
14+
def wraps(func):
15+
"""Update a wrapper function to looks like the wrapped one"""
16+
# A custom implementation of functools.wraps is needed because we need some
17+
# more metadata on the returned function
18+
def updater(original):
19+
# Here the original signature is needed in order to call the function
20+
# with the right set of arguments in Bot._call
21+
original_signature = inspect.signature(original)
22+
23+
updated = functools.update_wrapper(original, func)
24+
updated._botogram_original_signature = original_signature
25+
return updated
26+
return updater
27+
28+
29+
class CallLazyArgument:
30+
"""A special argument which is loaded lazily"""
31+
32+
_botogram_call_lazy_argument = True
33+
34+
def __init__(self, loader):
35+
self.loader = loader
36+
37+
def load(self):
38+
return self.loader()
39+
40+
41+
def call(func, **available):
42+
"""Call a function with a dynamic set of arguments"""
43+
# Get the correct function signature
44+
# _botogram_original_signature contains the signature used before wrapping
45+
# a function with @utils.wraps, so the arguments gets resolved correctly
46+
if hasattr(func, "_botogram_original_signature"):
47+
signature = func._botogram_original_signature
48+
else:
49+
signature = inspect.signature(func)
50+
51+
kwargs = {}
52+
for name in signature.parameters:
53+
if name not in available:
54+
raise TypeError("botogram doesn't know what to provide for %s"
55+
% name)
56+
57+
# If the argument is lazily loaded wake him up
58+
arg = available[name]
59+
if hasattr(arg, "_botogram_call_lazy_argument"):
60+
arg = arg.load()
61+
62+
kwargs[name] = arg
63+
64+
return func(**kwargs)

botogram/utils/deprecations.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
"""
2+
botogram.utils.warns
3+
Utilities about warns and deprecations
4+
5+
Copyright (c) 2015-2016 Pietro Albini <[email protected]>
6+
Released under the MIT license
7+
"""
8+
9+
import sys
10+
import traceback
11+
12+
import logbook
13+
14+
15+
warn_logger = logbook.Logger("botogram's code warnings")
16+
17+
18+
def _deprecated_message(name, removed_on, fix, back):
19+
before = "%s will be removed in botogram %s." % (name, removed_on)
20+
after = "Fix: %s" % fix
21+
warn(back - 1, before, after)
22+
23+
24+
def deprecated(name, removed_on, fix, back=0):
25+
"""Mark a function as deprecated"""
26+
def decorator(func):
27+
def wrapper(*args, **kwargs):
28+
_deprecated_message(name, removed_on, fix, -2 - back)
29+
return func(*args, **kwargs)
30+
return wrapper
31+
return decorator
32+
33+
34+
class DeprecatedAttributes:
35+
"""Mark a class attribute as deprecated"""
36+
37+
_deprecated_ = {}
38+
39+
def __getattribute__(self, key):
40+
def get(k):
41+
return object.__getattribute__(self, k)
42+
43+
deprecated = get("_deprecated_")
44+
45+
if key in deprecated:
46+
_deprecated_message(
47+
get("__class__").__name__ + "." + key,
48+
deprecated[key]["removed_on"],
49+
deprecated[key]["fix"],
50+
-2,
51+
)
52+
if "callback" in deprecated[key]:
53+
return deprecated[key]["callback"]()
54+
55+
return object.__getattribute__(self, key)
56+
57+
58+
def warn(stack_pos, before_message, after_message=None):
59+
"""Issue a warning caused by user code"""
60+
# This is a workaround for http://bugs.python.org/issue25108
61+
# In Python 3.5.0, traceback.extract_stack returns an additional internal
62+
# stack frame, which causes a lot of trouble around there.
63+
if sys.version_info[:3] == (3, 5, 0):
64+
stack_pos -= 1
65+
66+
frame = traceback.extract_stack()[stack_pos - 1]
67+
at_message = "At: %s (line %s)" % (frame[0], frame[1])
68+
69+
warn_logger.warn(before_message)
70+
if after_message is not None:
71+
warn_logger.warn(at_message)
72+
warn_logger.warn(after_message + "\n")
73+
else:
74+
warn_logger.warn(at_message + "\n")

0 commit comments

Comments
 (0)