Module utils

Utilities.

New in version 0.5.0.

Overview

AlreadyRunning

Raised by ensure_single_instance().

StopWatch

Simple stop watch.

StopWatchError

Raised by StopWatch if an action is not allowed.

check_bytes_like

Check if an object is a bytes-like object.

check_path_like

Check if an object is a path-like object.

check_type

Check the type of an object.

docopt_helper

Helper function for docopt.

ensure_single_instance

Make sure that only one instance of the program/script is running.

sys_exit

Exit from Python.

exception salmagundi.utils.AlreadyRunning[source]

Raised by ensure_single_instance().

class salmagundi.utils.StopWatch(start=True)[source]

Simple stop watch.

Parameters

start (bool) – if True the stop watch starts immediately

New in version 0.12.0.

property started

Return whether the stop watch has been started.

property running

Return whether the stop watch is running.

start()[source]

Start or restart the stop watch.

Raises

StopWatchError – if the stop watch is running

pause()[source]

Pause the stop watch.

Does not reset the stop watch.

Returns

the elapsed time in seconds

Return type

float

Raises

StopWatchError – if the stop watch is not running

stop()[source]

Stop and reset the stop watch.

Returns

the elapsed time in seconds

Return type

float

Raises

StopWatchError – if the stop watch is not running

reset()[source]

Reset the stop watch.

Raises

StopWatchError – if the stop watch is running

time()[source]

Get elapsed time from the stop watch.

Returns

the currently elapsed time in seconds

Return type

float

Raises

StopWatchError – if the stop watch has not been started

exception salmagundi.utils.StopWatchError[source]

Raised by StopWatch if an action is not allowed.

New in version 0.12.0.

salmagundi.utils.check_bytes_like(obj, name='object', msg=None)[source]

Check if an object is a bytes-like object.

Parameters
  • obj (object) – the object

  • name (str) – name shown in the exception message

  • msg (str) – alternative message

Raises

TypeError – if the check fails

salmagundi.utils.check_path_like(obj, name='object', msg=None)[source]

Check if an object is a path-like object.

Parameters
  • obj (object) – the object

  • name (str) – name shown in the exception message

  • msg (str) – alternative message

Raises

TypeError – if the check fails

salmagundi.utils.check_type(obj, classinfo, name='object', msg=None)[source]

Check the type of an object.

>>> utils.check_type(1, str, 'num')
Traceback (most recent call last):
  ...
TypeError: the type of 'num' must be 'str', got 'int'
>>> utils.check_type(1, (str, float), 'num')
Traceback (most recent call last):
  ...
TypeError: the type of 'num' must be one of 'str, float', got 'int'
>>> utils.check_type(1, str, msg='wrong type for num')
Traceback (most recent call last):
  ...
TypeError: wrong type for num
Parameters
  • obj (object) – the object

  • classinfo – see isinstance()

  • name (str) – name shown in the exception message

  • msg (str) – message for the exception

Raises

TypeError – if the check fails

salmagundi.utils.docopt_helper(text, *, name=None, version=None, version_str=None, argv=None, help=True, options_first=False, converters=None, err_code=1, **kwargs)[source]

Helper function for docopt.

The name defaults to os.path.basename(sys.argv[0]).

If version is a tuple it will be converted to a string with '.'.join(map(str, version)).

If version_str is set it will be printed if called with --version. Else if version is set the resulting string will be name + ' ' + version.

Within the help message string substitution is supported with template strings. The placeholder identifiers name, version and version_str are always available; more can be added with kwargs.

If the help message is indented it will be dedented so that the least indented lines line up with the left edge of the display.

The optional argument converters is a mapping with the same keys as in the dictionary returned by docopt.docopt(). The values are callables which take one argument of an appropriate type and return a value of the desired type. It is not required to provide a converter for every option, argument and command. If a value cannot be converted the converter should raise a ValueError.

Example (naval_fate.py):

#!/usr/bin/env python3
"""$title Version $version.

Usage:
  $name ship new <name>...
  $name ship <name> move <x> <y> [--speed=<kn>]
  $name ship shoot <x> <y>
  $name mine (set|remove) <x> <y> [--moored | --drifting]
  $name (-h | --help)
  $name --version

Options:
  -h --help     Show this screen.
  --version     Show version.
  --speed=<kn>  Speed in knots [default: $speed].
  --moored      Moored (anchored) mine.
  --drifting    Drifting mine.

"""
import os

from salmagundi.utils import docopt_helper

__version__ = '2.0'


if __name__ == '__main__':
    def int_conv(x):
        if x is not None:
            x = int(x)
            if x < 0:
                raise ValueError('value < 0')
        return x

    arguments = docopt_helper(
        __doc__, version=__version__,
        converters={
            '<x>': int_conv,
            '<y>': int_conv,
            '--speed': int_conv,
        },
        title='Naval Fate',
        speed=os.environ.get('NAVAL_FATE_SPEED', '10'))
    print(arguments)
$ ./naval_fate.py -h
Naval Fate Version 2.0.

Usage:
  naval_fate.py ship new <name>...
  naval_fate.py ship <name> move <x> <y> [--speed=<kn>]
  naval_fate.py ship shoot <x> <y>
  naval_fate.py mine (set|remove) <x> <y> [--moored | --drifting]
  naval_fate.py (-h | --help)
  naval_fate.py --version

Options:
  -h --help     Show this screen.
  --version     Show version.
  --speed=<kn>  Speed in knots [default: 10].
  --moored      Moored (anchored) mine.
  --drifting    Drifting mine.

$ ./naval_fate.py --version
naval_fate.py 2.0

$ ./naval_fate.py ship Titanic move 42 84
{'--drifting': False,
 '--help': False,
 '--moored': False,
 '--speed': 10,
 '--version': False,
 '<name>': ['Titanic'],
 '<x>': 42,
 '<y>': 84,
 'mine': False,
 'move': True,
 'new': False,
 'remove': False,
 'set': False,
 'ship': True,
 'shoot': False}

$ ./naval_fate.py ship Titanic move 42 84 --speed=-10
error in '--speed': value < 0

$ ./naval_fate.py ship Titanic move 42 84 --speed=abc
error in '--speed': invalid literal for int() with base 10: 'abc'
Parameters
Returns

result of docopt.docopt()

Return type

dict

Raises

SystemExit – if program was invoked with incorrect arguments or a converter function raised a ValueError

New in version 0.10.0.

Changed in version 0.11.0: Add parameter err_code

salmagundi.utils.ensure_single_instance(lockname=None, *, lockdir=None, extra=None, err_code=1, err_msg=None, use_socket=False)[source]

Make sure that only one instance of the program/script is running.

The result of this function can be used as a context manager in with statements or as a decorator.

def main():
    ...

if __name__ == '__main__':
    with ensure_single_instance():
        main()

# is equivalent to:

@ensure_single_instance()
def main():
    ...

if __name__ == '__main__':
    main()

If lockname is not set the name will be constructed from the absolute path of the program/script and the lock file will be created in the lockdir (which defaults to the temporary directory).

On Linux ,if use_socket=True, an abstract domain socket will be used instead of a lock file and the name of the socket will be the value of lockname.

This function should work on Windows and any platform that supports fcntl but it is only tested on Linux. The user running the program/script must have the permissions to create and delete the lock file. If the program/script will be run by multiple users the single instance restriction can be per user or system wide. The temporary directory on Windows is normally user specific; on unix-like systems it is normally one directory for all users. To create a user specific lock name the extra argument can be used:

import getpass
from salmagundi.utils import ensure_single_instance

with ensure_single_instance(extra=getpass.getuser()):
    ...
Parameters
  • lockname (str) – user defined lock name

  • lockdir (path-like object) – user defined directory for lock files (must exist and the path must be absolute; ignored if use_socket=True)

  • extra (str) – will be appended to lock name

  • err_code (int or None) – exit status code if another instance is running (if set to None AlreadyRunning will be raised instead of SystemExit)

  • err_msg (str or None) – error message (if None it defaults to f'already running: {sys.argv[0]}')

  • use_socket (bool) – if true an abstract domain socket is used (Linux only)

Raises
  • SystemExit – if another instance is running and err_code is not None

  • AlreadyRunning – if another instance is running and err_code is None

  • RuntimeError – if lockdir is not absolute or use_socket=True and platform is not Linux

  • OSError – if the lock file could not be created/deleted

New in version 0.11.0.

Changed in version 0.11.2: Rename parameter lockfile to lockname

salmagundi.utils.sys_exit(arg=None, code=None, *, logger=None)[source]

Exit from Python.

If code is not an integer, this function calls sys.exit() with arg as its argument. Otherwise arg will be printed to sys.stderr if it is not None and sys.exit() will be called with code as its argument.

If logger is set, the message, if any, will be logged with level CRITICAL instead of printing it to sys.stderr.

Parameters
Raises

SystemExit

New in version 0.11.0.