def test_make_relative(): """Test make relative path.""" p = ElementPath('catalog.groups') path = pathlib.Path('catalog/groups/controls') assert p.make_relative(path) == 1 path = pathlib.Path('profiles/controls') assert p.make_relative(path) == 1
def perform_split( cls, effective_cwd: pathlib.Path, file_name: str, elements: str, trestle_root: pathlib.Path ) -> int: """Perform the split operation. Args: effective_cwd: effective directory in which the the split operation is performed file_name: file name of model to split, or '' if deduced from elements and cwd elements: comma separated list of paths to strip from the file, with quotes removed Returns: 0 on success and 1 on failure """ file_path_list: List[Tuple[str, str]] = [] if file_name: file_path_list.append((file_name, elements)) else: # cwd must be in the model directory if file to split is not specified # find top directory for this model based on trestle root and cwd model_dir = file_utils.extract_project_model_path(effective_cwd) if model_dir is None: raise TrestleError('Current directory must be within a model directory if file is not specified') content_type: FileContentType = FileContentType.dir_to_content_type(model_dir) # determine the file needed for each split path element_paths = elements.split(',') for path in element_paths: element_path = ElementPath(path) # if element path is relative use directory context to determine absolute path element_path.make_absolute(model_dir, effective_cwd) file_path = element_path.find_last_file_in_path(content_type, model_dir) # now make the element path relative to the model file to be loaded if file_path is None or element_path.make_relative(file_path.relative_to(model_dir)) != 0: raise TrestleError(f'Unable to match element path with files in model directory {element_path}') file_path_list.append((file_path, element_path.to_string())) # match paths to corresponding files since several paths may be split from the same file file_path_dict: Dict[str, str] = {} for file_path in file_path_list: key = file_path[0] path = file_path[1] if key not in file_path_dict: file_path_dict[key] = path else: current_path = file_path_dict[key] file_path_dict[key] = f'{current_path},{path}' for raw_file_name, element_path in file_path_dict.items(): file_path = file_utils.relative_resolve(pathlib.Path(raw_file_name), effective_cwd) # this makes assumptions that the path is relative. if not file_path.exists(): raise TrestleError(f'File {file_path} does not exist.') content_type = FileContentType.to_content_type(file_path.suffix) # find the base directory of the file base_dir = file_path.parent model_type, _ = ModelUtils.get_stripped_model_type(file_path, trestle_root) model: OscalBaseModel = model_type.oscal_read(file_path) if cmd_utils.split_is_too_fine(element_path, model): raise TrestleError('Cannot split the model to the level of uuids, strings, etc.') # use the model itself to resolve any wildcards and create list of element paths logger.debug(f'split calling parse_element_args on {element_path}') # use contextual mode to parse element_paths: List[ElementPath] = cmd_utils.parse_element_args( model, element_path.split(','), base_dir.relative_to(trestle_root) ) # analyze the split tree and determine which aliases should be stripped from each file aliases_to_strip = cls.find_aliases_to_strip(element_paths) # need the file name relative to the base directory file_name_no_path = str(file_path.name) split_plan = cls.split_model( model, element_paths, base_dir, content_type, file_name_no_path, aliases_to_strip ) trash.store(file_path, True) try: split_plan.execute() except Exception as e: trash.recover(file_path, True) raise TrestleError(f'Split has failed with error: {e}.') return CmdReturnCodes.SUCCESS.value