Skip to main content

Collection of lower-level utilities that enhance code compatibility and validation.

Project description

https://github.com/brunonicko/basicco/workflows/MyPy/badge.svg https://github.com/brunonicko/basicco/workflows/Lint/badge.svg https://github.com/brunonicko/basicco/workflows/Tests/badge.svg https://readthedocs.org/projects/basicco/badge/?version=stable https://img.shields.io/github/license/brunonicko/basicco?color=light-green https://static.pepy.tech/personalized-badge/basicco?period=total&units=international_system&left_color=grey&right_color=brightgreen&left_text=Downloads https://img.shields.io/pypi/pyversions/basicco?color=light-green&style=flat

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__'

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'

generic_meta

Replacement metaclass for typing.GenericMeta that fixes bugs for Python 2.7. For newer Python versions that don’t use a metaclass for generics, this will simply be type.

>>> from six import with_metaclass
>>> from typing import Generic, TypeVar
>>> from basicco.generic_meta import GenericMeta
>>> T = TypeVar("T")
>>> class MyGeneric(six.with_metaclass(GenericMeta, Generic[T])):
...     pass
...
>>> MyGeneric[int]
__main__.MyGeneric[int]

get_mro

Get consistent MRO amongst different python versions. This works even with generic classes in Python 2.7.

>>> from six import with_metaclass
>>> from typing import Generic, TypeVar
>>> from basicco.get_mro import get_mro
>>> T = TypeVar("T")
>>> class MyGeneric(Generic[T]):
...     pass
...
>>> class SubClass(MyGeneric[T]):
...     pass
...
>>> class Mixed(SubClass[T], MyGeneric[T]):
...     pass
...
>>> [c.__name__ for c in get_mro(Mixed)]
['Mixed', 'SubClass', 'MyGeneric', 'Generic', 'object']

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']

state

Get/update the state of an object, slotted or not (works even in Python 2.7).

>>> from basicco.state import get_state
>>> class Slotted(object):
...     __slots__ = ("foo", "bar")
...     def __init__(self, foo, bar):
...         self.foo = foo
...         self.bar = bar
...
>>> slotted = Slotted("a", "b")
>>> sorted(get_state(slotted).items())
[('bar', 'b'), ('foo', 'a')]

type_checking

Runtime type checking with support for import paths and type hints.

>>> from typing import Mapping
>>> 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
>>> is_instance({"a": 1, "b": 2}, Mapping[str, int])
True

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]

weak_reference

Weak Reference-like object that supports pickling.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

basicco-6.2.0.tar.gz (30.2 kB view hashes)

Uploaded Source

Built Distribution

basicco-6.2.0-py2.py3-none-any.whl (26.7 kB view hashes)

Uploaded Python 2 Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page