Source code for dfimagetools.path_resolver

# -*- coding: utf-8 -*-
"""Helper for resolving paths."""

import logging


[docs] class PathResolver(object): """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', None) 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 "{path:s}" ' f'not supported.')) elif '**' in path_segment: logging.warning(( f'Globstar with prefix "{path_segment:s}" in path "{path:s}" not ' f'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}" exceeds ' f'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)