basic_structures

class Obj(**kwargs)
class Obj(map_: Mapping, **kwargs)
class Obj(iterable: Iterable, **kwargs)

Bases: dict

Obj is a sort of wishy-washy map where branches can be implicitly generated easily.

For example:

holder = Obj()
holder.inner.name = 42
holder.outer[52] = 7
holder.outer[37] = 'lorem'
holder['general'] = 'Washington'

Should generate:

{
    'inner': {
        'name': 42
    },
    'outer': {
        52: 7,
        37: 'lorem'
    },
    'general': 'Washington'
}

It has as_list() and as_tuple() methods which can be used to return only the values of the dict, and an as_dict() option which will return the entire dict.

It can also be hardened so that new keys cannot be added and existing keys cannot be removed.

as_dict(recur: type | Tuple[type] = Ellipsis) dict

Generates a dict of the key-value pairs in an Obj. These pairs may or may not be in the expected order.

Parameters:

recur

The type of conversion that should be used on internally-nested Objs. Valid types for this are list, tuple, and dict, and it can be specified either for the entire depth of the Obj or as a tuple of those types to be used for each depth. If it is not provided, nested Objs will be included as-is.

For Example:

holder = Obj()
holder.lorem.ipsum = 5
holder.lorem.dolor = 6
holder.lorem.sit = 7
holder.amet.consectetur = 1
holder.amet.adipiscing = 2
holder.amet.elit.sed = 3
holder.amet.elit.tempor = 4
holder.sagittis[0] = 55
holder.sagittis[1] = 56
holder.sagittis[2] = 57
holder.sagittis[3] = 58
holder[543] = 'mi'

… which should essentially be …

{
    'lorem': {
        'ipsum': 5,
        'dolor': 6,
        'sit': 7
    },
    'amet': {
        'consectetur': 1,
        'adipiscing': 2,
        'elit': {
            'sed': 3,
            'tempor': 4
        }
    },
    'sagittis': {
        0: 55,
        1: 56,
        2: 57,
        3: 58
    },
    543: 'mi'
}

… should behave in the following manner:

Example 1 - No Recursive Re-formatting:

ans = holder.as_dict()

ans should be:

{
    'lorem': Obj({'ipsum': 5, 'dolor': 6, 'sit': 7}),
    'amet': Obj({'consectetur': 1, 'adipiscing': 2,
         'elit': Obj({'sed': 3, 'tempor': 4})}),
    'sagittis': Obj({0: 55, 1: 56, 2: 57, 3: 58}),
    543: 'mi'
}

Example 2 - Recur is a Type:

ans = holder.as_dict(list)

ans should be:

{
    'lorem': [5, 6, 7],
    'amet': [1, 2, [3, 4]],
    'sagittis': [55, 56, 57, 58],
    543: 'mi'
}
Example 3 - Recur is a Tuple of Types (Longer than or Equal to the Max Depth of the

Nesting):

ans = holder.as_dict((dict, tuple))

ans should be:

{
    'lorem': {'ipsum': 5, 'dolor': 6, 'sit': 7 },
    'amet': {'consectetur': 1, 'adipiscing': 2, 'elit': (3, 4)},
    'sagittis': {0: 55, 1: 56, 2: 57, 3: 58},
    543: 'mi'
}

Example 4 - Recur is a Tuple of Types(Shorter than the Max Depth of the Nesting):

ans = holder.as_dict((tuple,))

ans should be:

{
    'lorem': (5, 6, 7),
    'amet': (1, 2, Obj({'sed': 3, 'tempor': 4})),
    'sagittis': (55, 56, 57, 58),
    543: 'mi'
}

as_list(recur: type | Tuple[type] = Ellipsis) list

Generates a list of the values in an Obj. These values should be in the expected order.

Parameters:

recur

The type of conversion that should be used on internally-nested Objs. Valid types for this are list, tuple, and dict, and it can be specified either for the entire depth of the Obj or as a tuple of those types to be used for each depth. If it is not provided, nested Objs will be included as-is.

For Example:

holder = Obj()
holder.lorem.ipsum = 5
holder.lorem.dolor = 6
holder.lorem.sit = 7
holder.amet.consectetur = 1
holder.amet.adipiscing = 2
holder.amet.elit.sed = 3
holder.amet.elit.tempor = 4
holder.sagittis[0] = 55
holder.sagittis[1] = 56
holder.sagittis[2] = 57
holder.sagittis[3] = 58
holder[543] = 'mi'

… which should essentially be …

{
    'lorem': {
        'ipsum': 5,
        'dolor': 6,
        'sit': 7
    },
    'amet': {
        'consectetur': 1,
        'adipiscing': 2,
        'elit': {
            'sed': 3,
            'tempor': 4
        }
    },
    'sagittis': {
        0: 55,
        1: 56,
        2: 57,
        3: 58
    },
    543: 'mi'
}

… should behave in the following manner:

Example 1 - No Recursive Re-formatting:

ans = holder.as_list()

ans should be:

[
    Obj({'ipsum': 5, 'dolor': 6, 'sit': 7}),
    Obj({'consectetur': 1, 'adipiscing': 2,
         'elit': Obj({'sed': 3, 'tempor': 4})}),
    Obj({0: 55, 1: 56, 2: 57, 3: 58}),
    'mi'
]

Example 2 - Recur is a Type:

ans = holder.as_list(list)

ans should be:

[
    [5, 6, 7],
    [1, 2, [3, 4]],
    [55, 56, 57, 58],
    'mi'
]

Example 3 - Recur is a Tuple of Types (Longer than or Equal to the Max Depth of the Nesting):

ans = holder.as_list((dict, tuple))

ans should be:

[
    {'ipsum': 5, 'dolor': 6, 'sit': 7 },
    {'consectetur': 1, 'adipiscing': 2, 'elit': (3, 4)},
    {0: 55, 1: 56, 2: 57, 3: 58},
    'mi'
]

Example 4 - Recur is a Tuple of Types(Shorter than the Max Depth of the Nesting):

ans = holder.as_list((tuple,))

ans should be:

[
    (5, 6, 7),
    (1, 2, Obj({'sed': 3, 'tempor': 4})),
    (55, 56, 57, 58),
    'mi'
]

as_tuple(recur: type | Tuple[type] = Ellipsis) tuple

Generates a tuple of the values in an Obj. These values may or may not be in the expected order.

Parameters:

recur

The type of conversion that should be used on internally-nested Objs. Valid types for this are list, tuple, and dict, and it can be specified either for the entire depth of the Obj or as a tuple of those types to be used for each depth. If it is not provided, nested Objs will be included as-is.

For Example:

holder = Obj()
holder.lorem.ipsum = 5
holder.lorem.dolor = 6
holder.lorem.sit = 7
holder.amet.consectetur = 1
holder.amet.adipiscing = 2
holder.amet.elit.sed = 3
holder.amet.elit.tempor = 4
holder.sagittis[0] = 55
holder.sagittis[1] = 56
holder.sagittis[2] = 57
holder.sagittis[3] = 58
holder[543] = 'mi'

… which should essentially be …

{
    'lorem': {
        'ipsum': 5,
        'dolor': 6,
        'sit': 7
    },
    'amet': {
        'consectetur': 1,
        'adipiscing': 2,
        'elit': {
            'sed': 3,
            'tempor': 4
        }
    },
    'sagittis': {
        0: 55,
        1: 56,
        2: 57,
        3: 58
    },
    543: 'mi'
}

… should behave in the following manner:

Example 1 - No Recursive Re-formatting:

ans = holder.as_tuple()

ans should be:

(
    Obj({'ipsum': 5, 'dolor': 6, 'sit': 7}),
    Obj({'consectetur': 1, 'adipiscing': 2,
         'elit': Obj({'sed': 3, 'tempor': 4})}),
    Obj({0: 55, 1: 56, 2: 57, 3: 58}),
    'mi'
)

Example 2 - Recur is a Type:

ans = holder.as_tuple(list)

ans should be:

(
    [5, 6, 7],
    [1, 2, [3, 4]],
    [55, 56, 57, 58],
    'mi'
)

Example 3 - Recur is a Tuple of Types (Longer than or Equal to the Max Depth of the Nesting):

ans = holder.as_tuple((dict, tuple))

ans should be:

(
    {'ipsum': 5, 'dolor': 6, 'sit': 7 },
    {'consectetur': 1, 'adipiscing': 2, 'elit': (3, 4)},
    {0: 55, 1: 56, 2: 57, 3: 58},
    'mi'
)

Example 4 - Recur is a Tuple of Types(Shorter than the Max Depth of the Nesting):

ans = holder.as_tuple((tuple,))

ans should be:

(
    (5, 6, 7),
    (1, 2, Obj({'sed': 3, 'tempor': 4})),
    (55, 56, 57, 58),
    'mi'
)

harden()

Hardens the Obj.

When an Obj is hardened:

  • New keys/attributes may not be added to it and attempts to add new keys/attributes will fail, raising an exception.

  • Current keys/attributes may not be removed from it and attempts to do so will fail, raising an exception.

Warning

This will erase and replace the existing __setitem__, __setattr__, __getattr__, __getitem__, __delitem__, __delattr__, pop, popitem, clear, and update methods of the instance.

property is_hardened: bool

Whether the Obj is hardened. When hardened, an Obj cannot have new keys assigned nor can existing keys be removed.

update(**kwargs)
update(iterable: Iterable, **kwargs)

This method updates the Obj much the same way that dict.update works. The major difference is that if a dictionary is set as the value for a key that already has an Obj as its value, the dictionary will be used to update the existing Obj instead of replacing it.

exception ObjAttributeError

Bases: AttributeError

An error associated with setting or getting an attribute from an Obj.

class Singleton

Bases: object

A simple Singleton structure which when inherited from, will make a class follow the singleton pattern.

class Speed(number: int, duration: float)

Bases: object

A simple structure which holds a number of times to do something and a time in seconds during which to do it.

property duration: float

The time in seconds to do it during.

property number: int

The number of times to do the thing.

property rate

The rate at which it would be done.

pass_(x: Any) Any

Return x.

simple_trinomial(check_func: Callable[[Any, Args], bool], check_func2: Callable[[Any, Args], bool] | None = None) Callable[[Any, Any, Args], bool | None]

Generates a callable which accepts two arguments and compares them via check_func or - if both are specified - check_func and check_func2. - If only the first or only the second returns True, the function will return False. - If both return True, the function will return True. - If both return False, the function will return ... (ellipsis).

Parameters:
  • check_func – This is the first function used to check inputs. If check_func2 is also specified, this will only be used to check the first input to the generated function. If check_func2 is not specified, this will be used to check both inputs to the generated function. This may have varargs used, but if check_func2 is specified, its signature should match.

  • check_func2 – This is the second function used to check inputs. If not specified, then check_func will be used to check both inputs to the generated function. This may have varargs used, but should only do so if check_func accepts them.

Returns:

A function which can be used to check two values against check_func (and check_func2) to verify if they both pass criteria, only one passes criteria, or neither passes criteria. Useful for checking for instances of classes.