1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
|
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/
Security contact information
------------------------------------------------------------------------------
To report a security vulnerability, please use the
`Tidelift security contact <https://tidelift.com/security>`_.
Tidelift will coordinate the fix and disclosure.
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):
.. code-block:: bash
pip install python-utils
Or if `pip` is not available, `easy_install` should work as well:
.. code-block:: bash
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:
.. code-block:: bash
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
------------------------------------------------------------------------------
Automatically converting a generator to a list, dict or other collections
using a decorator:
.. code-block:: pycon
>>> @decorators.listify()
... def generate_list():
... yield 1
... yield 2
... yield 3
...
>>> generate_list()
[1, 2, 3]
>>> @listify(collection=dict)
... def dict_generator():
... yield 'a', 1
... yield 'b', 2
>>> dict_generator()
{'a': 1, 'b': 2}
Retrying until timeout
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To easily retry a block of code with a configurable timeout, you can use the
`time.timeout_generator`:
.. code-block:: pycon
>>> for i in time.timeout_generator(10):
... try:
... # Run your code here
... except Exception as e:
... # Handle the exception
Formatting of timestamps, dates and times
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Easy formatting of timestamps and calculating the time since:
.. code-block:: pycon
>>> 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:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: pycon
>>> camel_to_underscore('SpamEggsAndBacon')
'spam_eggs_and_bacon'
Attribute setting decorator. Very useful for the Django admin
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A convenient decorator to set function attributes using a decorator:
.. code-block:: pycon
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'
This can be very useful for the Django admin as it allows you to have all
metadata in one place.
Scaling numbers between ranges
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: pycon
>>> 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')
Get the screen/window/terminal size in characters:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: pycon
>>> 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.
Extracting numbers from nearly every string:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: pycon
>>> converters.to_int('spam15eggs')
15
>>> converters.to_int('spam')
0
>>> number = converters.to_int('spam', default=1)
1
Doing a global import of all the modules in a package programmatically:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To do a global import programmatically you can use the `import_global`
function. This effectively emulates a `from ... import *`
.. code-block:: python
from python_utils.import_ import import_global
# The following is the equivalent of `from some_module import *`
import_global('some_module')
Automatically named logger for classes:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Or add a correclty named logger to your classes which can be easily accessed:
.. code-block:: python
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')
Alternatively loguru is also supported. It is largely a drop-in replacement for the logging module which is a bit more convenient to configure:
First install the extra loguru package:
.. code-block:: bash
pip install 'python-utils[loguru]'
.. code-block:: python
class MyClass(Logurud):
...
Now you can use the `Logurud` class to make functions such as `self.info()`
available. The benefit of this approach is that you can add extra context or
options to you specific loguru instance (i.e. `self.logger`):
Convenient type aliases and some commonly used types:
.. code-block:: python
# For type hinting scopes such as locals/globals/vars
Scope = Dict[str, Any]
OptionalScope = O[Scope]
# Note that Number is only useful for extra clarity since float
# will work for both int and float in practice.
Number = U[int, float]
DecimalNumber = U[Number, decimal.Decimal]
# To accept an exception or list of exceptions
ExceptionType = Type[Exception]
ExceptionsType = U[Tuple[ExceptionType, ...], ExceptionType]
# Matching string/bytes types:
StringTypes = U[str, bytes]
|