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 raisesAttributeError
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 classMeta
. If it’s not found there, andself.inherit == True
and there is a base class that has a classMeta
, use that value, otherwiseself.default
.Parameters: - Meta – the class
Meta
(if any) from the class-under-construction (NOTE: this will be anobject
orNone
, NOT an instance ofMetaOptionsFactory
) - 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
- Meta – the class
-
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 understandMeta
options.-
name = 'abstract'
The attribute name on class
Meta
objects isabstract
.-
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 ofMetaOption
subclasses (or instances):class MyMetaOptionsFactory(MetaOptionsFactory): _options = [AbstractMetaOption]
Or override
_get_meta_options()
to return a list ofMetaOption
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 declaredclass 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’sclass Meta
with this populated factory instance (akaself
).
-
_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’sclass 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 ofYourMetaOptionsFactory
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
- mcs_args – The
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 ofYourFooSubclass
as if callingYourFooSubclass()
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