bubop package

Submodules

bubop.arg_parser module

Argparse-related utilities.

bubop.arg_parser.add_bool_argument(parser: ArgumentParser, arg_name: str, default: bool | None = None, true_help: str | None = None)

Add a boolean CLI argument to the given ArgumentParser object.

The flag defaults to False if no other is specified

Usage:

>>> import argparse
>>> parser = argparse.ArgumentParser(description="SomeArgumentParser")
>>> add_bool_argument(parser, "someflag")
>>> add_bool_argument(parser, "someflag2", default=True)
>>> add_bool_argument(parser, "someflag3", default=False)
>>> add_bool_argument(parser, "someflag4", true_help="somehelp4")
>>> add_bool_argument(parser, "a_flag")
>>> add_bool_argument(parser, "b-flag")
>>> config = parser.parse_args(["--someflag"]) # basic
>>> config.someflag
True
>>> config = parser.parse_args(["--no-someflag"])
>>> config.someflag
False

>>> config = vars(parser.parse_args([])) # defaults
>>> config["someflag"] == False
True
>>> config["someflag2"] == True
True
>>> config["someflag3"] == False
True
>>> config["someflag4"] == False
True
>>> "somehelp4" in parser.format_help()
True
>>> parser.parse_args(["--someflag", "--no-someflag"]) # exception
Traceback (most recent call last):
...
SystemExit: 2

>>> config = vars(parser.parse_args(["--a_flag"]))
>>> config["a_flag"] == True
True
>>> config = vars(parser.parse_args(["--b-flag"]))
>>> config["b-flag"] == True
True

bubop.classes module

Class and metaclass-related utilities.

bubop.classes.all_subclasses(cls: type[Any]) set[type[Any]]

Recursively get all the (reachable) subclasses of the given class.

Usage:

>>> class Foo: pass
>>> class Bar(Foo): pass
>>> class Baz(Foo): pass
>>> class Bing(Bar): pass
>>> sorted([c.__name__ for c in all_subclasses(Foo)])
['Bar', 'Baz', 'Bing']

bubop.cli module

CLI-related utilities.

bubop.cli.check_optional_mutually_exclusive(arg1: Any, arg2: Any) None

Check if the given mutually exclusive args indeed hold the said required rule (optional + mutual exclusivity)

Raise a CliIncompatibleOptionsError if they don’t uphold the rule.

>>> kalimera = 1
>>> kalinuxta = 2
>>> check_optional_mutually_exclusive(kalimera, kalinuxta)
Traceback (most recent call last):
bubop.exceptions.CliIncompatibleOptionsError: ... kalimera ... kalinuxta ...
>>> kalimera = None
>>> check_optional_mutually_exclusive(kalimera, kalinuxta) == None
True
>>> kalinuxta = None
>>> check_optional_mutually_exclusive(kalimera, kalinuxta) == None
True
bubop.cli.check_required_mutually_exclusive(arg1: Any, arg2: Any, arg1_name: str, arg2_name: str) None

Check if the given mutually exclusive args indeed hold the said required rule (required + mutual exclusivity)

Raise exception if they don’t uphold the rule.

bubop.common_dir module

Home of the CommonDir class.

class bubop.common_dir.CommonDir

Bases: object

Get the path to one of the standard user directories - depending on the OS at hand.

static cache(*args, **kargs)
static config(*args, **kargs)
static share(*args, **kargs)

bubop.crypto module

Cryptography-related utilities.

bubop.crypto.read_gpg_token(p: Path, timeout_secs: int = 3) str

Read the token from a gpg file.

Raise a RuntimeError if the decryption was unsuccessful.

bubop.crypto.write_gpg_token(p: Path, token: str, recipient: str) None

Write the given token to a gpg file designated by p.

Raise a RuntimeError if the encryption was unsuccessful.

bubop.exceptions module

Custom Exceptions.

exception bubop.exceptions.ApplicationNotInstalled(appname: str)

Bases: BaseException

Exception raised when a required application is not installed on the system.

exception bubop.exceptions.AtLeastNOptionsRequired(*args, **kargs)

Bases: NOptionsRequired

AtLeastNOptionsRequired exception.

Usage:

>>> raise AtLeastNOptionsRequired(num_required=2, foo="bar", baz="bing")
Traceback (most recent call last):
bubop.exceptions.AtLeastNOptionsRequired: At least 2 of the following arguments are required:
exception bubop.exceptions.AuthenticationError(appname: str)

Bases: BaseException

Exception raised when authentication with a certain application/service failed

exception bubop.exceptions.CliIncompatibleOptionsError(opt1: Any, opt2: Any)

Bases: BaseException

Exception raised when incompatible options are given in the CLI of a program.

Usage:

>>> raise CliIncompatibleOptionsError("foo", "bar")
Traceback (most recent call last):
bubop.exceptions.CliIncompatibleOptionsError: Provided option groups foo and bar are incompatible with each other
exception bubop.exceptions.Exactly1OptionRequired(*args, **kargs)

Bases: ExactlyNOptionsRequired

Exactly1OptionRequired exception.

Usage:

>>> raise Exactly1OptionRequired(num_given=0, foo="bar", baz="bing")
Traceback (most recent call last):
bubop.exceptions.Exactly1OptionRequired: Exactly 1 of the following arguments are required:
...
exception bubop.exceptions.ExactlyNOptionsRequired(*args, **kargs)

Bases: NOptionsRequired

ExactlyNOptionsRequired exception.

Usage:

>>> raise ExactlyNOptionsRequired(num_required=2, foo="bar", baz="bing")
Traceback (most recent call last):
bubop.exceptions.ExactlyNOptionsRequired: Exactly 2 of the following arguments are required:
...
exception bubop.exceptions.NOptionsRequired(prefix: str, num_required: int, *args, num_given: int | None = None, **kargs)

Bases: BaseException

Exception raised when at least N of the given options were required

exception bubop.exceptions.NoSuchFileOrDirectoryError(name)

Bases: BaseException

Exception raised when file/directory is not found.

Usage:

>>> raise NoSuchFileOrDirectoryError("foo")
Traceback (most recent call last):
bubop.exceptions.NoSuchFileOrDirectoryError: No such file or directory -> foo
exception bubop.exceptions.NotEnoughArgumentsError

Bases: BaseException

Exception raised when incompatible options are given in the CLI of a program.

Usage:

>>> raise NotEnoughArgumentsError()
Traceback (most recent call last):
bubop.exceptions.NotEnoughArgumentsError: ...
exception bubop.exceptions.OperatingSystemNotSupportedError(os_name: str)

Bases: BaseException

Exception raised when an operation is not supported for the OS at hand.

Usage:

>>> raise OperatingSystemNotSupportedError("Windows")
Traceback (most recent call last):
bubop.exceptions.OperatingSystemNotSupportedError: Operation is not supported for this OS -> Windows
exception bubop.exceptions.TooShallowStackError

Bases: BaseException

Exception raised when the stack trace does not have as many frames as expected.

bubop.exit_hooks module

Exit hooks class.

class bubop.exit_hooks.ExitHooks(print_fn=<built-in function print>)

Bases: object

Monkeypatch sys.exit + set the excepthook to figure out when an exception or an exit event was raised.

Set this during program execution and then, e.g., during program teardown, e.g., using atexit, figure out whether an exception was raised or whether the program exited successfully or failed.

exc_handler(exc_type, exc_value, tb, *args)

Exception handler override.

exit(code=0)

Exit function override.

register()

Register the exit hook.

bubop.fs module

Filesystem-related utilities.

class bubop.fs.FileType(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)

Bases: Enum

Enum to represent an entity on a filesystem and abstract operations on them.

>>> ft = FileType.FILE
>>> ft.exists(Path("/etc/passwd"))
True
>>> ft.exists(Path("/etc/"))
False
>>> ft = FileType.DIR
>>> ft.exists(Path("/etc/passwd"))
False
>>> ft.exists(Path("/etc/"))
True
DIR = 2
FILE = 1
FILE_OR_DIR = 3
exists(path: Path) bool

True if the give npath exists. Uses the appropriate function for the Filepath at hand.

bubop.fs.get_file_unique_id(p: Path) str

Get a unique identifier for the filesystem entity at hand.

Use a combination of device ID and inode.

bubop.fs.get_valid_filename(s: str) str

Return a filename-compatible version of the given string s.

Parameters:

s – String to be used as the base of the filename. You may also pass non-string objects that will however be able to convert to strings via the str operator.

>>> get_valid_filename(r"5678^()^")
'5678____'
>>> get_valid_filename(r"a|string\go/es||here")
'a_string_go_es__here'
>>> get_valid_filename(r"strin***g")
'strin___g'
bubop.fs.valid_dir(s: str) Path

Return a pathlib.Path from the given string.

If the input does not correspond to a valid directory, then raise an exception

>>> valid_dir("/etc/")
PosixPath...
>>> valid_dir("/etc/passwd")
Traceback (most recent call last):
NotADirectoryError: ...
bubop.fs.valid_file(s: str) Path

Return a pathlib.Path from the given string.

If the input does not correspond to a valid directory, then raise an exception >>> valid_file(“/etc/”) Traceback (most recent call last): FileNotFoundError: … >>> valid_file(“/etc/passwd”) PosixPath…

bubop.fs.valid_path(s: str, filetype=FileType.FILE_OR_DIR) Path

Return a pathlib.Path from the given string.

If the input does not correspond to a valid path, then raise an exception

>>> valid_path("/etc")
PosixPath...
>>> valid_path("/etc/some-invalid-path")
Traceback (most recent call last):
bubop.exceptions.NoSuchFileOrDirectoryError: No such ...
>>> valid_path("/etc", filetype=FileType.FILE)
Traceback (most recent call last):
FileNotFoundError: ...
>>> valid_path("/etc/passwd", filetype=FileType.FILE)
PosixPath...
>>> valid_path("/etc/passwd", filetype=FileType.DIR)
Traceback (most recent call last):
NotADirectoryError: ...
>>> valid_path("/etc", filetype=FileType.DIR)
PosixPath...

bubop.inspect module

Runtime-related utilities - making use of the inspect module.

bubop.inspect.inspect_var_name(var: Any, level=2) str | None

Retrieve the name of the variable var passed from the caller to this function.

Use the level argument to refer to the argument name of the caller’s caller (level=2) or the caller’s caller’s caller, (level=3) etc. instead.

Quite experimental function in nature, may not work as expected.

Usage:

>>> var1 = "kalimera"
>>> inspect_var_name(var1, level=1)
'var1'
>>> fn = lambda x: (inspect_var_name(x, level=1), inspect_var_name(x))
>>> fn(var1)
('x', 'var1')
>>> inspect_var_name(var1, level=1000) == None
Traceback (most recent call last):
bubop.exceptions.TooShallowStackError: Stack has less ...
>>> inspect_var_name(1, level=1) == None
True

bubop.logging module

Logging-oriented utilities.

bubop.logging.log_to_syslog(name: str, level: Literal['FATAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG', 'TRACE'] = 'WARNING')

Enable logging to syslog for the given logger.

This method does not remove any of the existing logging handlers.

bubop.logging.loguru_set_verbosity(verbosity: int)

Set the verbosity of the tqdm logger.

Parameters:

verbosity – 0 for >= INFO, 1 for >= DEBUG, 2 for >= TRACE

bubop.logging.loguru_tqdm_sink(verbosity: int)

Change the default loguru logger to use tqdm.

Parameters:

verbosity – Set the verbosity of the tqdm logger. 0 for >= INFO, 1 for >= DEBUG, 2 for >= TRACE

bubop.logging.verbosity_int_to_std_logging_lvl(verbosity: int) int

Map an integer to a corresponding python std logging module level :param verbosity: Set the verbosity of the tqdm logger.

0 for >= INFO, 1 for >= DEBUG,

>>> verbosity_int_to_std_logging_lvl(0)
20
>>> verbosity_int_to_std_logging_lvl(1)
10
>>> verbosity_int_to_std_logging_lvl(2)
10
>>> verbosity_int_to_std_logging_lvl(3)
10
>>> verbosity_int_to_std_logging_lvl(-1)
Traceback (most recent call last):
RuntimeError: ...
bubop.logging.verbosity_int_to_str(verbosity: int) Literal['FATAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG', 'TRACE']

Map a verbosity integer to the corresponding Loguru Level. :param verbosity: = 0 -> INFO,

= 1 -> DEBUG, >=2 -> TRACE

Usage:

>>> verbosity_int_to_str(1)
'DEBUG'
>>> verbosity_int_to_str(2)
'TRACE'
>>> verbosity_int_to_str(3)
'TRACE'
>>> verbosity_int_to_str(-1)
Traceback (most recent call last):
RuntimeError: ...

bubop.misc module

Miscellaneous utilities - not tied to a more specific module right now.

bubop.misc.get_object_unique_name(obj: Any) str

Return a unique string associated with the given object.

That string is constructed as follows: <object class name>_<object_hex_id>

bubop.misc.xor(*args) bool

True if exactly one of the arguments of the iterable is True.

>>> xor(0,1,0,)
True
>>> xor(1,2,3,)
False
>>> xor(False, False, False)
False
>>> xor("kalimera", "kalinuxta")
False
>>> xor("", "a", "")
True
>>> xor("", "", "")
False

bubop.prefs_manager module

Home of the PrefsManager class.

class bubop.prefs_manager.PrefsManager(app_name: str, config_fname: str = 'cfg.yaml', logger=<loguru.logger handlers=[(id=0, level=10, sink=<stderr>)]>)

Bases: object

Manage application-related preferences.

All the preferences of the app are stored in a key-value store (aka dict). You can either access this dict via the .contents property or the PrefsManager instance itself using the same methods as a standard dict (key in prefs_manager or prefs_manager["key"]) or using them as attributes: (prefs_manager.key)

property config_directory: Path

Get the path to the top-level config directory of the preferences at hand.

property config_file: Path

Get the path to the config file of the preferences at hand.

empty() bool

Returns whether the current preferences store is empty.

flush_config(p: Path)

Helper class for writing the current cached settings to a file.

Parameters:

p – Path to write the current settings. The given file is to be overwritten

items()

Return the items in the Preferences Manager - similar to a dict.

keys() Sequence[Any]

Return the list of keys in the Preferences Manager

update_latest(new_val)

Update the latest fetched setting.

values()

Return the list of values in the Preferences Manager

bubop.serial module

Serialization utilities.

bubop.serial.pickle_dump(item: Any, path: Path | str, *, protocol=0, **kargs)

Helper method to serialize your dictionary to the given path.

By default use protocol 0.

bubop.serial.pickle_load(path: Path | str) Any

Pickle-load using the given path.

bubop.string module

String-related utilities.

bubop.string.camel_case_to_dashed(s: str) str

Convert a CamelCase string into the dashed representation.

>>> camel_case_to_dashed("KalimeraKalinuxta")
'kalimera-kalinuxta'
>>> camel_case_to_dashed("somethingIsRotten")
'something-is-rotten'
>>> camel_case_to_dashed("SLAM")
'slam'
bubop.string.format_dict(items: Mapping[Any, Any], align_items: bool = True, **kargs) str

Utility for formatting a dictionary - similar to print.pformat.

Accepts mostly the same arguments as format_list.

bubop.string.format_list(items: Sequence[str], header: str, indent=2, bullet_char='-', header_sep='=', prefix: str = '', suffix: str = '') str

Format and return a string with the corresponding header and all the items each occupying a single line and with the specified indentation.

bubop.string.get_random_string(len_=10) str

Return a random string containing ascii characters, digits and punctuation marks.

bubop.string.non_empty(title: str, value: str, join_with: str = ' -> ', newline=True) str

Return a one-line formatted string of “title -> value” but only if value is a non-empty string. Otherwise return an empty string

>>> non_empty(title="title", value="value")
'title -> value\n'
>>> non_empty(title="title", value=None)
''

bubop.test module

bubop.time module

Time-related utilities.

class bubop.time.TimePeriod(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)

Bases: Enum

Holds different units of measuring time.

Day = 4
Hour = 3
Microsecond = 0
Minute = 2
Month = 5
Second = 1
Year = 6
higher_or_equal_to_this() list[TimePeriod]

Return all the periods that are higher or equal than the current one.

Usage:

>>> [p.name for p in TimePeriod.Month.higher_or_equal_to_this()]
['Year', 'Month']
higher_to_this() list[TimePeriod]

Return all the periods that are bigger than the current one.

Usage:

>>> [p.name for p in TimePeriod.Year.higher_to_this()]
[]
>>> [p.name for p in TimePeriod.Month.higher_to_this()]
['Year']
>>> [p.name for p in TimePeriod.Day.higher_to_this()]
['Month', 'Year']
lower_or_equal_to_this() list[TimePeriod]

Return all the periods that are smaller or equal than the current one.

Usage:

>>> [p.name for p in TimePeriod.Microsecond.lower_or_equal_to_this()]
['Microsecond']
lower_to_this() list[TimePeriod]

Return all the periods that are smaller than the current one.

Usage:

>>> [p.name for p in TimePeriod.Microsecond.lower_to_this()]
[]
>>> [p.name for p in TimePeriod.Minute.lower_to_this()]
['Microsecond', 'Second']
>>> [p.name for p in TimePeriod.Day.lower_to_this()]
['Microsecond', 'Second', 'Minute', 'Hour']
bubop.time.assume_local_tz_if_none(dt: datetime) datetime

If there is no timezone in the given datetime object, assign a local timezone.

In all cases return a new instance of the datetime object.

Usage:

>>> dt = datetime.datetime(2019, 3, 8, 0, 29, 6)
>>> dt.tzinfo is None
True
>>> dt = assume_local_tz_if_none(dt)
>>> dt.tzinfo is None
False

>>> dt_old = datetime.datetime.now(tz=dateutil.tz.tzlocal())
>>> dt_old.tzinfo is not None
True
>>> dt = assume_local_tz_if_none(dt_old)
>>> dt.tzinfo is None
False
bubop.time.format_datetime_tz(dt: datetime) str

Format a datetime object according to the ISO specifications containing the ‘T’ and ‘Z’ separators

Usage:

>>> format_datetime_tz(datetime.datetime(2019, 3, 5, 0, 3, 9, 1234))
'2019-03-05T00:03:09.001234Z'
>>> format_datetime_tz(datetime.datetime(2019, 3, 5, 0, 3, 9, 123))
'2019-03-05T00:03:09.000123Z'
bubop.time.get_datetime_up_to(period: TimePeriod, dt: datetime) datetime

Create a new date with only the parts up to the period specified. The rest are discarded.

Usage:

>>> dt = datetime.datetime(year=1, month=2, day=3, hour=4, minute=5, second=6, microsecond=7)
>>> get_datetime_up_to(period=TimePeriod.Year, dt=dt)
Traceback (most recent call last):
TypeError: Invalid TimePeriod...
>>> get_datetime_up_to(period=TimePeriod.Month, dt=dt)
Traceback (most recent call last):
TypeError: Invalid TimePeriod...
>>> get_datetime_up_to(period=TimePeriod.Day, dt=dt)
datetime.datetime(1, 2, 3, 0, 0)
>>> get_datetime_up_to(period=TimePeriod.Hour, dt=dt)
datetime.datetime(1, 2, 3, 4, 0)
>>> get_datetime_up_to(period=TimePeriod.Minute, dt=dt)
datetime.datetime(1, 2, 3, 4, 5)
>>> get_datetime_up_to(period=TimePeriod.Second, dt=dt)
datetime.datetime(1, 2, 3, 4, 5, 6)
>>> get_datetime_up_to(period=TimePeriod.Microsecond, dt=dt) == dt
True
bubop.time.is_same_datetime(dt1: datetime, dt2: datetime, tol: timedelta = datetime.timedelta(0)) bool

Compare two datetime.datetime objects.

If the timezone is empty, assume local timezone.

Usage:

>>> dt = datetime.datetime
>>> td = datetime.timedelta
>>> dt1 = datetime.datetime.now()
>>> dt2 = dt1 + td(minutes=5)
>>> is_same_datetime(dt1, dt2)
False
>>> is_same_datetime(dt1, dt2, tol=td(minutes=2))
False
>>> is_same_datetime(dt1, dt2, tol=td(minutes=5))
True
bubop.time.parse_datetime(s: str) datetime

Parse a datetime from the given string.

Usage:

>>> parse_datetime("2019-03-05") == datetime.datetime(2019, 3, 5, 0, 0)
True
>>> is_same_datetime(parse_datetime("2019-03-05T00:03:09Z"), datetime.datetime(2019, 3, 5, 0, 3, 9, tzinfo=dateutil.tz.tzutc()))
True
>>> is_same_datetime(parse_datetime("2019-03-05T00:03:01.1234Z"), datetime.datetime(2019, 3, 5, 0, 3, 1, 123400, tzinfo=dateutil.tz.tzutc()))
True
>>> is_same_datetime(parse_datetime("2019-03-08T00:29:06.602Z"), datetime.datetime(2019, 3, 8, 0, 29, 6, 602000, tzinfo=dateutil.tz.tzutc()))
True

Module contents

Init.

exception bubop.AuthenticationError(appname: str)

Bases: BaseException

Exception raised when authentication with a certain application/service failed

exception bubop.CliIncompatibleOptionsError(opt1: Any, opt2: Any)

Bases: BaseException

Exception raised when incompatible options are given in the CLI of a program.

Usage:

>>> raise CliIncompatibleOptionsError("foo", "bar")
Traceback (most recent call last):
bubop.exceptions.CliIncompatibleOptionsError: Provided option groups foo and bar are incompatible with each other
class bubop.CommonDir

Bases: object

Get the path to one of the standard user directories - depending on the OS at hand.

static cache(*args, **kargs)
static config(*args, **kargs)
static share(*args, **kargs)
class bubop.ExitHooks(print_fn=<built-in function print>)

Bases: object

Monkeypatch sys.exit + set the excepthook to figure out when an exception or an exit event was raised.

Set this during program execution and then, e.g., during program teardown, e.g., using atexit, figure out whether an exception was raised or whether the program exited successfully or failed.

exc_handler(exc_type, exc_value, tb, *args)

Exception handler override.

exit(code=0)

Exit function override.

register()

Register the exit hook.

class bubop.FileType(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)

Bases: Enum

Enum to represent an entity on a filesystem and abstract operations on them.

>>> ft = FileType.FILE
>>> ft.exists(Path("/etc/passwd"))
True
>>> ft.exists(Path("/etc/"))
False
>>> ft = FileType.DIR
>>> ft.exists(Path("/etc/passwd"))
False
>>> ft.exists(Path("/etc/"))
True
DIR = 2
FILE = 1
FILE_OR_DIR = 3
exists(path: Path) bool

True if the give npath exists. Uses the appropriate function for the Filepath at hand.

exception bubop.NoSuchFileOrDirectoryError(name)

Bases: BaseException

Exception raised when file/directory is not found.

Usage:

>>> raise NoSuchFileOrDirectoryError("foo")
Traceback (most recent call last):
bubop.exceptions.NoSuchFileOrDirectoryError: No such file or directory -> foo
exception bubop.NotEnoughArgumentsError

Bases: BaseException

Exception raised when incompatible options are given in the CLI of a program.

Usage:

>>> raise NotEnoughArgumentsError()
Traceback (most recent call last):
bubop.exceptions.NotEnoughArgumentsError: ...
exception bubop.OperatingSystemNotSupportedError(os_name: str)

Bases: BaseException

Exception raised when an operation is not supported for the OS at hand.

Usage:

>>> raise OperatingSystemNotSupportedError("Windows")
Traceback (most recent call last):
bubop.exceptions.OperatingSystemNotSupportedError: Operation is not supported for this OS -> Windows
class bubop.PrefsManager(app_name: str, config_fname: str = 'cfg.yaml', logger=<loguru.logger handlers=[(id=0, level=10, sink=<stderr>)]>)

Bases: object

Manage application-related preferences.

All the preferences of the app are stored in a key-value store (aka dict). You can either access this dict via the .contents property or the PrefsManager instance itself using the same methods as a standard dict (key in prefs_manager or prefs_manager["key"]) or using them as attributes: (prefs_manager.key)

property config_directory: Path

Get the path to the top-level config directory of the preferences at hand.

property config_file: Path

Get the path to the config file of the preferences at hand.

empty() bool

Returns whether the current preferences store is empty.

flush_config(p: Path)

Helper class for writing the current cached settings to a file.

Parameters:

p – Path to write the current settings. The given file is to be overwritten

items()

Return the items in the Preferences Manager - similar to a dict.

keys() Sequence[Any]

Return the list of keys in the Preferences Manager

update_latest(new_val)

Update the latest fetched setting.

values()

Return the list of values in the Preferences Manager

bubop.add_bool_argument(parser: ArgumentParser, arg_name: str, default: bool | None = None, true_help: str | None = None)

Add a boolean CLI argument to the given ArgumentParser object.

The flag defaults to False if no other is specified

Usage:

>>> import argparse
>>> parser = argparse.ArgumentParser(description="SomeArgumentParser")
>>> add_bool_argument(parser, "someflag")
>>> add_bool_argument(parser, "someflag2", default=True)
>>> add_bool_argument(parser, "someflag3", default=False)
>>> add_bool_argument(parser, "someflag4", true_help="somehelp4")
>>> add_bool_argument(parser, "a_flag")
>>> add_bool_argument(parser, "b-flag")
>>> config = parser.parse_args(["--someflag"]) # basic
>>> config.someflag
True
>>> config = parser.parse_args(["--no-someflag"])
>>> config.someflag
False

>>> config = vars(parser.parse_args([])) # defaults
>>> config["someflag"] == False
True
>>> config["someflag2"] == True
True
>>> config["someflag3"] == False
True
>>> config["someflag4"] == False
True
>>> "somehelp4" in parser.format_help()
True
>>> parser.parse_args(["--someflag", "--no-someflag"]) # exception
Traceback (most recent call last):
...
SystemExit: 2

>>> config = vars(parser.parse_args(["--a_flag"]))
>>> config["a_flag"] == True
True
>>> config = vars(parser.parse_args(["--b-flag"]))
>>> config["b-flag"] == True
True
bubop.all_subclasses(cls: type[Any]) set[type[Any]]

Recursively get all the (reachable) subclasses of the given class.

Usage:

>>> class Foo: pass
>>> class Bar(Foo): pass
>>> class Baz(Foo): pass
>>> class Bing(Bar): pass
>>> sorted([c.__name__ for c in all_subclasses(Foo)])
['Bar', 'Baz', 'Bing']
bubop.assume_local_tz_if_none(dt: datetime) datetime

If there is no timezone in the given datetime object, assign a local timezone.

In all cases return a new instance of the datetime object.

Usage:

>>> dt = datetime.datetime(2019, 3, 8, 0, 29, 6)
>>> dt.tzinfo is None
True
>>> dt = assume_local_tz_if_none(dt)
>>> dt.tzinfo is None
False

>>> dt_old = datetime.datetime.now(tz=dateutil.tz.tzlocal())
>>> dt_old.tzinfo is not None
True
>>> dt = assume_local_tz_if_none(dt_old)
>>> dt.tzinfo is None
False
bubop.camel_case_to_dashed(s: str) str

Convert a CamelCase string into the dashed representation.

>>> camel_case_to_dashed("KalimeraKalinuxta")
'kalimera-kalinuxta'
>>> camel_case_to_dashed("somethingIsRotten")
'something-is-rotten'
>>> camel_case_to_dashed("SLAM")
'slam'
bubop.check_optional_mutually_exclusive(arg1: Any, arg2: Any) None

Check if the given mutually exclusive args indeed hold the said required rule (optional + mutual exclusivity)

Raise a CliIncompatibleOptionsError if they don’t uphold the rule.

>>> kalimera = 1
>>> kalinuxta = 2
>>> check_optional_mutually_exclusive(kalimera, kalinuxta)
Traceback (most recent call last):
bubop.exceptions.CliIncompatibleOptionsError: ... kalimera ... kalinuxta ...
>>> kalimera = None
>>> check_optional_mutually_exclusive(kalimera, kalinuxta) == None
True
>>> kalinuxta = None
>>> check_optional_mutually_exclusive(kalimera, kalinuxta) == None
True
bubop.check_required_mutually_exclusive(arg1: Any, arg2: Any, arg1_name: str, arg2_name: str) None

Check if the given mutually exclusive args indeed hold the said required rule (required + mutual exclusivity)

Raise exception if they don’t uphold the rule.

bubop.format_datetime_tz(dt: datetime) str

Format a datetime object according to the ISO specifications containing the ‘T’ and ‘Z’ separators

Usage:

>>> format_datetime_tz(datetime.datetime(2019, 3, 5, 0, 3, 9, 1234))
'2019-03-05T00:03:09.001234Z'
>>> format_datetime_tz(datetime.datetime(2019, 3, 5, 0, 3, 9, 123))
'2019-03-05T00:03:09.000123Z'
bubop.format_dict(items: Mapping[Any, Any], align_items: bool = True, **kargs) str

Utility for formatting a dictionary - similar to print.pformat.

Accepts mostly the same arguments as format_list.

bubop.format_list(items: Sequence[str], header: str, indent=2, bullet_char='-', header_sep='=', prefix: str = '', suffix: str = '') str

Format and return a string with the corresponding header and all the items each occupying a single line and with the specified indentation.

bubop.get_object_unique_name(obj: Any) str

Return a unique string associated with the given object.

That string is constructed as follows: <object class name>_<object_hex_id>

bubop.get_random_string(len_=10) str

Return a random string containing ascii characters, digits and punctuation marks.

bubop.get_valid_filename(s: str) str

Return a filename-compatible version of the given string s.

Parameters:

s – String to be used as the base of the filename. You may also pass non-string objects that will however be able to convert to strings via the str operator.

>>> get_valid_filename(r"5678^()^")
'5678____'
>>> get_valid_filename(r"a|string\go/es||here")
'a_string_go_es__here'
>>> get_valid_filename(r"strin***g")
'strin___g'
bubop.inspect_var_name(var: Any, level=2) str | None

Retrieve the name of the variable var passed from the caller to this function.

Use the level argument to refer to the argument name of the caller’s caller (level=2) or the caller’s caller’s caller, (level=3) etc. instead.

Quite experimental function in nature, may not work as expected.

Usage:

>>> var1 = "kalimera"
>>> inspect_var_name(var1, level=1)
'var1'
>>> fn = lambda x: (inspect_var_name(x, level=1), inspect_var_name(x))
>>> fn(var1)
('x', 'var1')
>>> inspect_var_name(var1, level=1000) == None
Traceback (most recent call last):
bubop.exceptions.TooShallowStackError: Stack has less ...
>>> inspect_var_name(1, level=1) == None
True
bubop.is_same_datetime(dt1: datetime, dt2: datetime, tol: timedelta = datetime.timedelta(0)) bool

Compare two datetime.datetime objects.

If the timezone is empty, assume local timezone.

Usage:

>>> dt = datetime.datetime
>>> td = datetime.timedelta
>>> dt1 = datetime.datetime.now()
>>> dt2 = dt1 + td(minutes=5)
>>> is_same_datetime(dt1, dt2)
False
>>> is_same_datetime(dt1, dt2, tol=td(minutes=2))
False
>>> is_same_datetime(dt1, dt2, tol=td(minutes=5))
True
bubop.log_to_syslog(name: str, level: Literal['FATAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG', 'TRACE'] = 'WARNING')

Enable logging to syslog for the given logger.

This method does not remove any of the existing logging handlers.

bubop.loguru_set_verbosity(verbosity: int)

Set the verbosity of the tqdm logger.

Parameters:

verbosity – 0 for >= INFO, 1 for >= DEBUG, 2 for >= TRACE

bubop.loguru_tqdm_sink(verbosity: int)

Change the default loguru logger to use tqdm.

Parameters:

verbosity – Set the verbosity of the tqdm logger. 0 for >= INFO, 1 for >= DEBUG, 2 for >= TRACE

bubop.non_empty(title: str, value: str, join_with: str = ' -> ', newline=True) str

Return a one-line formatted string of “title -> value” but only if value is a non-empty string. Otherwise return an empty string

>>> non_empty(title="title", value="value")
'title -> value\n'
>>> non_empty(title="title", value=None)
''
bubop.parse_datetime(s: str) datetime

Parse a datetime from the given string.

Usage:

>>> parse_datetime("2019-03-05") == datetime.datetime(2019, 3, 5, 0, 0)
True
>>> is_same_datetime(parse_datetime("2019-03-05T00:03:09Z"), datetime.datetime(2019, 3, 5, 0, 3, 9, tzinfo=dateutil.tz.tzutc()))
True
>>> is_same_datetime(parse_datetime("2019-03-05T00:03:01.1234Z"), datetime.datetime(2019, 3, 5, 0, 3, 1, 123400, tzinfo=dateutil.tz.tzutc()))
True
>>> is_same_datetime(parse_datetime("2019-03-08T00:29:06.602Z"), datetime.datetime(2019, 3, 8, 0, 29, 6, 602000, tzinfo=dateutil.tz.tzutc()))
True
bubop.pickle_dump(item: Any, path: Path | str, *, protocol=0, **kargs)

Helper method to serialize your dictionary to the given path.

By default use protocol 0.

bubop.pickle_load(path: Path | str) Any

Pickle-load using the given path.

bubop.read_gpg_token(p: Path, timeout_secs: int = 3) str

Read the token from a gpg file.

Raise a RuntimeError if the decryption was unsuccessful.

bubop.valid_dir(s: str) Path

Return a pathlib.Path from the given string.

If the input does not correspond to a valid directory, then raise an exception

>>> valid_dir("/etc/")
PosixPath...
>>> valid_dir("/etc/passwd")
Traceback (most recent call last):
NotADirectoryError: ...
bubop.valid_file(s: str) Path

Return a pathlib.Path from the given string.

If the input does not correspond to a valid directory, then raise an exception >>> valid_file(“/etc/”) Traceback (most recent call last): FileNotFoundError: … >>> valid_file(“/etc/passwd”) PosixPath…

bubop.valid_path(s: str, filetype=FileType.FILE_OR_DIR) Path

Return a pathlib.Path from the given string.

If the input does not correspond to a valid path, then raise an exception

>>> valid_path("/etc")
PosixPath...
>>> valid_path("/etc/some-invalid-path")
Traceback (most recent call last):
bubop.exceptions.NoSuchFileOrDirectoryError: No such ...
>>> valid_path("/etc", filetype=FileType.FILE)
Traceback (most recent call last):
FileNotFoundError: ...
>>> valid_path("/etc/passwd", filetype=FileType.FILE)
PosixPath...
>>> valid_path("/etc/passwd", filetype=FileType.DIR)
Traceback (most recent call last):
NotADirectoryError: ...
>>> valid_path("/etc", filetype=FileType.DIR)
PosixPath...
bubop.verbosity_int_to_std_logging_lvl(verbosity: int) int

Map an integer to a corresponding python std logging module level :param verbosity: Set the verbosity of the tqdm logger.

0 for >= INFO, 1 for >= DEBUG,

>>> verbosity_int_to_std_logging_lvl(0)
20
>>> verbosity_int_to_std_logging_lvl(1)
10
>>> verbosity_int_to_std_logging_lvl(2)
10
>>> verbosity_int_to_std_logging_lvl(3)
10
>>> verbosity_int_to_std_logging_lvl(-1)
Traceback (most recent call last):
RuntimeError: ...
bubop.verbosity_int_to_str(verbosity: int) Literal['FATAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG', 'TRACE']

Map a verbosity integer to the corresponding Loguru Level. :param verbosity: = 0 -> INFO,

= 1 -> DEBUG, >=2 -> TRACE

Usage:

>>> verbosity_int_to_str(1)
'DEBUG'
>>> verbosity_int_to_str(2)
'TRACE'
>>> verbosity_int_to_str(3)
'TRACE'
>>> verbosity_int_to_str(-1)
Traceback (most recent call last):
RuntimeError: ...
bubop.write_gpg_token(p: Path, token: str, recipient: str) None

Write the given token to a gpg file designated by p.

Raise a RuntimeError if the encryption was unsuccessful.

bubop.xor(*args) bool

True if exactly one of the arguments of the iterable is True.

>>> xor(0,1,0,)
True
>>> xor(1,2,3,)
False
>>> xor(False, False, False)
False
>>> xor("kalimera", "kalinuxta")
False
>>> xor("", "a", "")
True
>>> xor("", "", "")
False