25 from .config
import Config, FieldValidationError, _typeStr
26 from .configChoiceField
import ConfigInstanceDict, ConfigChoiceField
28 __all__ = (
"Registry",
"makeRegistry",
"RegistryField",
"registerConfig",
"registerConfigurable")
31 """A wrapper for configurables
33 Used for configurables that don't contain a ConfigClass attribute,
34 or contain one that is being overridden.
41 return self.
_target(*args, **kwargs)
45 """A base class for global registries, mapping names to configurables.
47 There are no hard requirements on configurable, but they typically create an algorithm
48 or are themselves the algorithm, and typical usage is as follows:
49 - configurable is a callable whose call signature is (config, ...extra arguments...)
50 - All configurables added to a particular registry will have the same call signature
51 - All configurables in a registry will typically share something important in common.
52 For example all configurables in psfMatchingRegistry return a psf matching
53 class that has a psfMatch method with a particular call signature.
55 A registry acts like a read-only dictionary with an additional register method to add items.
56 The dict contains configurables and each configurable has an instance ConfigClass.
60 class FooConfig(Config):
61 val = Field(dtype=int, default=3, doc="parameter for Foo")
63 ConfigClass = FooConfig
64 def __init__(self, config):
66 def addVal(self, num):
67 return self.config.val + num
68 registry.register("foo", Foo)
69 names = registry.keys() # returns ("foo",)
70 fooConfigurable = registry["foo"]
71 fooConfig = fooItem.ConfigClass()
72 foo = fooConfigurable(fooConfig)
73 foo.addVal(5) # returns config.val + 5
77 """Construct a registry of name: configurables
79 @param configBaseType: base class for config classes in registry
81 if not issubclass(configBaseType, Config):
82 raise TypeError(
"configBaseType=%s must be a subclass of Config" %
_typeStr(configBaseType,))
86 def register(self, name, target, ConfigClass=None):
87 """Add a new item to the registry.
89 @param target A callable 'object that takes a Config instance as its first argument.
90 This may be a Python type, but is not required to be.
91 @param ConfigClass A subclass of pex_config Config used to configure the configurable;
92 if None then configurable.ConfigClass is used.
94 @note: If ConfigClass is provided then then 'target' is wrapped in a new object that forwards
95 function calls to it. Otherwise the original 'target' is stored.
97 @raise AttributeError if ConfigClass is None and target does not have attribute ConfigClass
99 if name
in self.
_dict:
100 raise RuntimeError(
"An item with name %r already exists" % name)
101 if ConfigClass
is None:
106 raise TypeError(
"ConfigClass=%s is not a subclass of %r" % \
108 self.
_dict[name] = wrapper
115 def makeField(self, doc, default=None, optional=False, multi=False):
119 """Private class that makes a Registry behave like the thing a ConfigChoiceField expects."""
131 ConfigInstanceDict.__init__(self, config, field)
135 if self._field.multi:
136 raise FieldValidationError(self._field, self._config,
137 "Multi-selection field has no attribute 'target'")
138 return self._field.typemap.registry[self._selection]
139 target = property(_getTarget)
142 if not self._field.multi:
143 raise FieldValidationError(self._field, self._config,
144 "Single-selection field has no attribute 'targets'")
145 return [self._field.typemap.registry[c]
for c
in self._selection]
146 targets = property(_getTargets)
149 """Call the active target(s) with the active config as a keyword arg
151 If this is a multi-selection field, return a list obtained by calling
152 each active target with its corresponding active config.
154 Additional arguments will be passed on to the configurable target(s)
156 if self.active
is None:
157 msg =
"No selection has been made. Options: %s" % \
158 (
" ".join(self._field.typemap.registry.keys()))
159 raise FieldValidationError(self._field, self._config, msg)
160 if self._field.multi:
162 for c
in self._selection:
163 retvals.append(self._field.typemap.registry[c](*args, config=self[c], **kw))
166 return self._field.typemap.registry[self.name](*args, config=self[self.name], **kw)
168 if attr ==
"registry":
169 object.__setattr__(self, attr, value)
171 ConfigInstanceDict.__setattr__(self, attr, value)
174 instanceDictClass = RegistryInstanceDict
176 def __init__(self, doc, registry, default=None, optional=False, multi=False):
179 ConfigChoiceField.__init__(self, doc, types, default, optional, multi)
182 """Customize deep-copying, want a reference to the original registry.
183 WARNING: this must be overridden by subclasses if they change the
184 constructor signature!
186 other = type(self)(doc=self.doc, registry=self.
registry,
187 default=copy.deepcopy(self.default),
188 optional=self.optional, multi=self.multi)
189 other.source=self.source
193 """A convenience function to create a new registry.
195 The returned value is an instance of a trivial subclass of Registry whose only purpose is to
196 customize its doc string and set attrList.
198 cls = type(
"Registry", (Registry,), {
"__doc__": doc})
199 return cls(configBaseType=configBaseType)
202 """A decorator that adds a class as a configurable in a Registry.
204 If the 'ConfigClass' argument is None, the class's ConfigClass attribute will be used.
207 registry.register(name, target=cls, ConfigClass=ConfigClass)
212 """A decorator that adds a class as a ConfigClass in a Registry, and associates it with the given
216 registry.register(name, target=target, ConfigClass=cls)