import functools import weakref class Property(object): instances = weakref.WeakValueDictionary() def __new__(cls, name): if name in Property.instances: return Property.instances[name] obj = super().__new__(cls) setattr(obj, 'name', name) Property.instances[name] = obj return obj class requires(object): def __init__(self, *args, dynamic=False): self.requires = set(args) self.dynamic = dynamic def __call__(self, func, *args, **kwargs): @functools.wraps(func) def static_wrapper(*args, **kwargs): return func(*args, **kwargs) @functools.wraps(func) def dynamic_wrapper(*args, **kwargs): for req in self.requires: assert req in Property.instances, 'missing requirement: {}'.format(req) return func(*args, **kwargs) if self.dynamic: return dynamic_wrapper for req in self.requires: assert req in Property.instances return static_wrapper class provides(object): def __init__(self, *args): self.properties = list(args) def __call__(self, func, *args, **kwargs): @functools.wraps(func) def impl(*args, **kwargs): return func(*args, **kwargs) setattr(impl, '__provides__', [Property(p) for p in self.properties]) return impl @requires('bar', dynamic=True) @provides('foo') class Foo(object): def stuff(self): print('foo') provides('baz') @requires('baz', dynamic=True) def other(self): print('baz') @provides('bar') class Bar(object): pass f = Foo() f.stuff() f.other()