def create(cls, spec_path: str, address_maps: Iterable[AddressMap]) -> "AddressFamily": """Creates an address family from the given set of address maps. :param spec_path: The directory prefix shared by all address_maps. :param address_maps: The family of maps that form this namespace. :raises: :class:`MappingError` if the given address maps do not form a family. """ if spec_path == ".": spec_path = "" for address_map in address_maps: if not address_map.path.startswith(spec_path): raise DifferingFamiliesError( f"Expected AddressMaps to share the same parent directory {spec_path!r}, " f"but received: {address_map.path!r}") name_to_target_adaptors: Dict[str, Tuple[str, TargetAdaptor]] = {} for address_map in address_maps: for name, target_adaptor in address_map.name_to_target_adaptor.items( ): if name in name_to_target_adaptors: previous_path, _ = name_to_target_adaptors[name] raise DuplicateNameError( f"A target with name {name!r} is already defined in {previous_path!r}, but" f"is also defined in {address_map.path!r}. Because both targets share the " f"same namespace of {spec_path!r}, this is not allowed." ) name_to_target_adaptors[name] = (address_map.path, target_adaptor) return AddressFamily( namespace=spec_path, name_to_target_adaptors=dict( sorted(name_to_target_adaptors.items())), )
def parse(cls, filepath: str, filecontent: bytes, parser: Parser) -> "AddressMap": """Parses a source for addressable Serializable objects. No matter the parser used, the parsed and mapped addressable objects are all 'thin'; ie: any objects they point to in other namespaces or even in the same namespace but from a separate source are left as unresolved pointers. :param filepath: The path to the byte source containing serialized objects. :param filecontent: The content of byte source containing serialized objects to be parsed. :param parser: The parser cls to use. """ try: objects = parser.parse(filepath, filecontent) except Exception as e: raise MappingError(f"Failed to parse {filepath}:\n{e!r}") objects_by_name: Dict[str, ThinAddressableObject] = {} for obj in objects: if not Serializable.is_serializable(obj): raise UnaddressableObjectError("Parsed a non-serializable object: {!r}".format(obj)) attributes = obj._asdict() name = attributes.get("name") if not name: raise UnaddressableObjectError("Parsed a non-addressable object: {!r}".format(obj)) if name in objects_by_name: raise DuplicateNameError( "An object already exists at {!r} with name {!r}: {!r}. Cannot " "map {!r}".format(filepath, name, objects_by_name[name], obj) ) objects_by_name[name] = obj return cls(filepath, dict(sorted(objects_by_name.items())))
def parse( cls, filepath: str, build_file_content: str, parser: Parser, extra_symbols: BuildFilePreludeSymbols, ) -> "AddressMap": """Parses a source for targets. The target adaptors are all 'thin': any targets they point to in other namespaces or even in the same namespace but from a separate source are left as unresolved pointers. """ try: target_adaptors = parser.parse(filepath, build_file_content, extra_symbols) except Exception as e: raise MappingError(f"Failed to parse {filepath}:\n{e}") name_to_target_adaptors: Dict[str, TargetAdaptor] = {} for target_adaptor in target_adaptors: name = target_adaptor.name if name in name_to_target_adaptors: duplicate = name_to_target_adaptors[name] raise DuplicateNameError( f"A target already exists at {filepath!r} with name {name!r} and target type " f"{duplicate.type_alias!r}. The {target_adaptor.type_alias!r} target " "cannot use the same name.") name_to_target_adaptors[name] = target_adaptor return cls(filepath, dict(sorted(name_to_target_adaptors.items())))
def create(cls, spec_path: str, address_maps: Iterable[AddressMap]) -> "AddressFamily": """Creates an address family from the given set of address maps. :param spec_path: The directory prefix shared by all address_maps. :param address_maps: The family of maps that form this namespace. :raises: :class:`MappingError` if the given address maps do not form a family. """ if spec_path == ".": spec_path = "" for address_map in address_maps: if not address_map.path.startswith(spec_path): raise DifferingFamiliesError( "Expected AddressMaps to share the same parent directory {}, " "but received: {}".format(spec_path, address_map.path)) objects_by_name: Dict[str, Tuple[str, ThinAddressableObject]] = {} for address_map in address_maps: current_path = address_map.path for name, obj in address_map.objects_by_name.items(): previous = objects_by_name.get(name) if previous: previous_path, _ = previous raise DuplicateNameError( "An object with name {name!r} is already defined in " "{previous_path!r}, will not overwrite with {obj!r} from " "{current_path!r}.".format( name=name, previous_path=previous_path, obj=obj, current_path=current_path, )) objects_by_name[name] = (current_path, obj) return AddressFamily( namespace=spec_path, objects_by_name={ name: (path, obj) for name, (path, obj) in sorted(objects_by_name.items()) }, )