def __init__(self, params): """ Constructor Args: params (dict): parameters sourcedir (string,required): path to source directory patchdir (string,required): path to patch directory targets (string/list, optional): target file(s) indent (int, optional): indentation default = 3 mode (string, optional): scanning mode 'full' : report edit errors only 'complete' : report status of all edits default is 'full' find (bool, optional): find missing strings default is True debug (int, optional) debug options default is 0 Raises: PT_ParameterError Notes: If '' is passed as sourcedir or patchdir, the caller must supply file paths to the match method that are accessible from the caller's current dircectory. If 'targets' is specified the code will only scan diff sections that modify the filenames in params['targets] """ #-- self.name = 'Checker' if ((params is None) or (not isinstance(params, dict))): raise PT_ParameterError(self.name, 'params') self.sourcedir = self._check_required_string_param(params, 'sourcedir') self._check_path_param('sourcedir', self.sourcedir) self.patchdir = self._check_required_string_param(params, 'patchdir') self._check_path_param('patchdir', self.patchdir) self.indent = self._check_optional_string_param(params, 'indent', ' ') self.mode = self._check_optional_string_param(params, 'mode', 'full') self.find = self._check_optional_param(params, 'find', bool, False) self.debug = self._check_optional_param(params, 'debug', int, 0) if (not self.mode in ('full', 'complete')): raise PT_ParameterError(self.name, 'mode') if ('targets' in params): targets = params['targets'] if (ut.is_string_type(targets)): self.targets = [targets] elif (isinstance(targets, list) and ut.is_string_type(targets[0])): self.targets = targets else: raise PT_ParameterError(self.name, 'targets') else: self.targets = None
def __init__(self, params): """ Constructor Args: params (dict): parameters match (list, optional): string match pattern(s) prefix (list, optional): string start pattern(s) suffix (list, optional): string end pattern(s) substr (list, optional): substring pattern(s) regexp (list, optional): regular expression pattern(s) funcs (list, optional): callback function(s) Raises: PT_ParameterError on invalid parameters Notes: At least one option must be specified for the filter to have an effect. Regular expression pattern strings should be coded using the r"..." string form. """ #-- self.name = 'Matcher' if (not isinstance(params, dict)): raise PT_ParameterError(self.name, 'params') self.prefix_patterns = self._check_optional_param( params, 'prefix', list, None) self.suffix_patterns = self._check_optional_param( params, 'suffix', list, None) self.substr_patterns = self._check_optional_param( params, 'substr', list, None) self.match_patterns = self._check_optional_param( params, 'match', list, None) regexp = self._check_optional_param(params, 'regexp', list, None) if (isinstance(regexp, list)): try: self.regex_patterns = [re.compile(s) for s in regexp] except Exception as e: raise PT_ParameterError(self.name, str(e)) else: self.regex_patterns = None if ('funcs' in params): cbs = params['funcs'] for cb in cbs: if (not isfunction(cb)): raise PatchToolsError(self.name, 'callback must be a function') self.callbacks = cbs else: self.callbacks = None
def match(self, params): """ Report matches by file to (patterns) found in selected files Args: filter (dict): Filter parameters Returns: A list of matches in the format specified above Raises: PT_ParameterError Notes: See the Filter object for a description of Filter parameters. """ #-- if (not isinstance(params, dict)): raise PT_ParameterError(self.name, 'params') self.matcher = Matcher(params) self.matches = {} if (self.mode == 'file'): for path in self.file_paths: self._match_files(path) matches = self._list_by_file() else: for path in self.file_paths: self._match_pattern(path) matches = self._list_by_pattern() return matches
def vp2f(self, patchname, params): """ List patch and files it uses Args: patchname (string): name of patch file params (dict): parameters patchdir (string, required): path of patches folder Raises: PT_ParameterError for any missing files """ #-- if (not isinstance(params, dict)): raise PT_ParameterError(self.name, 'params') sourcedir = self._check_directory_param(params, 'sourcedir') patchdir = self._check_directory_param(params, 'patchdir') patchpath = self._check_filename_param(patchdir, patchname, 'patchname') # Get list of source filenames referenced in patch # Note that a patch may refer to files that do not exist in the source tree. filelist = Patch.list_files(patchpath) filepaths = [] for file_ in filelist: path = ut.join_path(sourcedir, file_) if (ut.is_file(path)): filepaths += [path] paths = [patchpath] + filepaths return self._view(paths)
def sort_patches(self, params): """ Reorder patches to match the order in the patch set Args: patches (list, required): list of patchname strings order (string, optional): sort order 'patchset' = sort patches by the order in patchset groups 'date' = sort patches by "Author Date:" default is 'patchset' Returns: The list in the specified order """ patches = self._check_required_param(params, 'patches', list) order = self._check_optional_param(params, 'order', str, 'patchset') if (order == 'patchset'): if (self.format == 'new'): return self._sort_patches_new(patches) else: return self._sort_patches_old(patches) elif (order == 'date'): return self._sort_on_date(patches) else: raise PT_ParameterError(self.name, 'order')
def __init__(self, path): """ Constructor Args: path (string): path to patch archive file Raises: PT_ParameterError, PT_NotFoundError Notes: A "patch archive" file lists diff sections from patches that were applied to produce the associated kernel version. Since the patch archive files can be very large, we take care to avoid copying or storing data that is not of interest to the user. """ #-- self.name = 'Archive' if (not ut.is_string_type(path)): raise PT_ParameterError(self.name, path) if (not ut.is_file(path)): raise PT_NotFoundError(self.name, path) self._path = path
def __init__(self, params): """ Constructor Args: params (dict): parameters root_path (string, required): path of file tree root file_paths (list, required): relative paths of files to search options (string, optional): display format 'terse' show count of matching lines 'compact' show line numbers of matching lines 'full' show line number and text of matching lines 'complete' also show matching pattern 'match' list only matching text default is 'full' mode (string, optional) search mode 'file' report results by file 'pattern' report results by pattern trim_paths (bool, optional): remove root portion of paths from returned paths default is True Raises: PT_ParameterError PT_NotFoundError """ #-- self.name = 'Finder' if ((params is None) or (not isinstance(params, dict))): raise PT_ParameterError(self.name, 'params') self.root_path = self._check_required_string_param(params, 'root_path') self._check_path_param('root_path', self.root_path) self.options = self._check_optional_string_param( params, 'options', 'full') if (self.options not in ('full', 'compact', 'complete', 'match', 'terse')): raise PT_ParameterError(self.name, 'options param') self.mode = self._check_optional_string_param(params, 'mode', 'file') self.file_paths = self._check_required_param(params, 'file_paths', list) self.trim_paths = self._check_optional_param(params, 'trim_paths', bool, True) self.debug = self._check_optional_param(params, 'debug', int, 0)
def _check_file_1_arg(self, name, value): ''' Validate file argument when only one file is allowed. ''' if (ut.is_string_type(value)): return value else: raise PT_ParameterError(self.name, name)
def _check_filename_param(self, prefix, value, name): if (not ut.is_string_type(value)): raise PT_ParameterError(self.name, name) path = ut.join_path(prefix, value) if (not ut.is_file(path)): raise PT_NotFoundError(self.name, value) return path
def _check_file_params(self, params): if (('file' in params) and ut.is_string_type(params['file'])): return [params['file']] elif (('files' in params) and isinstance(params['files'], list)): return params['files'] else: raise PT_ParameterError(self.name, 'filespec')
def _check_required_string_param(self, params, field): if (field in params): param = params[field] if (ut.is_string_type(param)): return param else: raise PT_TypeError(self.name, field, 'str') else: raise PT_ParameterError(self.name, field)
def _check_file_N_args(self, name, value): ''' Validate file argument when N files are allowed. ''' if (ut.is_string_type(value)): return [value] elif (isinstance(value, list)): return value else: raise PT_ParameterError(self.name, name)
def _check_required_param(self, params, field, types): if (field in params): param = params[field] if (isinstance(param, types)): return param else: PT_TypeError(self.name, field, types) else: raise PT_ParameterError(self.name, field)
def __init__(self, params=None): """ Constructor Args: params (dict, optional): parameters editor (dict, optional): editor specification target (string, required): name or path for target program multifile (bool, required): can open multiple files in a single invocation multiview (bool, required): can display multiple files in a single window wait (bool, optional): wait for subprocess to exit default is False Raises: PT_ParameterError Notes: If editor is not specified, the default editor for the host system is used. The default editor for Linux is gedit. The default editor for Windows is write (aka 'WordPad'). If any method is called in a loop, the wait option should be True. """ #-- self.name = 'Viewer' if ((params is not None) and (not isinstance(params, dict))): raise PT_ParameterError(self.name, 'params') if (isinstance(params, dict)): editor = self._check_optional_param(params, 'editor', dict, None) else: editor = None is_windows = ut.is_windows() if (editor is not None): self.target = self._check_required_string_param(editor, 'target') self.multifile = self._check_required_param( editor, 'multifile', bool) self.multiview = self._check_required_param( editor, 'multiview', bool) elif (is_windows): self.target = 'write' self.multifile = False # check this self.multiview = False else: self.target = 'gedit' self.multifile = True self.multiview = True if (is_windows): self.cmd_sep = ' && ' else: self.cmd_sep = ' ; ' self.command = Command()
def vp2a(self, patchname, archpath, params): """ Display patch and archive diff sections that use its files Args: patchname (string): name of patch file archpath (string): path to archive file params (dict): parameters patchdir (string, required): path of patches folder tempdir (string, required): path to store temporary data Raises: PT_ParameterError for any missing files Notes: Launches the editor synchronously, since a temporary file is created to hold the archive sections. """ #-- if (not isinstance(params, dict)): raise PT_ParameterError(self.name, 'params') sourcedir = self._check_directory_param(params, 'sourcedir') patchdir = self._check_directory_param(params, 'patchdir') tempdir = self._check_directory_param(params, 'tempdir') patchpath = self._check_filename_param(patchdir, patchname, 'patchname') if (not ut.is_file(archpath)): raise PT_NotFoundError(self.name, archpath) # Get list of files used by the patch filelist = Patch.list_files(patchpath) filepaths = [ut.join_path(sourcedir, file_) for file_ in filelist] # Get archive diff sections that use any of the files strings = [] for diff in self._get_diffs(archpath, filepaths): strings += diff if (len(strings) == 0): return [] # Store the diff sections in a temporary file archname = archpath[archpath.rfind('/') + 1:] tempname = archname + '.tmp' temppath = ut.join_path(tempdir, tempname) ut.write_strings(strings, temppath) self._view([patchpath, temppath], True) # Note that we requested _view to wait for subprocess exit above, # so that we do not try to delete the file while it is in use. os.remove(temppath)
def match(self, param): """ Validate the contents of one or more Linux kernel patch files against a kernel Args: param (choice): (string): path to patch file (list): paths to patch files Returns: A list of strings describing the results of analysis Raises: PT_ParameterError PT_ParsingError """ #-- if (ut.is_string_type(param)): paths = [param] elif (isinstance(param, (list, tuple))): paths = param else: raise PT_ParameterError(self.name, param) self.msgs = [] self._misc_msg('patchdir = "%s":' % self.patchdir) self._misc_msg('sourcedir = "%s":' % self.sourcedir) passed = skipped = 0 for path in paths: if (self.debug > 0): print("matching %s" % path) errors = self._check(path) if (errors == 0): passed += 1 elif (errors < 0): skipped += 1 self._misc_msg('\nSummary:') self._misc_msg( '%d passed, %d skipped, %d tested' % (passed, skipped, len(paths)), 1) return self.msgs
def __init__(self, path): """ Constructor Args: path (string): path to patch file Raises: PT_ParameterError PT_NotFoundError Notes: Commented out diff and hunk sections are omitted. """ #-- self.name = 'Patch' if (not ut.is_string_type(path)): raise PT_ParameterError(self.name, path) if (not ut.is_file(path)): raise PT_NotFoundError(self.name, path) strings = Strings(ut.read_strings(path)) strings = strings.discard('"""', '"""') # Split any email header from the patch data (_, body) = strings.partition('diff -') if (body is None): #all diffs commented out? self.diffs = [] self.patch_type = 'text' self.patch_mode = 'git' print "body is none" elif (body[0].startswith('diff --git ')): # Split any email footer from the patch data (body, _) = body.rpartition('-- ') self._parse_body(body, 'diff --git ') self.patch_mode = 'git' print "git diff" else: self._parse_body(body, 'diff -') self.patch_mode = 'urn' print "urn"
def __init__(self, strings): """ Constructor Args: strings (Strings): diff section from a patch file or archive file Raises: PT_ParameterError """ #-- self.name = 'Diff' if ((strings is None) or (not isinstance(strings, list))): raise PT_ParameterError(self.name, 'strings') (head, body) = strings.partition('@@ ') self._parse_head(head) self._parse_body(body)
def __init__(self, params): """ Constructor Args: params (dict): parameters patchdir (string, required): path to patch directory patchset (dict, required): description of patches in patchdir Raises: PT_ParameterError """ #-- self.name = 'PatchSet' if ((params is None) or (not isinstance(params, dict))): raise PT_ParameterError(self.name, 'params') self.patchdir = self._check_required_string_param(params, 'patchdir') self.patchset = self._check_required_param(params, 'patchset', dict) groups = self.patchset['groups'] if (groups[0] in self.patchset): # old format self.format = 'old' else: self.format = 'new' self.filedata = None self.patchdata = None self.month_numbers = { 'Jan': 1, 'Feb': 2, 'Mar': 3, 'Apr': 4, 'May': 5, 'Jun': 6, 'Jul': 7, 'Aug': 8, 'Sep': 9, 'Oct': 10, 'Nov': 11, 'Dec': 12 }
def __init__(self, params): """ Constructor Args: params (dict): parameters patchdir (string, required): path to patch folder sourcedir (string, required): path to source folder datadir (string, required): path to data folder tempdir (string, required): path to temp file folder patchset (dict, required): description of patches Raises: PT_ParameterError PT_NotFoundError Notes: Experience in testing shows a very low probability that an archive diff section will match any of our patches exactly, so we merely display the related files. """ #-- self.name = 'Watcher' if ((params is None) or (not isinstance(params, dict))): raise PT_ParameterError(self.name, 'params') self._patchdir = self._check_required_string_param(params, 'patchdir') self._check_path_param('patchdir', self._patchdir) self._sourcedir = self._check_required_string_param(params, 'sourcedir') self._check_path_param('sourcedir', self._sourcedir) self._datadir = self._check_required_string_param(params, 'datadir') self._check_path_param('datadir', self._datadir) self._tempdir = self._check_required_string_param(params, 'tempdir') self._check_path_param('tempdir', self._tempdir) self._patchset = PatchSet(params)
def vp2p(self, patchname, params): """ Display a patch file and the other patch files that use the same source files Args: patchname (string): name of patch file params (dict): parameters patchdir (string, required): path of patches folder patchset (dict, required): patchset description Raises: PT_ParameterError for any missing files """ #-- if (not isinstance(params, dict)): raise PT_ParameterError(self.name, 'params') patchdir = self._check_directory_param(params, 'patchdir') patchset = self._check_required_param(params, 'patchset', dict) patchpath = self._check_filename_param(patchdir, patchname, 'patchname') # Get list of files used by the patch filelist = Patch.list_files(patchpath) # Get list of patches that use the same files patchlist = [] patchset = PatchSet({'patchdir': patchdir, 'patchset': patchset}) patchdata = patchset.get_file_data() patchkeys = sorted(patchdata) # 2to3 for file_ in filelist: for key in patchkeys: if (file_ == patchdata[key][0]): patchlist += [key] break patchpaths = [ut.join_path(patchdir, patch) for patch in patchlist] paths = [patchpath] + patchpaths return self._view(paths)
def view(self, files): """ Launch specified editor to view the file(s) Args: files (list): paths of files to display check_files(bool): verify target files exist Raises: PT_ParameterError for any missing files """ #-- if (not isinstance(files, list)): raise PT_ParameterError(self.name, 'files') # The user may specify a file that does not exist paths = [] for path in files: if (ut.is_file(path)): paths += [path] return self._view(paths)
def vcpf(self, checkpath, patchname, params): """ List checker output file, patch and files it uses Args: checkpath (string): path to Checker output file patchname (string): patch name params (dict) parameters: sourcedir (string, required): path of sources folder patchdir (string, required): path of patches folder Raises: PT_ParameterError PT_NotFoundError """ #-- if (not isinstance(params, dict)): raise PT_ParameterError(self.name, 'params') if (not ut.is_file(checkpath)): raise PT_NotFoundError(self.name, checkpath) sourcedir = self._check_directory_param(params, 'sourcedir') patchdir = self._check_directory_param(params, 'patchdir') patchpath = self._check_filename_param(patchdir, patchname, 'patchname') # Get list of source filenames referenced in patch. # Note that a patch may refer to files that do not exist in the source tree. filelist = Patch.list_files(patchpath) filepaths = [] for file_ in filelist: path = ut.join_path(sourcedir, file_) if (ut.is_file(path)): filepaths += [path] paths = [checkpath, patchpath] + filepaths return self._view(paths)
def watch(self, archpath): """ View files related to archive diff sections Args: archpath (string): path to patch archive file Returns: None. Output is a series of launches of the Viewer to view the files. Raises: PT_ParameterError PT_NotFoundError """ #-- if (not ut.is_string_type(archpath)): raise PT_ParameterError(self.name, 'archpath') if (not ut.is_file(archpath)): raise PT_NotFoundError(self.name, archpath) tempfile = ut.join_path(self._tempdir, 'archdata.txt') filedata = self._patchset.get_file_data() filenames = [key for key in filedata] a = Archive(archpath) s = a.sections(filenames) print("Found %d matching sections" % len(s)) v = Viewer() for section in s: ut.write_strings(section, tempfile) filename = ut.get_string_filename(section[1]) filepath = ut.join_path(self._sourcedir, filename) patchfiles = [] for (fn, _) in filedata[filename]: patchfiles += [ut.join_path(self._patchdir, fn)] r = v.view([tempfile, filepath] + patchfiles) print(r)
def vf2p(self, filename, params): """ List file and patches that use it Args: filename (string): name of patch file params (dict): parameters sourcedir (string, required): path of sources folder patchdir (string, required): path of patches folder patchset (dict, required): patchset description Raises: PT_ParameterError for any missing files """ if (not isinstance(params, dict)): raise PT_ParameterError(self.name, 'params') sourcedir = self._check_directory_param(params, 'sourcedir') patchdir = self._check_directory_param(params, 'patchdir') patchset = self._check_required_param(params, 'patchset', dict) filepath = self._check_filename_param(sourcedir, filename, 'filename') # Get list of patches that use the source file patchset = PatchSet({'patchdir': patchdir, 'patchset': patchset}) patchlist = patchset.get_file_patches(filename) # Arrange the patches in patchset group order. #patchlist = patchset.sort_patches({ "patches" : patchlist }) # Create abolute paths for the patches patchpaths = [] for patchname in patchlist: patchpaths += [ut.join_path(patchdir, patchname)] filepaths = [filepath] + patchpaths return self._view(filepaths)
def __init__(self, params): """ Constructor Args: params (dict, required) parameters: root_path (string, required): search root path incl_dirs (list, optional): top level subdirs to include excl_dirs (list, optional): top level subdirs to exclude incl_files (dict, optional): include file name filters excl_files (dict, optional): exclude file name filters test_dirs (bool, optional): True = include dir in tests Raises: PT_ParameterError Notes: The "root_path" parameter specifies the root of the search. If the "incl_dirs" option is specified, only those subdirs of the root will be searched. If the "excl_dirs" option is specified, those subdirs of the root will not be searched. If the "incl_files" option is specified, only those filetypes will be enumerated. If the "excl_files" option is specified, those filetypes will not be enumerated. If it is desired to search a folder that is a subfolder of an excluded folder, the search must be split into two operations. When callback functions need the containing directory to test a file name, the 'test_dirs' option should be set to True Walking a large file tree can take significant time and produce a large amount of data, but this tendency can be reduced by cleaning the tree of generated files beforehand, and by applying suitable directory and file filters. """ #-- self.name = 'Walker' if ((params is None) or (not isinstance(params, dict))): raise PT_ParameterError(self.name, 'params') self._root_path = self._check_required_string_param(params, 'root_path') self._check_path_param('root_path', self._root_path) incl_dirs = self._check_optional_param(params, 'incl_dirs', list, None) excl_dirs = self._check_optional_param(params, 'excl_dirs', list, None) incl_files = self._check_optional_param(params, 'incl_files', dict, None) excl_files = self._check_optional_param(params, 'excl_files', dict, None) self.test_dirs = self._check_optional_param(params, 'test_dirs', bool, False) self._trim_paths = self._check_optional_param(params, 'trim_paths', bool, True) if ((incl_dirs is None) and (excl_dirs is None)): self._folders = [self._root_path] elif (incl_dirs is not None): # only iterate over incl_dirs self._folders = [ut.join_path(self._root_path, d) for d in incl_dirs] else: # exclude subdirs in excl_dirs dirs = [d for d in os.listdir(self._root_path) if os.path.isdir(ut.join_path(self._root_path, d))] for x in excl_dirs: if x in dirs: dirs.remove(x) self._folders = [ut.join_path(self._root_path, d) for d in dirs] if (incl_files is not None): self._incl_files_filter = Matcher(incl_files) else: self._incl_files_filter = None if (excl_files is not None): self._excl_files_filter = Matcher(excl_files) else: self._excl_files_filter = None
def _check_path_param(self, name, value): if (not ut.is_dir(value)): raise PT_ParameterError(self.name, name, value)