22 from builtins
import object
27 from .config
import Config, FieldValidationError, _typeStr
28 from .configChoiceField
import ConfigInstanceDict, ConfigChoiceField
30 __all__ = (
"Registry",
"makeRegistry",
"RegistryField",
"registerConfig",
"registerConfigurable")
34 """A wrapper for configurables
36 Used for configurables that don't contain a ConfigClass attribute,
37 or contain one that is being overridden.
44 return self.
_target(*args, **kwargs)
48 """A base class for global registries, mapping names to configurables.
50 There are no hard requirements on configurable, but they typically create an algorithm
51 or are themselves the algorithm, and typical usage is as follows:
52 - configurable is a callable whose call signature is (config, ...extra arguments...)
53 - All configurables added to a particular registry will have the same call signature
54 - All configurables in a registry will typically share something important in common.
55 For example all configurables in psfMatchingRegistry return a psf matching
56 class that has a psfMatch method with a particular call signature.
58 A registry acts like a read-only dictionary with an additional register method to add items.
59 The dict contains configurables and each configurable has an instance ConfigClass.
63 class FooConfig(Config):
64 val = Field(dtype=int, default=3, doc="parameter for Foo")
66 ConfigClass = FooConfig
67 def __init__(self, config):
69 def addVal(self, num):
70 return self.config.val + num
71 registry.register("foo", Foo)
72 names = registry.keys() # returns ("foo",)
73 fooConfigurable = registry["foo"]
74 fooConfig = fooItem.ConfigClass()
75 foo = fooConfigurable(fooConfig)
76 foo.addVal(5) # returns config.val + 5
80 """Construct a registry of name: configurables
82 @param configBaseType: base class for config classes in registry
84 if not issubclass(configBaseType, Config):
85 raise TypeError(
"configBaseType=%s must be a subclass of Config" % _typeStr(configBaseType,))
89 def register(self, name, target, ConfigClass=None):
90 """Add a new item to the registry.
92 @param target A callable 'object that takes a Config instance as its first argument.
93 This may be a Python type, but is not required to be.
94 @param ConfigClass A subclass of pex_config Config used to configure the configurable;
95 if None then configurable.ConfigClass is used.
97 @note: If ConfigClass is provided then then 'target' is wrapped in a new object that forwards
98 function calls to it. Otherwise the original 'target' is stored.
100 @raise AttributeError if ConfigClass is None and target does not have attribute ConfigClass
102 if name
in self.
_dict:
103 raise RuntimeError(
"An item with name %r already exists" % name)
104 if ConfigClass
is None:
109 raise TypeError(
"ConfigClass=%s is not a subclass of %r" %
111 self.
_dict[name] = wrapper
114 return self.
_dict[key]
117 return len(self.
_dict)
120 return iter(self.
_dict)
123 return key
in self.
_dict
125 def makeField(self, doc, default=None, optional=False, multi=False):
130 """Private class that makes a Registry behave like the thing a ConfigChoiceField expects."""
150 ConfigInstanceDict.__init__(self, config, field)
153 def _getTarget(self):
154 if self._field.multi:
155 raise FieldValidationError(self._field, self._config,
156 "Multi-selection field has no attribute 'target'")
157 return self._field.typemap.registry[self._selection]
158 target = property(_getTarget)
160 def _getTargets(self):
161 if not self._field.multi:
162 raise FieldValidationError(self._field, self._config,
163 "Single-selection field has no attribute 'targets'")
164 return [self._field.typemap.registry[c]
for c
in self._selection]
165 targets = property(_getTargets)
168 """Call the active target(s) with the active config as a keyword arg
170 If this is a multi-selection field, return a list obtained by calling
171 each active target with its corresponding active config.
173 Additional arguments will be passed on to the configurable target(s)
175 if self.active
is None:
176 msg =
"No selection has been made. Options: %s" % \
177 (
" ".join(list(self._field.typemap.registry.keys())))
178 raise FieldValidationError(self._field, self._config, msg)
179 if self._field.multi:
181 for c
in self._selection:
182 retvals.append(self._field.typemap.registry[c](*args, config=self[c], **kw))
185 return self._field.typemap.registry[self.name](*args, config=self[self.name], **kw)
188 if attr ==
"registry":
189 object.__setattr__(self, attr, value)
191 ConfigInstanceDict.__setattr__(self, attr, value)
195 instanceDictClass = RegistryInstanceDict
197 def __init__(self, doc, registry, default=None, optional=False, multi=False):
200 ConfigChoiceField.__init__(self, doc, types, default, optional, multi)
203 """Customize deep-copying, want a reference to the original registry.
204 WARNING: this must be overridden by subclasses if they change the
205 constructor signature!
207 other = type(self)(doc=self.doc, registry=self.
registry,
208 default=copy.deepcopy(self.default),
209 optional=self.optional, multi=self.multi)
210 other.source = self.source
215 """A convenience function to create a new registry.
217 The returned value is an instance of a trivial subclass of Registry whose only purpose is to
218 customize its doc string and set attrList.
220 cls = type(
"Registry", (Registry,), {
"__doc__": doc})
221 return cls(configBaseType=configBaseType)
225 """A decorator that adds a class as a configurable in a Registry.
227 If the 'ConfigClass' argument is None, the class's ConfigClass attribute will be used.
230 registry.register(name, target=cls, ConfigClass=ConfigClass)
236 """A decorator that adds a class as a ConfigClass in a Registry, and associates it with the given
240 registry.register(name, target=target, ConfigClass=cls)