Collection of lower-level utilities that enhance code compatibility and validation.
Project description
Motivation
While developing Python software for Visual Effects pipelines, I found myself having to write the same boiler-plate code over and over again, as well as struggling with compatibility issues and feature gaps between Python 2.7 and Python 3.7+.
So I decided to implement solutions for those issues at the base, and basicco was born.
Overview
Basicco provides a collection of lower-level Utilities that enhance code readability and validation.
Utilities
caller_module
Retrieve the caller’s module name.
>>> from basicco.caller_module import caller_module
>>> def do_something():
... return "I was called by {}".format(caller_module())
...
>>> do_something()
'I was called by __main__'
context_vars
Rough implementation of ContextVar for Python 2.7 that works with threads.
>>> from basicco.context_vars import ContextVar
>>> my_var = ContextVar("my_var", default="bar")
>>> token = my_var.set("foo")
>>> my_var.get()
'foo'
>>> my_var.reset(token)
>>> my_var.get()
'bar'
custom_repr
Custom representation functions.
>>> from basicco.custom_repr import mapping_repr
>>> dct = {"a": 1, "b": 2}
>>> mapping_repr(dct, prefix="<", suffix=">", template="{key}={value}", sorting=True)
"<'a'=1, 'b'=2>"
>>> from basicco.custom_repr import iterable_repr
>>> tup = ("a", "b", "c", 1, 2, 3)
>>> iterable_repr(tup, prefix="<", suffix=">", value_repr=str)
'<a, b, c, 1, 2, 3>'
explicit_hash
Metaclass that forces __hash__ to be declared when __eq__ is declared.
>>> import six
>>> from basicco.explicit_hash import ExplicitHashMeta
>>> class Asset(six.with_metaclass(ExplicitHashMeta, object)):
... def __eq__(self, other):
... pass
...
Traceback (most recent call last):
TypeError: declared '__eq__' in 'Asset' but didn't declare '__hash__'
fabricate_value
Run a value through a callable factory (or None).
>>> from basicco.fabricate_value import fabricate_value
>>> fabricate_value(None, 3)
3
>>> fabricate_value(str, 3)
'3'
>>> fabricate_value("str", 3) # use an import path
'3'
import_path
Generate importable dot paths and import from them.
>>> import itertools
>>> from basicco.import_path import get_path, import_path
>>> get_path(itertools.chain)
'itertools.chain'
>>> import_path("itertools.chain")
<... 'itertools.chain'>
>>> from basicco.import_path import extract_generic_paths
>>> extract_generic_paths("Tuple[int, str]")
('Tuple', ('int', 'str'))
mangling
Functions to mangle/unmangle/extract private names.
>>> from basicco.mangling import mangle, unmangle, extract
>>> mangle("__member", "Foo")
'_Foo__member'
>>> unmangle("_Foo__member", "Foo")
'__member'
>>> extract("_Foo__member")
('__member', 'Foo')
mapping_proxy
Mapping Proxy type (read-only) for older Python versions.
>>> from basicco.mapping_proxy import MappingProxyType
>>> internal_dict = {"foo": "bar"}
>>> proxy_dict = MappingProxyType(internal_dict)
>>> proxy_dict["foo"]
'bar'
namespace
Wraps a dictionary/mapping and provides attribute-style access to it.
>>> from basicco.namespace import Namespace
>>> ns = Namespace({"bar": "foo"})
>>> ns.bar
'foo'
>>> from basicco.namespace import MutableNamespace
>>> ns = MutableNamespace({"bar": "foo"})
>>> ns.foo = "bar"
>>> ns.foo
'bar'
>>> ns.bar
'foo'
Also provides a NamespacedMeta metaclass for adding a __namespace__ private property that is unique to each class.
>>> from six import with_metaclass
>>> from basicco.namespace import NamespacedMeta
>>> class Asset(with_metaclass(NamespacedMeta, object)):
... pass
...
>>> Asset.__namespace__.foo = "bar"
qualname
Python 2.7 compatible way of getting the qualified name. Inspired by wbolster/qualname.
recursive_repr
Decorator that prevents infinite recursion for __repr__ methods.
>>> from basicco.recursive_repr import recursive_repr
>>> class MyClass(object):
...
... @recursive_repr
... def __repr__(self):
... return "MyClass<{!r}>".format(self)
...
>>> my_obj = MyClass()
>>> repr(my_obj)
'MyClass<...>'
runtime_final
Runtime-checked version of the typing.final decorator.
Can be used on methods, properties, classmethods, staticmethods, and classes that have FinalizedMeta as a metaclass. It is also recognized by static type checkers and prevents subclassing and/or member overriding during runtime:
>>> import six
>>> from basicco.runtime_final import FinalizedMeta, final
>>> @final
... class Asset(six.with_metaclass(FinalizedMeta, object)):
... pass
...
>>> class SubAsset(Asset):
... pass
...
Traceback (most recent call last):
TypeError: can't subclass final class 'Asset'
>>> import six
>>> from basicco.runtime_final import FinalizedMeta, final
>>> class Asset(six.with_metaclass(FinalizedMeta, object)):
... @final
... def method(self):
... pass
...
>>> class SubAsset(Asset):
... def method(self):
... pass
Traceback (most recent call last):
TypeError: can't override final member 'method'
>>> import six
>>> from basicco.runtime_final import FinalizedMeta, final
>>> class Asset(six.with_metaclass(FinalizedMeta, object)):
... @property
... @final
... def prop(self):
... pass
...
>>> class SubAsset(Asset):
... @property
... def prop(self):
... pass
Traceback (most recent call last):
TypeError: can't override final member 'prop'
scrape_class
Scrape a class and get a dictionary with filtered named members. This will respect the MRO (supports multiple inheritance).
>>> from basicco.scrape_class import scrape_class
>>> class Field(object):
... pass
...
>>> class Asset(object):
... name = Field()
... version = Field()
...
>>> class SubAsset(Asset):
... sub_name = Field()
...
>>> def field_filter(base, member_name, member):
... return isinstance(member, Field)
>>> sorted(scrape_class(SubAsset, field_filter))
['name', 'sub_name', 'version']
type_checking
Runtime type checking with support for import paths.
>>> from itertools import chain
>>> from basicco.type_checking import is_instance
>>> class SubChain(chain):
... pass
...
>>> is_instance(3, int)
True
>>> is_instance(3, (chain, int))
True
>>> is_instance(3, ())
False
>>> is_instance(SubChain(), "itertools.chain")
True
>>> is_instance(chain(), "itertools.chain", subtypes=False)
True
>>> is_instance(SubChain(), "itertools.chain", subtypes=False)
False
Asserting an instance’s type:
>>> from itertools import chain
>>> from basicco.type_checking import assert_is_instance
>>> class SubChain(chain):
... pass
...
>>> assert_is_instance(3, int)
3
>>> assert_is_instance(3, (chain, int))
3
>>> assert_is_instance(3, ())
Traceback (most recent call last):
ValueError: no types were provided to perform assertion
>>> assert_is_instance(3, "itertools.chain")
Traceback (most recent call last):
TypeError: got 'int' object, expected instance of 'chain' or any of its subclasses
>>> assert_is_instance(chain(), "itertools.chain", subtypes=False)
<itertools.chain object at ...>
>>> assert_is_instance(SubChain(), "itertools.chain", subtypes=False)
Traceback (most recent call last):
TypeError: got 'SubChain' object, expected instance of 'chain' (instances of subclasses are not accepted)
unique_iterator
Iterator that yields unique values.
>>> from basicco.unique_iterator import unique_iterator
>>> list(unique_iterator([1, 2, 3, 3, 4, 4, 5]))
[1, 2, 3, 4, 5]
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for basicco-4.2.1-py2.py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | d43f12c41b194b03d5ca14a06506abacbc3fd89bd504353e87aaf1a7286f832d |
|
MD5 | fe6f48445d5f2d35314477695cdb94b2 |
|
BLAKE2b-256 | 69eb9936ba6b0c04a1dc050e147521434a6e3d0d7a509b5d84f98f6d476ee9cb |