egse.decorators
A collection of useful decorator functions.
Classes:
Name | Description |
---|---|
Nothing |
Just to get a nice repr for Nothing. It is kind of a Null object... |
Profiler |
A simple profiler class that provides some useful functions to profile a function. |
classproperty |
Defines a read-only class property. |
Functions:
Name | Description |
---|---|
average_time |
This is a decorator that is intended mainly as a development aid. When you decorate your function with |
borg |
Use the Borg pattern to make a class with a shared state between its instances and subclasses. |
debug |
Logs the function signature and return value. |
deprecate |
Deprecate a function or method. This will print a warning with the function name and where |
dynamic_interface |
Adds a static variable |
profile |
Prints the function signature and return value to stdout. |
profile_func |
A time profiler decorator. |
query_command |
Adds a static variable |
read_command |
Adds a static variable |
retry |
Decorator that retries a function multiple times with a delay between attempts. |
retry_with_exponential_backoff |
Decorator for retrying a function with exponential backoff. |
singleton |
Use class as a singleton. |
spy_on_attr_change |
Tweak an object to show attributes changing. The changes are reported as WARNING log messages |
static_vars |
Define static variables in a function. |
time_it |
Print the runtime of the decorated function. |
timer |
Print the runtime of the decorated function. |
to_be_implemented |
Print a warning message that this function/method has to be implemented. |
transaction_command |
Adds a static variable |
write_command |
Adds a static variable |
Nothing
¶
Just to get a nice repr for Nothing. It is kind of a Null object...
Profiler
¶
A simple profiler class that provides some useful functions to profile a function.
- count: count the number of times this function is executed
- duration: measure the total and average duration of the function [seconds]
Examples:
>>> from egse.decorators import Profiler
>>> @Profiler.count()
... def square(x):
... return x**2
>>> x = [square(x) for x in range(1_000_000)]
>>> print(f"Function 'square' called {square.get_count()} times.")
>>> print(square)
>>> @Profiler.duration()
... def square(x):
... time.sleep(0.1)
... return x**2
>>> x = [square(x) for x in range(100)]
>>> print(f"Function 'square' takes on average {square.get_average_duration():.6f} seconds.")
>>> print(square)
classproperty
¶
classproperty(func)
Defines a read-only class property.
Examples:
>>> class Message:
... def __init__(self, msg):
... self._msg = msg
...
... @classproperty
... def name(cls):
... return cls.__name__
>>> msg = Message("a simple doctest")
>>> assert "Message" == msg.name
average_time
¶
average_time(
*, name="average_time", level=INFO, precision=6
)
This is a decorator that is intended mainly as a development aid. When you decorate your function with
@average_time
, the execution time of your function will be kept and accumulated. At anytime in your code,
you can request the total execution time and the number of calls:
@average_time()
def my_function():
...
total_execution_time, call_count = my_function.report()
Requesting the report will automatically log the average runtime and the number of calls. If you need to reset the execution time and the number of calls during your testing, use:
my_function.reset()
Parameters:
Name | Type | Description | Default |
---|---|---|---|
name
|
str
|
A name for the timer that will be used during reporting, default='average_time' |
'average_time'
|
level
|
int
|
the required log level, default=logging.INFO |
INFO
|
precision
|
int
|
the precision used to report the average time, default=6 |
6
|
Returns:
Type | Description |
---|---|
The decorated function. |
borg
¶
borg(cls)
Use the Borg pattern to make a class with a shared state between its instances and subclasses.
deprecate
¶
deprecate(reason=None, alternative=None)
Deprecate a function or method. This will print a warning with the function name and where
it is called from. If the optional parameters reason
and alternative
are given, that
information will be printed with the warning.
Examples:
@deprecate(reason="it doesn't follow PEP8", alternative="set_color()")
def setColor(self, color):
self.set_color(color)
Parameters:
Name | Type | Description | Default |
---|---|---|---|
reason
|
Optional[str]
|
provide a short explanation why this function is deprecated. Generates 'because {reason}' |
None
|
alternative
|
Optional[str]
|
provides an alternative function/parameters to be used. Generates 'Use {alternative} |
None
|
Returns:
Type | Description |
---|---|
Callable
|
The decorated function. |
dynamic_interface
¶
dynamic_interface(func)
Adds a static variable __dynamic_interface
to a method.
The intended use of this function is as a decorator for functions in an interface class.
The static variable is currently used by the Proxy class to check if a method is meant to be overridden dynamically. The idea behind this is to loosen the contract of an abstract base class (ABC) into an interface. For an ABC, the abstract methods must be implemented at construction/initialization. This is not possible for the Proxy subclasses as they load their commands (i.e. methods) from the control server, and the method will be added to the Proxy interface after loading. Nevertheless, we like the interface already defined for auto-completion during development or interactive use.
When a Proxy subclass that implements an interface with methods decorated by
the @dynamic_interface
does overwrite one or more of the decorated methods statically,
these methods will not be dynamically overwritten when loading the interface from the
control server. A warning will be logged instead.
profile
¶
profile(func)
Prints the function signature and return value to stdout.
This function checks the Settings.profiling()
value and only prints out
profiling information if this returns True.
Profiling can be activated with Settings.set_profiling(True)
.
profile_func
¶
profile_func(
output_file=None,
sort_by="cumulative",
lines_to_print=None,
strip_dirs=False,
)
A time profiler decorator.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
output_file
|
str or None. Default is None Path of the output file. If only name of the file is given, it's saved in the current directory. If it's None, the name of the decorated function is used. |
None
|
|
sort_by
|
str or SortKey enum or tuple/list of str/SortKey enum Sorting criteria for the Stats object. For a list of valid string and SortKey refer to: https://docs.python.org/3/library/profile.html#pstats.Stats.sort_stats |
'cumulative'
|
|
lines_to_print
|
int or None Number of lines to print. Default (None) is for all the lines. This is useful in reducing the size of the printout, especially that sorting by 'cumulative', the time consuming operations are printed toward the top of the file. |
None
|
|
strip_dirs
|
bool Whether to remove the leading path info from file names. This is also useful in reducing the size of the printout |
False
|
Returns:
Type | Description |
---|---|
Profile of the decorated function |
Note
This code was taken from this gist: a profile decorator.
Inspired by and modified the profile decorator of Giampaolo Rodola: profile decorato.
retry
¶
retry(times=3, wait=10.0, exceptions=None)
Decorator that retries a function multiple times with a delay between attempts.
This decorator can be applied to a function to handle specified exceptions by
retrying the function execution. It will make up to 'times' attempts with a
waiting period of 'wait' seconds between each attempt. Any exception from the
list provided in the exceptions
argument will be ignored for the given times
.
If after times attempts still an exception is raised, it will be passed through the calling function, otherwise the functions return value will be returned.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
times
|
int
|
The number of retry attempts. Defaults to 3. |
3
|
wait
|
float
|
The waiting period between retries in seconds. Defaults to 10.0. |
10.0
|
exceptions
|
List[Exception] or None
|
List of exception types to catch and retry. Defaults to None, which catches all exceptions. |
None
|
Returns:
Name | Type | Description |
---|---|---|
Callable |
The decorated function. |
Example
Apply the retry decorator to a function with specific retry settings:
@retry(times=5, wait=15.0, exceptions=[ConnectionError, TimeoutError])
def my_function():
# Function logic here
Note
The decorator catches specified exceptions and retries the function, logging information about each retry attempt.
retry_with_exponential_backoff
¶
retry_with_exponential_backoff(
max_attempts=5,
initial_wait=1.0,
backoff_factor=2,
exceptions=None,
)
Decorator for retrying a function with exponential backoff.
This decorator can be applied to a function to handle specified exceptions by
retrying the function execution. It will make up to 'max_attempts' attempts with a
waiting period that grows exponentially between each attempt (dependent on the backoff_factor).
Any exception from the list provided in the exceptions
argument will be ignored for the
given max_attempts
.
If after all attempts still an exception is raised, it will be passed through the calling function, otherwise the functions return value will be returned.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
max_attempts
|
The maximum number of attempts to make. |
5
|
|
initial_wait
|
The initial waiting time in seconds before retrying after the first failure. |
1.0
|
|
backoff_factor
|
The factor by which the wait time increases after each failure. |
2
|
Returns:
Type | Description |
---|---|
The response from the executed function. |
spy_on_attr_change
¶
spy_on_attr_change(obj, obj_name=None)
Tweak an object to show attributes changing. The changes are reported as WARNING log messages
in the egse.spy
logger.
Note this is not a decorator, but a function that changes the class of an object.
Note that this function is a debugging aid and should not be used in production code!
Parameters:
Name | Type | Description | Default |
---|---|---|---|
obj
|
object
|
any object that you want to monitor |
required |
obj_name
|
str
|
the variable name of the object that was given in the code, if None than the class name will be printed. |
None
|
Example
class X:
pass
x = X()
spy_on_attr_change(x, obj_name="x")
x.a = 5
static_vars
¶
static_vars(**kwargs)
Define static variables in a function.
The static variable can be accessed with
Example
@static_vars(count=0)
def special_count():
return special_count.count += 2
time_it
¶
time_it(count=1000, precision=4)
Print the runtime of the decorated function.
This is a simple replacement for the builtin timeit
function. The purpose is to simplify
calling a function with some parameters.
The intended way to call this is as a function:
value = function(args)
value = time_it(10_000)(function)(args)
The time_it
function can be called as a decorator in which case it will always call the
function count
times which is probably not what you want.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
count
|
int
|
the number of executions [default=1000]. |
1000
|
precision
|
int
|
the number of significant digits [default=4] |
4
|
Returns:
Name | Type | Description |
---|---|---|
value |
the return value of the last function execution. |
See also
the Timer
context manager located in egse.system
.
Usage
@time_it(count=10000)
def function(args):
pass
time_it(10000)(function)(args)
timer
¶
timer(*, name='timer', level=INFO, precision=4)
Print the runtime of the decorated function.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
name
|
str
|
a name for the Timer, will be printed in the logging message |
'timer'
|
level
|
int
|
the logging level for the time message [default=INFO] |
INFO
|
precision
|
int
|
the number of decimals for the time [default=3 (ms)] |
4
|
to_be_implemented
¶
to_be_implemented(func)
Print a warning message that this function/method has to be implemented.
transaction_command
¶
transaction_command(func)
Adds a static variable __transaction_command
to a method.