310 lines
9.6 KiB
Plaintext
310 lines
9.6 KiB
Plaintext
Metadata-Version: 2.1
|
|
Name: python-utils
|
|
Version: 3.9.1
|
|
Summary: Python Utils is a module with some convenient utilities not included with the standard Python install
|
|
Home-page: https://github.com/WoLpH/python-utils
|
|
Author: Rick van Hattem
|
|
Author-email: Wolph@wol.ph
|
|
License: BSD
|
|
Classifier: License :: OSI Approved :: BSD License
|
|
Requires-Python: >=3.9.0
|
|
License-File: LICENSE
|
|
Requires-Dist: typing_extensions>3.10.0.2
|
|
Provides-Extra: loguru
|
|
Requires-Dist: loguru; extra == "loguru"
|
|
Provides-Extra: docs
|
|
Requires-Dist: mock; extra == "docs"
|
|
Requires-Dist: sphinx; extra == "docs"
|
|
Requires-Dist: python-utils; extra == "docs"
|
|
Provides-Extra: tests
|
|
Requires-Dist: ruff; extra == "tests"
|
|
Requires-Dist: pyright; extra == "tests"
|
|
Requires-Dist: pytest; extra == "tests"
|
|
Requires-Dist: pytest-cov; extra == "tests"
|
|
Requires-Dist: pytest-mypy; extra == "tests"
|
|
Requires-Dist: pytest-asyncio; extra == "tests"
|
|
Requires-Dist: sphinx; extra == "tests"
|
|
Requires-Dist: types-setuptools; extra == "tests"
|
|
Requires-Dist: loguru; extra == "tests"
|
|
Requires-Dist: loguru-mypy; extra == "tests"
|
|
Requires-Dist: mypy-ipython; extra == "tests"
|
|
Requires-Dist: blessings; extra == "tests"
|
|
|
|
Useful Python Utils
|
|
==============================================================================
|
|
|
|
.. image:: https://github.com/WoLpH/python-utils/actions/workflows/main.yml/badge.svg?branch=master
|
|
:target: https://github.com/WoLpH/python-utils/actions/workflows/main.yml
|
|
|
|
.. image:: https://coveralls.io/repos/WoLpH/python-utils/badge.svg?branch=master
|
|
:target: https://coveralls.io/r/WoLpH/python-utils?branch=master
|
|
|
|
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]
|