def __init__(self, abstract=False, extends=None, merges=None, **kwargs): """Creates a new configuration data blob. By default configurations are anonymous (un-named), concrete (not `abstract`), and they neither inherit nor merge another configuration. Inheritance is only allowed via one of the `extends` or `merges` channels, it is an error to specify both. A configuration can be semantically abstract without setting `abstract=True`. The `abstract` value can serve as documentation, or, for subclasses that provide an implementation for `validate_concrete`, it allows skipping validation for abstract instances. :param bool abstract: `True` to mark this configuration item as abstract, in which case no validation is performed (see `validate_concrete`); `False` by default. :param extends: The configuration instance to inherit field values from. Any shared fields are over-written with this instances values. :type extends: An addressed or concrete configuration instance that is a type compatible with this configuration or this configurations superclasses. :param merges: The configuration instance to merge this instances field values with. Merging is like extension except for containers, which are extended instead of replaced; ie: any `dict` values are updated with this instances items and any `list` values are extended with this instances items. :type merges: An addressed or concrete configuration instance that is a type compatible with this configuration or this configurations superclasses. :param **kwargs: The configuration parameters. """ self._kwargs = kwargs self._kwargs['abstract'] = abstract # It only makes sense to inherit a subset of our own fields (we should not inherit new fields!), # our superclasses logically provide fields within this constrained set. # NB: Since Configuration is at base an ~unconstrained struct, a superclass does allow for # arbitrary and thus more fields to be defined than a subclass might logically support. We # accept this hole in a trade for generally expected behavior when Configuration is subclassed # in the style of constructors with named parameters representing the full complete set of # expected parameters leaving **kwargs only for use by 'the system'; ie for `typename` and # `address` plumbing for example. self._kwargs['extends'] = addressable(SuperclassesOf(type(self)), extends) self._kwargs['merges'] = addressable(SuperclassesOf(type(self)), merges) # Allow for configuration items that are directly constructed in memory. These can have an # address directly assigned (vs. inferred from name + source file location) and we only require # that if they do, their name - if also assigned, matches the address. if self.address: if self.name and self.name != self.address.target_name: self.report_validation_error( 'Address and name do not match! address: {}, name: {}'. format(self.address, self.name)) self._kwargs['name'] = self.address.target_name self._hashable_key = None
def __init__(self, abstract=False, extends=None, merges=None, **kwargs): """Creates a new configuration data blob. By default configurations are anonymous (un-named), concrete (not `abstract`), and they neither inherit nor merge another configuration. Inheritance is only allowed via one of the `extends` or `merges` channels, it is an error to specify both. A configuration can be semantically abstract without setting `abstract=True`. The `abstract` value can serve as documentation, or, for subclasses that provide an implementation for `validate_concrete`, it allows skipping validation for abstract instances. :param bool abstract: `True` to mark this configuration item as abstract, in which case no validation is performed (see `validate_concrete`); `False` by default. :param extends: The configuration instance to inherit field values from. Any shared fields are over-written with this instances values. :type extends: An addressed or concrete configuration instance that is a type compatible with this configuration or this configurations superclasses. :param merges: The configuration instance to merge this instances field values with. Merging is like extension except for containers, which are extended instead of replaced; ie: any `dict` values are updated with this instances items and any `list` values are extended with this instances items. :type merges: An addressed or concrete configuration instance that is a type compatible with this configuration or this configurations superclasses. :param **kwargs: The configuration parameters. """ self._kwargs = kwargs self._kwargs['abstract'] = abstract # It only makes sense to inherit a subset of our own fields (we should not inherit new fields!), # our superclasses logically provide fields within this constrained set. # NB: Since Configuration is at base an ~unconstrained struct, a superclass does allow for # arbitrary and thus more fields to be defined than a subclass might logically support. We # accept this hole in a trade for generally expected behavior when Configuration is subclassed # in the style of constructors with named parameters representing the full complete set of # expected parameters leaving **kwargs only for use by 'the system'; ie for `typename` and # `address` plumbing for example. self._kwargs['extends'] = addressable(SuperclassesOf(type(self)), extends) self._kwargs['merges'] = addressable(SuperclassesOf(type(self)), merges) # Allow for configuration items that are directly constructed in memory. These can have an # address directly assigned (vs. inferred from name + source file location) and we only require # that if they do, their name - if also assigned, matches the address. if self.address: if self.name and self.name != self.address.target_name: self.report_validation_error('Address and name do not match! address: {}, name: {}' .format(self.address, self.name)) self._kwargs['name'] = self.address.target_name self._hashable_key = None
def __init__(self, default_repo, repos, name=None, **kwargs): super(PublishConfiguration, self).__init__(name=name, default_repo=addressable(Exactly(Configuration), default_repo), repos=addressable_mapping(Exactly(Configuration), repos), **kwargs)
def __init__(self, default_repo, repos, name=None, **kwargs): super(PublishConfiguration, self).__init__(name=name, default_repo=addressable(Exactly(Configuration), default_repo), repos=addressable_mapping(Exactly(Configuration), repos), **kwargs)
@property def path_globs(self): """Creates a `PathGlobs` object for the files held by these Sources. This field may be projected to request the content of the files for this Sources object. """ return PathGlobs.create(self.spec_path, files=self.files, globs=self.globs, rglobs=self.rglobs, zglobs=self.zglobs) @abstractproperty def extensions(self): """A collection of file extensions collected by this Sources instance. An empty collection indicates that any extension will be accepted. """ @property def excludes(self): """The sources to exclude. :rtype: :class:`Sources` """ # Since Sources.excludes is recursive on the Sources type, we need to post-class-definition # re-define excludes in this way. Sources.excludes = addressable(Exactly(Sources))(Sources.excludes)
for spec, fileset_wrapper_type in ((self.globs, Globs), (self.rglobs, RGlobs), (self.zglobs, ZGlobs)): if spec: fileset = fileset_wrapper_type(base_path)(spec) yield fileset for rel_path in itertools.chain.from_iterable(file_sources()): if select(rel_path): file_path = os.path.join(base_path, rel_path) if file_path not in excluded_files: yield file_path # Since Sources.excludes is recursive on the Sources type, we need to post-class-definition # re-define excludes in this way. Sources.excludes = addressable(Exactly(Sources))(Sources.excludes) class Target(Configuration): """TODO(John Sirois): XXX DOCME""" class ConfigurationNotFound(Exception): """Indicates a requested configuration of a target could not be found.""" def __init__(self, name=None, sources=None, configurations=None, dependencies=None, **kwargs): """ :param string name: The name of this target which forms its address in its namespace. :param sources: The relative source file paths of sources this target owns. :type sources: :class:`Sources` :param list configurations: The configurations that apply to this target in various contexts. :param list dependencies: The direct dependencies of this target.
def test_type_mismatch(self): with self.assertRaises(AddressedError): addressable(Exactly(int), 42.0)
def test_pointer(self): self.assertEqual(Addressed(Exactly(int), '//:meaning-of-life'), addressable(Exactly(int), '//:meaning-of-life'))
def test_value(self): self.assertEqual(42, addressable(Exactly(int), 42))
def test_none(self): self.assertIsNone(addressable(Exactly(int), None))
def test_type_mismatch(self): with self.assertRaises(AddressedError): addressable(Exactly(int), 42.0)
def test_pointer(self): self.assertEqual(Addressed(Exactly(int), '//:meaning-of-life'), addressable(Exactly(int), '//:meaning-of-life'))
def test_value(self): self.assertEqual(42, addressable(Exactly(int), 42))
def test_none(self): self.assertIsNone(addressable(Exactly(int), None))