def addresses(self): return self.dependencies def __eq__(self, other): if type(self) != type(other): return False return self.address == other.address def __ne__(self, other): return not (self == other) def __hash__(self): return hash(self.address) HydratedTargets = Collection.of(HydratedTarget) @rule(HydratedTargets, [ SelectTransitive(HydratedTarget, BuildFileAddresses, field_types=(Address, ), field='addresses') ]) def transitive_hydrated_targets(targets): """Recursively requests HydratedTargets, which will result in an eager, transitive graph walk.""" return HydratedTargets(targets) class HydratedField(datatype('HydratedField', ['name', 'value'])): """A wrapper for a fully constructed replacement kwarg for a HydratedTarget."""
class HydratedTargets(Collection.of(HydratedTarget)): """An intransitive set of HydratedTarget objects."""
def _recursive_dirname(f): """Given a relative path like 'a/b/c/d', yield all ascending path components like: 'a/b/c/d' 'a/b/c' 'a/b' 'a' '' """ while f: yield f f = dirname(f) yield '' BuildFilesCollection = Collection.of(BuildFiles) def create_graph_rules(address_mapper, symbol_table): """Creates tasks used to parse Structs from BUILD files. :param address_mapper_key: The subject key for an AddressMapper instance. :param symbol_table: A SymbolTable instance to provide symbols for Address lookups. """ symbol_table_constraint = symbol_table.constraint() return [ TaskRule(BuildFilesCollection, [SelectDependencies(BuildFiles, BuildDirs, field_types=(Dir,))], BuildFilesCollection), # A singleton to provide the AddressMapper. SingletonRule(AddressMapper, address_mapper),
""" def __eq__(self, other): if type(self) != type(other): return False return self.address == other.address def __ne__(self, other): return not (self == other) def __hash__(self): return hash(self.address) # TODO: Only used (currently) to represent transitive hydrated targets. Consider renaming. HydratedTargets = Collection.of(HydratedTarget) class HydratedField(datatype('HydratedField', ['name', 'value'])): """A wrapper for a fully constructed replacement kwarg for a HydratedTarget.""" def hydrate_target(target_adaptor, hydrated_fields): """Construct a HydratedTarget from a TargetAdaptor and hydrated versions of its adapted fields.""" # Hydrate the fields of the adaptor and re-construct it. kwargs = target_adaptor.kwargs() for field in hydrated_fields: kwargs[field.name] = field.value return HydratedTarget(target_adaptor.address, TargetAdaptor(**kwargs), tuple(target_adaptor.dependencies))
def files(self): return [p for p in self.path_stats if type(p.stat) == File] @property def file_stats(self): return [p.stat for p in self.files] def __repr__(self): return '''Snapshot(fingerprint='{}', entries={})'''.format( hexlify(self.fingerprint)[:8], len(self.path_stats)) def __str__(self): return repr(self) FilesContent = Collection.of(FileContent) @rule(Snapshot, [Select(PathGlobs)]) def snapshot_noop(*args): raise Exception( 'This task is replaced intrinsically, and should never run.') @rule(FilesContent, [Select(Snapshot)]) def files_content_noop(*args): raise Exception( 'This task is replaced intrinsically, and should never run.') def create_fs_rules():
return stats def files_content(files, file_values): entries = tuple(FileContent(f.path, f_value.content) for f, f_value in zip(files.dependencies, file_values)) return FilesContent(entries) def files_digest(files, file_values): entries = tuple(FileDigest(f.path, f_value.digest) for f, f_value in zip(files.dependencies, file_values)) return FilesDigest(entries) FilesContent = Collection.of(FileContent) FilesDigest = Collection.of(FileDigest) def generate_fs_subjects(filenames): """Given filenames, generate a set of subjects for invalidation predicate matching.""" for f in filenames: # ReadLink, FileContent, or DirectoryListing for the literal path. yield File(f) yield Link(f) yield Dir(f) # Additionally, since the FS event service does not send invalidation events # for the root directory, treat any changed file in the root as an invalidation # of the root's listing. if dirname(f) in ('.', ''): yield Dir('')
class Specs(Collection.of(Spec)): """A collection of Spec subclasses."""
def test_mismatching_paths(self): with self.assertRaises(DifferingFamiliesError): AddressFamily.create('one', [AddressMap('/dev/null/one/0', {}), AddressMap('/dev/null/two/0', {})]) def test_duplicate_names(self): with self.assertRaises(DuplicateNameError): AddressFamily.create('name/space', [AddressMap('name/space/0', {'one': Thing(name='one', age=42)}), AddressMap('name/space/1', {'one': Thing(name='one', age=37)})]) UnhydratedStructs = Collection.of(UnhydratedStruct) class AddressMapperTest(unittest.TestCase, SchedulerTestBase): def setUp(self): # Set up a scheduler that supports address mapping. symbol_table_cls = TargetTable address_mapper = AddressMapper(symbol_table_cls=symbol_table_cls, parser_cls=JsonParser, build_patterns=('*.BUILD.json',)) rules = create_fs_rules() + create_graph_rules(address_mapper, symbol_table_cls) # TODO handle updating the rule graph when passed unexpected root selectors. # Adding the following task allows us to get around the fact that SelectDependencies # requests are not currently supported. rules.append(TaskRule(UnhydratedStructs, [SelectDependencies(UnhydratedStruct,
@property def files(self): return [p for p in self.path_stats if type(p.stat) == File] @property def file_stats(self): return [p.stat for p in self.files] def __repr__(self): return '''Snapshot(fingerprint='{}', entries={})'''.format(hexlify(self.fingerprint)[:8], len(self.path_stats)) def __str__(self): return repr(self) FilesContent = Collection.of(FileContent) @rule(Snapshot, [Select(PathGlobs)]) def snapshot_noop(*args): raise Exception('This task is replaced intrinsically, and should never run.') @rule(FilesContent, [Select(Snapshot)]) def files_content_noop(*args): raise Exception('This task is replaced intrinsically, and should never run.') def create_fs_rules(): """Creates rules that consume the intrinsic filesystem types.""" return [
def _recursive_dirname(f): """Given a relative path like 'a/b/c/d', yield all ascending path components like: 'a/b/c/d' 'a/b/c' 'a/b' 'a' '' """ while f: yield f f = dirname(f) yield '' BuildFilesCollection = Collection.of(BuildFiles) def create_graph_rules(address_mapper, symbol_table_cls): """Creates tasks used to parse Structs from BUILD files. :param address_mapper_key: The subject key for an AddressMapper instance. :param symbol_table_cls: A SymbolTable class to provide symbols for Address lookups. """ symbol_table_constraint = symbol_table_cls.constraint() return [ TaskRule(BuildFilesCollection, [SelectDependencies(BuildFiles, BuildDirs, field_types=(Dir,))], BuildFilesCollection), # A singleton to provide the AddressMapper. SingletonRule(AddressMapper, address_mapper),
def test_mismatching_paths(self): with self.assertRaises(DifferingFamiliesError): AddressFamily.create('one', [AddressMap('/dev/null/one/0', {}), AddressMap('/dev/null/two/0', {})]) def test_duplicate_names(self): with self.assertRaises(DuplicateNameError): AddressFamily.create('name/space', [AddressMap('name/space/0', {'one': Thing(name='one', age=42)}), AddressMap('name/space/1', {'one': Thing(name='one', age=37)})]) UnhydratedStructs = Collection.of(UnhydratedStruct) class AddressMapperTest(unittest.TestCase, SchedulerTestBase): def setUp(self): # Set up a scheduler that supports address mapping. symbol_table_cls = TargetTable address_mapper = AddressMapper(symbol_table_cls=symbol_table_cls, parser_cls=JsonParser, build_patterns=('*.BUILD.json',)) tasks = create_graph_rules(address_mapper, symbol_table_cls) # TODO handle updating the rule graph when passed unexpected root selectors. # Adding the following task allows us to get around the fact that SelectDependencies # requests are not currently supported. tasks.append(TaskRule(UnhydratedStructs, [SelectDependencies(UnhydratedStruct,
def files_content(files, file_values): entries = tuple( FileContent(f.path, f_value.content) for f, f_value in zip(files.dependencies, file_values)) return FilesContent(entries) def files_digest(files, file_values): entries = tuple( FileDigest(f.path, f_value.digest) for f, f_value in zip(files.dependencies, file_values)) return FilesDigest(entries) FilesContent = Collection.of(FileContent) FilesDigest = Collection.of(FileDigest) def generate_fs_subjects(filenames): """Given filenames, generate a set of subjects for invalidation predicate matching.""" for f in filenames: # ReadLink, FileContent, or DirectoryListing for the literal path. yield File(f) yield Link(f) yield Dir(f) # Additionally, since the FS event service does not send invalidation events # for the root directory, treat any changed file in the root as an invalidation # of the root's listing. if dirname(f) in ('.', ''): yield Dir('')
'a/b/c/d' 'a/b/c' 'a/b' 'a' '' """ while f: yield f f = dirname(f) yield '' # TODO: This is a bit of a lie: `Struct` is effectively abstract, so this collection # will contain subclasses of `Struct` for the symbol table types. These APIs need more # polish before we make them public: see #4535 in particular. HydratedStructs = Collection.of(Struct) BuildFilesCollection = Collection.of(BuildFiles) def create_graph_rules(address_mapper, symbol_table): """Creates tasks used to parse Structs from BUILD files. :param address_mapper_key: The subject key for an AddressMapper instance. :param symbol_table: A SymbolTable instance to provide symbols for Address lookups. """ symbol_table_constraint = symbol_table.constraint() return [ TaskRule( BuildFilesCollection, [SelectDependencies(BuildFiles, BuildDirs, field_types=(Dir, ))],