Source code for dfimagetools.path_resolver

"""Helper for resolving paths."""

import logging


[docs] class PathResolver: """Path resolver.""" _GLOBSTAR_RECURSION_LIMIT = 10 _PATH_EXPANSIONS_PER_USERS_VARIABLE = { "%%users.appdata%%": [ ["%%users.userprofile%%", "AppData", "Roaming"], ["%%users.userprofile%%", "Application Data"], ], "%%users.localappdata%%": [ ["%%users.userprofile%%", "AppData", "Local"], ["%%users.userprofile%%", "Local Settings", "Application Data"], ], "%%users.localappdata_low%%": [ ["%%users.userprofile%%", "AppData", "LocalLow"] ], "%%users.temp%%": [["%%users.localappdata%%", "Temp"]], } _USER_DIRECTORY_VARIABLES = ("%%users.homedir%%", "%%users.userprofile%%") _WINDOWS_DRIVE_INDICATORS = ("%%environ_systemdrive%%", "%systemdrive%") def _CreateEnvironmentVariablesLookupTable(self, environment_variables): """Creates an environment variables lookup table. Args: environment_variables (list[EnvironmentVariable]): environment variables. Returns: dict[str, str]: environment variables lookup table. """ lookup_table = {} for environment_variable in environment_variables or []: attribute_value = environment_variable.value if not isinstance(attribute_value, str): continue # Make the attribute name is in upper case and without the leading and # trailing %-characters. attribute_name = environment_variable.name.upper() if ( len(attribute_name) >= 2 and attribute_name[0] == "%" and attribute_name[-1] == "%" ): attribute_name = attribute_name[1:-1] lookup_table[attribute_name] = attribute_value if "ALLUSERSAPPDATA" not in lookup_table: program_data = lookup_table.get("PROGRAMDATA") if program_data: lookup_table["ALLUSERSAPPDATA"] = program_data return lookup_table def _ExpandEnvironmentVariablesInPathSegments( self, path_segments, environment_variables ): """Expands environment variables in path segments. Args: path_segments (list[str]): path segments with environment variables. environment_variables (list[EnvironmentVariable]): environment variables. Returns: list[str]: path segments with environment variables expanded. """ if environment_variables is None: environment_variables = [] lookup_table = self._CreateEnvironmentVariablesLookupTable( environment_variables ) # Make a copy of path_segments since this loop can change it. for index, path_segment in enumerate(list(path_segments)): if ( len(path_segment) <= 2 or not path_segment[0] == "%" or not path_segment[-1] == "%" ): continue path_segment_upper_case = path_segment.upper() if path_segment_upper_case.startswith("%%ENVIRON_"): lookup_key = path_segment_upper_case[10:-2] else: lookup_key = path_segment_upper_case[1:-1] path_segment = lookup_table.get(lookup_key, path_segment) path_segment = path_segment.split("\\") expanded_path_segments = list(path_segments[:index]) expanded_path_segments.extend(path_segment) expanded_path_segments.extend(path_segments[index + 1 :]) path_segments = expanded_path_segments if self._IsWindowsDrivePathSegment(path_segments[0]): path_segments[0] = "" return path_segments def _ExpandUserDirectoryVariableInPathSegments( self, path_segments, path_separator, user_accounts ): """Expands a user directory variable in path segments. This method expands an artifact definition user directory variable such as %%users.homedir%% or %%users.userprofile%%. Args: path_segments (list[str]): path segments. path_separator (str): path segment separator. user_accounts (list[UserAccount]): user accounts. Returns: list[str]: paths returned for user accounts without a drive indicator. """ if not path_segments: return [] user_paths = [] first_path_segment = path_segments[0].lower() if first_path_segment not in self._USER_DIRECTORY_VARIABLES: if self._IsWindowsDrivePathSegment(path_segments[0]): path_segments[0] = "" user_path = path_separator.join(path_segments) user_paths.append(user_path) elif not user_accounts: # Default to "Documents and Settings", "home" and "Users" user_path = path_separator.join( ["", "Documents and Settings", "*"] + path_segments[1:] ) user_paths.append(user_path) user_path = path_separator.join(["", "home", "*"] + path_segments[1:]) user_paths.append(user_path) user_path = path_separator.join(["", "Users", "*"] + path_segments[1:]) user_paths.append(user_path) else: for user_account in user_accounts: if not user_account.user_directory: continue user_path_segments = user_account.user_directory.split( user_account.user_directory_path_separator ) if self._IsWindowsDrivePathSegment(user_path_segments[0]): user_path_segments[0] = "" # Prevent concatenating two consecutive path segment separators. if not user_path_segments[-1]: user_path_segments.pop() user_path_segments.extend(path_segments[1:]) user_path = path_separator.join(user_path_segments) user_paths.append(user_path) return user_paths def _ExpandUsersVariableInPathSegments( self, path_segments, path_separator, user_accounts ): """Expands a users variable in path segments. This method expands an artifact definition user variable such as %%users.appdata%% or %%users.temp%%. Args: path_segments (list[str]): path segments. path_separator (str): path segment separator. user_accounts (list[UserAccount]): user accounts. Returns: list[str]: paths for which the users variables have been expanded. """ if not path_segments: return [] path_segments_lower = [path_segment.lower() for path_segment in path_segments] if path_segments_lower[0] in self._USER_DIRECTORY_VARIABLES: return self._ExpandUserDirectoryVariableInPathSegments( path_segments, path_separator, user_accounts ) path_expansions = self._PATH_EXPANSIONS_PER_USERS_VARIABLE.get( path_segments[0], None ) if path_expansions: expanded_paths = [] for path_expansion in path_expansions: expanded_path_segments = list(path_expansion) expanded_path_segments.extend(path_segments[1:]) paths = self._ExpandUsersVariableInPathSegments( expanded_path_segments, path_separator, user_accounts ) expanded_paths.extend(paths) return expanded_paths if self._IsWindowsDrivePathSegment(path_segments[0]): path_segments[0] = "" # TODO: add support for %%users.username%% path = path_separator.join(path_segments) return [path] def _IsWindowsDrivePathSegment(self, path_segment): """Determines if the path segment contains a Windows Drive indicator. A drive indicator can be a drive letter, %SystemDrive% or the artifact definition environment variable %%environ_systemdrive%%. Args: path_segment (str): path segment. Returns: bool: True if the path segment contains a Windows Drive indicator. """ if ( len(path_segment) == 2 and path_segment[1] == ":" and path_segment[0].isalpha() ): return True path_segment_lower = path_segment.lower() return path_segment_lower in self._WINDOWS_DRIVE_INDICATORS
[docs] def ExpandEnvironmentVariables(self, path, path_separator, environment_variables): """Expands environment variables. Args: path (str): path with environment variables. path_separator (str): path segment separator. environment_variables (list[EnvironmentVariable]): environment variables. Returns: str: path with environment variables expanded. """ path_segments = path.split(path_separator) path_segments = self._ExpandEnvironmentVariablesInPathSegments( path_segments, environment_variables ) return path_separator.join(path_segments)
[docs] def ExpandGlobStars(self, path, path_separator): """Expands globstars "**". A globstar "**" will recursively match all files and zero or more directories and subdirectories. By default the maximum recursion depth is 10 subdirectories, a numeric values after the globstar, such as "**5", can be used to define the maximum recursion depth. Args: path (str): path with globstars. path_separator (str): path segment separator. Returns: str: path with seperate globs for every globstar. """ expanded_paths = [] path_segments = path.split(path_separator) last_segment_index = len(path_segments) - 1 for segment_index, path_segment in enumerate(path_segments): recursion_depth = None if path_segment.startswith("**"): if len(path_segment) == 2: recursion_depth = 10 else: try: recursion_depth = int(path_segment[2:], 10) except (TypeError, ValueError): logging.warning( ( f'Globstar with suffix "{path_segment:s}" in path ' f'"{path:s}" not supported.' ) ) elif "**" in path_segment: logging.warning( ( f'Globstar with prefix "{path_segment:s}" in path ' f'"{path:s}" not supported.' ) ) if recursion_depth is not None: if ( recursion_depth <= 1 or recursion_depth > self._GLOBSTAR_RECURSION_LIMIT ): logging.warning( ( f'Globstar "{path_segment:s}" in path "{path:s}" ' f"exceeds recursion maximum recursion depth, limiting to: " f"{self._GLOBSTAR_RECURSION_LIMIT:d}." ) ) recursion_depth = self._GLOBSTAR_RECURSION_LIMIT next_segment_index = segment_index + 1 for expanded_path_segment in [ ["*"] * depth for depth in range(1, recursion_depth + 1) ]: expanded_path_segments = list(path_segments[:segment_index]) expanded_path_segments.extend(expanded_path_segment) if next_segment_index <= last_segment_index: expanded_path_segments.extend( path_segments[next_segment_index:] ) expanded_path = path_separator.join(expanded_path_segments) expanded_paths.append(expanded_path) return expanded_paths or [path]
[docs] def ExpandUsersVariable(self, path, path_separator, user_accounts): """Expands a users variable, such as %%users.appdata%%. Args: path (str): path with users variable. path_separator (str): path segment separator. user_accounts (list[UserAccount]): user accounts. Returns: list[str]: paths for which the users variables have been expanded. """ path_segments = path.split(path_separator) return self._ExpandUsersVariableInPathSegments( path_segments, path_separator, user_accounts )