API Documentation

McsArgs

class py_meta_utils.McsArgs(mcs: type, name: str, bases: Tuple[Type[object], ...], clsdict: Dict[str, Any])[source]

Data holder for the parameters to type.__new__():

class Metaclass(type):
    def __new__(mcs, name, bases, clsdict):
        mcs_args = McsArgs(mcs, name, bases, clsdict)
        # do stuff
        return super().__new__(*mcs_args)

# or in short-hand:

class Metaclass(type):
    def __new__(mcs, *args):
        mcs_args = McsArgs(mcs, *args)
        # do stuff
        return super().__new__(*mcs_args)
getattr(name, default: Any = <py_meta_utils._missing object>)[source]

Convenience method equivalent to deep_getattr(mcs_args.clsdict, mcs_args.bases, 'attr_name'[, default])

module

Returns the module of the class-under-construction, or None.

qualname

Returns the fully qualified name of the class-under-construction, if possible, otherwise just the class name.

Meta

Returns the class Meta from the class-under-construction.

Raises KeyError if it’s not present.

is_abstract

Whether or not the class-under-construction was declared as abstract (NOTE: this property is usable even before the MetaOptionsFactory has run)

deep_getattr

py_meta_utils.deep_getattr(clsdict: Dict[str, Any], bases: Tuple[Type[object], ...], name: str, default: Any = <py_meta_utils._missing object>) → Any[source]

Acts just like getattr would on a constructed class object, except this operates on the pre-construction class dictionary and base classes. In other words, first we look for the attribute in the class dictionary, and then we search all the base classes (in method resolution order), finally returning the default value if the attribute was not found in any of the class dictionary or base classes (or it raises AttributeError if no default was given).

MetaOption

class py_meta_utils.MetaOption(name: str, default: Any = None, inherit: bool = False)[source]

Base class for custom meta options.

name = None

The attribute name of the option on class Meta objects.

default = None

The default value for this meta option.

inherit = None

Whether or not this option’s value should be inherited from the class Meta of any base classes.

get_value(Meta: Type[object], base_classes_meta, mcs_args: py_meta_utils.McsArgs) → Any[source]

Returns the value for self.name given the class-under-construction’s class Meta. If it’s not found there, and self.inherit == True and there is a base class that has a class Meta, use that value, otherwise self.default.

Parameters:
  • Meta – the class Meta (if any) from the class-under-construction (NOTE: this will be an object or None, NOT an instance of MetaOptionsFactory)
  • base_classes_meta – the MetaOptionsFactory instance (if any) from the base class of the class-under-construction
  • mcs_args – the McsArgs for the class-under-construction
check_value(value: Any, mcs_args: py_meta_utils.McsArgs)[source]

Optional callback to verify the user provided a valid value.

Your implementation should assert/raise with an error message if invalid.

contribute_to_class(mcs_args: py_meta_utils.McsArgs, value: Any)[source]

Optional callback to modify the McsArgs of the class-under-construction.

AbstractMetaOption

class py_meta_utils.AbstractMetaOption[source]

A meta option that allows designating a class as abstract, using either:

class SomeAbstractBase(metaclass=MetaclassWithAnOptionsFactory):
    __abstract__ = True

# or

class SomeAbstractBase(metaclass=MetaclassWithAnOptionsFactory):
    class Meta:
        abstract = True

In the latter case, we make sure to set the __abstract__ class attribute for backwards compatibility with libraries that do not understand Meta options.

name = 'abstract'

The attribute name on class Meta objects is abstract.

default = False

The default value is False.

inherit = False

We do not inherit this value from the class Meta of base classes.

MetaOptionsFactory

class py_meta_utils.MetaOptionsFactory[source]

Base class for meta options factory classes. Subclasses should either set _options to a list of MetaOption subclasses (or instances):

class MyMetaOptionsFactory(MetaOptionsFactory):
    _options = [AbstractMetaOption]

Or override _get_meta_options() to return a list of MetaOption instances:

class MyMetaOptionsFactory(MetaOptionsFactory):
    def _get_meta_options(self):
        return [AbstractMetaOption()]

IMPORTANT: If you add any attributes and/or methods to your factory subclass, they must be protected (ie, prefixed with an _ character).

_options = []

A list of MetaOption subclasses (or instances) that this factory supports.

_get_meta_options() → List[py_meta_utils.MetaOption][source]

Returns a list of MetaOption instances that this factory supports.

_contribute_to_class(mcs_args: py_meta_utils.McsArgs)[source]

Where the magic happens. Takes one parameter, the McsArgs of the class-under-construction, and processes the declared class Meta from it (if any). We fill ourself with the declared meta options’ name/value pairs, give the declared meta options a chance to also contribute to the class-under- construction, and finally replace the class-under-construction’s class Meta with this populated factory instance (aka self).

_fill_from_meta(Meta: Type[object], base_classes_meta, mcs_args: py_meta_utils.McsArgs)[source]

Iterate over our supported meta options, and set attributes on the factory instance (self) for each meta option’s name/value. Raises TypeError if we discover any unsupported meta options on the class-under-construction’s class Meta.

process_factory_meta_options

py_meta_utils.process_factory_meta_options(mcs_args: py_meta_utils.McsArgs, default_factory_class: Type[py_meta_utils.MetaOptionsFactory] = <class 'py_meta_utils.MetaOptionsFactory'>, factory_attr_name: str = '_meta_options_factory_class') → py_meta_utils.MetaOptionsFactory[source]

Main entry point for consumer metaclasses. Usage:

from py_meta_utils import (AbstractMetaOption, McsArgs, MetaOptionsFactory,
                           process_factory_meta_options)


class YourMetaOptionsFactory(MetaOptionsFactory):
    _options = [AbstractMetaOption]


class YourMetaclass(type):
    def __new__(mcs, name, bases, clsdict):
        mcs_args = McsArgs(mcs, name, bases, clsdict)

        # process_factory_meta_options must come *before* super().__new__()
        process_factory_meta_options(mcs_args, YourMetaOptionsFactory)
        return super().__new__(*mcs_args)


class YourClass(metaclass=YourMetaclass):
    pass

Subclasses of YourClass may set their _meta_options_factory_class attribute to a subclass of YourMetaOptionsFactory to customize their own supported meta options:

from py_meta_utils import MetaOption


class FooMetaOption(MetaOption):
    def __init__(self):
        super().__init__(name='foo', default=None, inherit=True)


class FooMetaOptionsFactory(YourMetaOptionsFactory):
    _options = YourMetaOptionsFactory._options + [
        FooMetaOption,
    ]


class FooClass(YourClass):
    _meta_options_factory_class = FooMetaOptionsFactory

    class Meta:
        foo = 'bar'
Parameters:
  • mcs_args – The McsArgs for the class-under-construction
  • default_factory_class – The default MetaOptionsFactory class to use, if the factory_attr_name attribute is not set on the class-under-construction
  • factory_attr_name – The attribute name to look for an overridden factory meta options class on the class-under-construction
Returns:

The populated instance of the factory class

Utility Classes

EnsureProtectedMembers

class py_meta_utils.EnsureProtectedMembers(name, bases, clsdict)[source]

Metaclass to ensure that all members (attributes and method names) of consumer classes are protected (ie, prefixed with an _).

Consumer classes may have an _allowed_properties class attribute set to a list of allowed public properties.

Raises NameError if any public members not in cls._allowed_properties are found.

Singleton

class py_meta_utils.Singleton[source]

A metaclass that makes a consumer class a singleton:

from py_meta_utils import Singleton

class Foo(metaclass=Singleton):
    pass

foo = Foo()
assert foo == Foo() == Foo()  # True

Note that if you subclass a singleton, then you must inform the base class:

Foo.set_singleton_class(YourFooSubclass)

This way, calling Foo() will still return the same instance of YourFooSubclass as if calling YourFooSubclass() itself:

foo = Foo()
sub = YourFooSubclass()
assert foo == sub == Foo() == YourFooSubclass()

OptionalClass

class py_meta_utils.OptionalClass(*args, **kwargs)[source]

Use this as a generic base class if you have classes that depend on an optional package:

try:
    from optional_dependency import SomeClass
except ImportError:
    from py_meta_utils import OptionalClass as SomeClass

class Optional(SomeClass):
    pass

OptionalMetaclass

class py_meta_utils.OptionalMetaclass[source]

Use this as a generic base metaclass if you need to subclass a metaclass from an optional package:

try:
    from optional_dependency import SomeMetaclass
except ImportError:
    from py_meta_utils import OptionalMetaclass as SomeMetaclass

class Optional(metaclass=SomeMetaclass):
    pass