Welcome to Python Utils’s documentation!¶
Contents:
Useful Python Utils¶
Python Utils is a collection of small Python functions and classes which make common patterns shorter and easier. It is by no means a complete collection but it has served me quite a bit in the past and I will keep extending it.
One of the libraries using Python Utils is Django Utils.
Documentation is available at: https://python-utils.readthedocs.org/en/latest/
Links¶
- The source: https://github.com/WoLpH/python-utils
- Project page: https://pypi.python.org/pypi/python-utils
- Reporting bugs: https://github.com/WoLpH/python-utils/issues
- Documentation: https://python-utils.readthedocs.io/en/latest/
- My blog: https://wol.ph/
Requirements for installing:¶
For the Python 3+ release (i.e. v3.0.0 or higher) there are no requirements. For the Python 2 compatible version (v2.x.x) the six package is needed.
Installation:¶
The package can be installed through pip (this is the recommended method):
pip install python-utils
Or if pip is not available, easy_install should work as well:
easy_install python-utils
Or download the latest release from Pypi (https://pypi.python.org/pypi/python-utils) or Github.
Note that the releases on Pypi are signed with my GPG key (https://pgp.mit.edu/pks/lookup?op=vindex&search=0xE81444E9CE1F695D) and can be checked using GPG:
gpg –verify python-utils-<version>.tar.gz.asc python-utils-<version>.tar.gz
Quickstart¶
This module makes it easy to execute common tasks in Python scripts such as converting text to numbers and making sure a string is in unicode or bytes format.
Examples¶
To easily retry a block of code with a configurable timeout, you can use the time.timeout_generator:
>>> for i in time.timeout_generator(10):
... try:
... # Run your code here
... except Exception as e:
... # Handle the exception
Easy formatting of timestamps and calculating the time since:
>>> time.format_time('1')
'0:00:01'
>>> time.format_time(1.234)
'0:00:01'
>>> time.format_time(1)
'0:00:01'
>>> time.format_time(datetime.datetime(2000, 1, 2, 3, 4, 5, 6))
'2000-01-02 03:04:05'
>>> time.format_time(datetime.date(2000, 1, 2))
'2000-01-02'
>>> time.format_time(datetime.timedelta(seconds=3661))
'1:01:01'
>>> time.format_time(None)
'--:--:--'
>>> formatters.timesince(now)
'just now'
>>> formatters.timesince(now - datetime.timedelta(seconds=1))
'1 second ago'
>>> formatters.timesince(now - datetime.timedelta(seconds=2))
'2 seconds ago'
>>> formatters.timesince(now - datetime.timedelta(seconds=60))
'1 minute ago'
Converting your test from camel-case to underscores:
>>> camel_to_underscore('SpamEggsAndBacon')
'spam_eggs_and_bacon'
A convenient decorator to set function attributes using a decorator:
You can use:
>>> @decorators.set_attributes(short_description='Name')
... def upper_case_name(self, obj):
... return ("%s %s" % (obj.first_name, obj.last_name)).upper()
Instead of:
>>> def upper_case_name(obj):
... return ("%s %s" % (obj.first_name, obj.last_name)).upper()
>>> upper_case_name.short_description = 'Name'
Or to scale numbers:
>>> converters.remap(500, old_min=0, old_max=1000, new_min=0, new_max=100)
50
# Or with decimals:
>>> remap(decimal.Decimal('250.0'), 0.0, 1000.0, 0.0, 100.0)
Decimal('25.0')
To get the screen/window/terminal size in characters:
>>> terminal.get_terminal_size()
(80, 24)
That method supports IPython and Jupyter as well as regular shells, using blessings and other modules depending on what is available.
To extract a number from nearly every string:
>>> converters.to_int('spam15eggs')
15
>>> converters.to_int('spam')
0
>>> number = converters.to_int('spam', default=1)
1
To do a global import programmatically you can use the import_global function. This effectively emulates a from … import *
from python_utils.import_ import import_global
# The following is the equivalent of `from some_module import *`
import_global('some_module')
Or add a correclty named logger to your classes which can be easily accessed:
class MyClass(Logged):
def __init__(self):
Logged.__init__(self)
my_class = MyClass()
# Accessing the logging method:
my_class.error('error')
# With formatting:
my_class.error('The logger supports %(formatting)s',
formatting='named parameters')
# Or to access the actual log function (overwriting the log formatting can
# be done n the log method)
import logging
my_class.log(logging.ERROR, 'log')
python_utils package¶
Submodules¶
python_utils.decorators module¶
-
python_utils.decorators.
set_attributes
(**kwargs)[source]¶ Decorator to set attributes on functions and classes
A common usage for this pattern is the Django Admin where functions can get an optional short_description. To illustrate:
Example from the Django admin using this decorator: https://docs.djangoproject.com/en/3.0/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_display
Our simplified version:
>>> @set_attributes(short_description='Name') ... def upper_case_name(self, obj): ... return ("%s %s" % (obj.first_name, obj.last_name)).upper()
The standard Django version:
>>> def upper_case_name(obj): ... return ("%s %s" % (obj.first_name, obj.last_name)).upper()
>>> upper_case_name.short_description = 'Name'
python_utils.converters module¶
-
python_utils.converters.
remap
(value, old_min, old_max, new_min, new_max)[source]¶ remap a value from one range into another.
>>> remap(500, 0, 1000, 0, 100) 50 >>> remap(250.0, 0.0, 1000.0, 0.0, 100.0) 25.0 >>> remap(-75, -100, 0, -1000, 0) -750 >>> remap(33, 0, 100, -500, 500) -170 >>> remap(decimal.Decimal('250.0'), 0.0, 1000.0, 0.0, 100.0) Decimal('25.0')
This is a great use case example. Take an AVR that has dB values the minimum being -80dB and the maximum being 10dB and you want to convert volume percent to the equilivint in that dB range
>>> remap(46.0, 0.0, 100.0, -80.0, 10.0) -38.6
I added using decimal.Decimal so floating point math errors can be avoided. Here is an example of a floating point math error >>> 0.1 + 0.1 + 0.1 0.30000000000000004
If floating point remaps need to be done my suggstion is to pass at least one parameter as a decimal.Decimal. This will ensure that the output from this function is accurate. I left passing floats for backwards compatability and there is no conversion done from float to decimal.Decimal unless one of the passed parameters has a type of decimal.Decimal. This will ensure that any existing code that uses this funtion will work exactly how it has in the past.
Some edge cases to test >>> remap(1, 0, 0, 1, 2) Traceback (most recent call last): … ValueError: Input range (0-0) is empty
>>> remap(1, 1, 2, 0, 0) Traceback (most recent call last): ... ValueError: Output range (0-0) is empty
Parameters: - value (int, float, decimal.Decimal) – value to be converted
- old_min (int, float, decimal.Decimal) – minimum of the range for the value that has been passed
- old_max (int, float, decimal.Decimal) – maximum of the range for the value that has been passed
- new_min (int, float, decimal.Decimal) – the minimum of the new range
- new_max (int, float, decimal.Decimal) – the maximum of the new range
Returns: value that has been re ranged. if any of the parameters passed is a decimal.Decimal all of the parameters will be converted to decimal.Decimal. The same thing also happens if one of the parameters is a float. otherwise all parameters will get converted into an int. technically you can pass a str of an integer and it will get converted. The returned value type will be decimal.Decimal of any of the passed parameters ar decimal.Decimal, the return type will be float if any of the passed parameters are a float otherwise the returned type will be int.
Return type:
-
python_utils.converters.
scale_1024
(x, n_prefixes)[source]¶ Scale a number down to a suitable size, based on powers of 1024.
Returns the scaled number and the power of 1024 used.
Use to format numbers of bytes to KiB, MiB, etc.
>>> scale_1024(310, 3) (310.0, 0) >>> scale_1024(2048, 3) (2.0, 1) >>> scale_1024(0, 2) (0.0, 0) >>> scale_1024(0.5, 2) (0.5, 0) >>> scale_1024(1, 2) (1.0, 0)
-
python_utils.converters.
to_float
(input_, default=0, exception=(<type 'exceptions.ValueError'>, <type 'exceptions.TypeError'>), regexp=None)[source]¶ Convert the given input_ to an integer or return default
When trying to convert the exceptions given in the exception parameter are automatically catched and the default will be returned.
The regexp parameter allows for a regular expression to find the digits in a string. When True it will automatically match any digit in the string. When a (regexp) object (has a search method) is given, that will be used. WHen a string is given, re.compile will be run over it first
The last group of the regexp will be used as value
>>> '%.2f' % to_float('abc') '0.00' >>> '%.2f' % to_float('1') '1.00' >>> '%.2f' % to_float('abc123.456', regexp=True) '123.46' >>> '%.2f' % to_float('abc123', regexp=True) '123.00' >>> '%.2f' % to_float('abc0.456', regexp=True) '0.46' >>> '%.2f' % to_float('abc123.456', regexp=re.compile(r'(\d+\.\d+)')) '123.46' >>> '%.2f' % to_float('123.456abc', regexp=re.compile(r'(\d+\.\d+)')) '123.46' >>> '%.2f' % to_float('abc123.46abc', regexp=re.compile(r'(\d+\.\d+)')) '123.46' >>> '%.2f' % to_float('abc123abc456', regexp=re.compile(r'(\d+(\.\d+|))')) '123.00' >>> '%.2f' % to_float('abc', regexp=r'(\d+)') '0.00' >>> '%.2f' % to_float('abc123', regexp=r'(\d+)') '123.00' >>> '%.2f' % to_float('123abc', regexp=r'(\d+)') '123.00' >>> '%.2f' % to_float('abc123abc', regexp=r'(\d+)') '123.00' >>> '%.2f' % to_float('abc123abc456', regexp=r'(\d+)') '123.00' >>> '%.2f' % to_float('1234', default=1) '1234.00' >>> '%.2f' % to_float('abc', default=1) '1.00' >>> '%.2f' % to_float('abc', regexp=123) Traceback (most recent call last): ... TypeError: unknown argument for regexp parameter
-
python_utils.converters.
to_int
(input_, default=0, exception=(<type 'exceptions.ValueError'>, <type 'exceptions.TypeError'>), regexp=None)[source]¶ Convert the given input to an integer or return default
When trying to convert the exceptions given in the exception parameter are automatically catched and the default will be returned.
The regexp parameter allows for a regular expression to find the digits in a string. When True it will automatically match any digit in the string. When a (regexp) object (has a search method) is given, that will be used. WHen a string is given, re.compile will be run over it first
The last group of the regexp will be used as value
>>> to_int('abc') 0 >>> to_int('1') 1 >>> to_int('abc123') 0 >>> to_int('123abc') 0 >>> to_int('abc123', regexp=True) 123 >>> to_int('123abc', regexp=True) 123 >>> to_int('abc123abc', regexp=True) 123 >>> to_int('abc123abc456', regexp=True) 123 >>> to_int('abc123', regexp=re.compile(r'(\d+)')) 123 >>> to_int('123abc', regexp=re.compile(r'(\d+)')) 123 >>> to_int('abc123abc', regexp=re.compile(r'(\d+)')) 123 >>> to_int('abc123abc456', regexp=re.compile(r'(\d+)')) 123 >>> to_int('abc123', regexp=r'(\d+)') 123 >>> to_int('123abc', regexp=r'(\d+)') 123 >>> to_int('abc', regexp=r'(\d+)') 0 >>> to_int('abc123abc', regexp=r'(\d+)') 123 >>> to_int('abc123abc456', regexp=r'(\d+)') 123 >>> to_int('1234', default=1) 1234 >>> to_int('abc', default=1) 1 >>> to_int('abc', regexp=123) Traceback (most recent call last): ... TypeError: unknown argument for regexp parameter: 123
-
python_utils.converters.
to_str
(input_, encoding=u'utf-8', errors=u'replace')[source]¶ Convert objects to string, encodes to the given encoding
Return type: str >>> to_str('a') b'a' >>> to_str(u'a') b'a' >>> to_str(b'a') b'a' >>> class Foo(object): __str__ = lambda s: u'a' >>> to_str(Foo()) 'a' >>> to_str(Foo) "<class 'python_utils.converters.Foo'>"
-
python_utils.converters.
to_unicode
(input_, encoding=u'utf-8', errors=u'replace')[source]¶ Convert objects to unicode, if needed decodes string with the given encoding and errors settings.
Return type: unicode >>> to_unicode(b'a') 'a' >>> to_unicode('a') 'a' >>> to_unicode(u'a') 'a' >>> class Foo(object): __str__ = lambda s: u'a' >>> to_unicode(Foo()) 'a' >>> to_unicode(Foo) "<class 'python_utils.converters.Foo'>"
python_utils.formatters module¶
-
python_utils.formatters.
camel_to_underscore
(name)[source]¶ Convert camel case style naming to underscore style naming
If there are existing underscores they will be collapsed with the to-be-added underscores. Multiple consecutive capital letters will not be split except for the last one.
>>> camel_to_underscore('SpamEggsAndBacon') 'spam_eggs_and_bacon' >>> camel_to_underscore('Spam_and_bacon') 'spam_and_bacon' >>> camel_to_underscore('Spam_And_Bacon') 'spam_and_bacon' >>> camel_to_underscore('__SpamAndBacon__') '__spam_and_bacon__' >>> camel_to_underscore('__SpamANDBacon__') '__spam_and_bacon__'
-
python_utils.formatters.
timesince
(dt, default='just now')[source]¶ Returns string representing ‘time since’ e.g. 3 days ago, 5 hours ago etc.
>>> now = datetime.datetime.now() >>> timesince(now) 'just now' >>> timesince(now - datetime.timedelta(seconds=1)) '1 second ago' >>> timesince(now - datetime.timedelta(seconds=2)) '2 seconds ago' >>> timesince(now - datetime.timedelta(seconds=60)) '1 minute ago' >>> timesince(now - datetime.timedelta(seconds=61)) '1 minute and 1 second ago' >>> timesince(now - datetime.timedelta(seconds=62)) '1 minute and 2 seconds ago' >>> timesince(now - datetime.timedelta(seconds=120)) '2 minutes ago' >>> timesince(now - datetime.timedelta(seconds=121)) '2 minutes and 1 second ago' >>> timesince(now - datetime.timedelta(seconds=122)) '2 minutes and 2 seconds ago' >>> timesince(now - datetime.timedelta(seconds=3599)) '59 minutes and 59 seconds ago' >>> timesince(now - datetime.timedelta(seconds=3600)) '1 hour ago' >>> timesince(now - datetime.timedelta(seconds=3601)) '1 hour and 1 second ago' >>> timesince(now - datetime.timedelta(seconds=3602)) '1 hour and 2 seconds ago' >>> timesince(now - datetime.timedelta(seconds=3660)) '1 hour and 1 minute ago' >>> timesince(now - datetime.timedelta(seconds=3661)) '1 hour and 1 minute ago' >>> timesince(now - datetime.timedelta(seconds=3720)) '1 hour and 2 minutes ago' >>> timesince(now - datetime.timedelta(seconds=3721)) '1 hour and 2 minutes ago' >>> timesince(datetime.timedelta(seconds=3721)) '1 hour and 2 minutes ago'
python_utils.import_ module¶
-
python_utils.import_.
import_global
(name, modules=None, exceptions=<class 'python_utils.import_.DummyException'>, locals_=None, globals_=None, level=-1)[source]¶ Import the requested items into the global scope
WARNING! this method _will_ overwrite your global scope If you have a variable named “path” and you call import_global(‘sys’) it will be overwritten with sys.path
- Args:
- name (str): the name of the module to import, e.g. sys modules (str): the modules to import, use None for everything exception (Exception): the exception to catch, e.g. ImportError locals_: the locals() method (in case you need a different scope) globals_: the globals() method (in case you need a different scope) level (int): the level to import from, this can be used for relative imports
python_utils.logger module¶
-
class
python_utils.logger.
Logged
[source]¶ Bases:
object
Class which automatically adds a named logger to your class when interiting
Adds easy access to debug, info, warning, error, exception and log methods
>>> class MyClass(Logged): ... def __init__(self): ... Logged.__init__(self) >>> my_class = MyClass() >>> my_class.debug('debug') >>> my_class.info('info') >>> my_class.warning('warning') >>> my_class.error('error') >>> my_class.exception('exception') >>> my_class.log(0, 'log')
-
classmethod
debug
(msg, *args, **kwargs)¶ Log a message with severity ‘DEBUG’ on the root logger.
-
classmethod
error
(msg, *args, **kwargs)¶ Log a message with severity ‘ERROR’ on the root logger.
-
classmethod
exception
(msg, *args, **kwargs)¶ Log a message with severity ‘ERROR’ on the root logger, with exception information.
-
classmethod
info
(msg, *args, **kwargs)¶ Log a message with severity ‘INFO’ on the root logger.
-
classmethod
log
(lvl, msg, *args, **kwargs)¶ Log ‘msg % args’ with the integer severity ‘level’ on the root logger.
-
classmethod
warning
(msg, *args, **kwargs)¶ Log a message with severity ‘WARNING’ on the root logger.
-
classmethod
python_utils.terminal module¶
-
python_utils.terminal.
get_terminal_size
()[source]¶ Get the current size of your terminal
Multiple returns are not always a good idea, but in this case it greatly simplifies the code so I believe it’s justified. It’s not the prettiest function but that’s never really possible with cross-platform code.
- Returns:
- width, height: Two integers containing width and height
python_utils.time module¶
-
python_utils.time.
format_time
(timestamp, precision=datetime.timedelta(0, 1))[source]¶ Formats timedelta/datetime/seconds
>>> format_time('1') '0:00:01' >>> format_time(1.234) '0:00:01' >>> format_time(1) '0:00:01' >>> format_time(datetime.datetime(2000, 1, 2, 3, 4, 5, 6)) '2000-01-02 03:04:05' >>> format_time(datetime.date(2000, 1, 2)) '2000-01-02' >>> format_time(datetime.timedelta(seconds=3661)) '1:01:01' >>> format_time(None) '--:--:--' >>> format_time(format_time) # doctest: +ELLIPSIS Traceback (most recent call last): ... TypeError: Unknown type ...
-
python_utils.time.
timedelta_to_seconds
(delta)[source]¶ Convert a timedelta to seconds with the microseconds as fraction
Note that this method has become largely obsolete with the timedelta.total_seconds() method introduced in Python 2.7.
>>> from datetime import timedelta >>> '%d' % timedelta_to_seconds(timedelta(days=1)) '86400' >>> '%d' % timedelta_to_seconds(timedelta(seconds=1)) '1' >>> '%.6f' % timedelta_to_seconds(timedelta(seconds=1, microseconds=1)) '1.000001' >>> '%.6f' % timedelta_to_seconds(timedelta(microseconds=1)) '0.000001'
-
python_utils.time.
timeout_generator
(timeout, interval=datetime.timedelta(0, 1), iterable=<type 'itertools.count'>, interval_multiplier=1.0)[source]¶ Generator that walks through the given iterable (a counter by default) until the timeout is reached with a configurable interval between items
>>> for i in timeout_generator(0.1, 0.06): ... print(i) 0 1 2 >>> timeout = datetime.timedelta(seconds=0.1) >>> interval = datetime.timedelta(seconds=0.06) >>> for i in timeout_generator(timeout, interval, itertools.count()): ... print(i) 0 1 2 >>> for i in timeout_generator(1, interval=0.1, iterable='ab'): ... print(i) a b
>>> timeout = datetime.timedelta(seconds=0.1) >>> interval = datetime.timedelta(seconds=0.06) >>> for i in timeout_generator(timeout, interval, interval_multiplier=2): ... print(i) 0 1
Module contents¶
Travis status:

Coverage:
