def orange_files(event): """beautify one or more files at c.p.""" c = event.get('c') if not c or not c.p: return t1 = time.process_time() tag = 'beautify-files' g.es(f"{tag}...") settings = orange_settings(c) roots = g.findRootsWithPredicate(c, c.p) n_changed = 0 for root in roots: filename = g.fullPath(c, root) if os.path.exists(filename): print('') print(f"{tag}: {g.shortFileName(filename)}") changed = leoAst.Orange(settings=settings).beautify_file(filename) if changed: n_changed += 1 changed_s = 'changed' if changed else 'unchanged' g.es(f"{changed_s:>9}: {g.shortFileName(filename)}") else: print('') print(f"{tag}: file not found:{filename}") g.es(f"{tag}: file not found:\n{filename}") t2 = time.process_time() print('') g.es_print(f"total files: {len(roots)}, " f"changed files: {n_changed}, " f"in {t2 - t1:5.2f} sec.")
def fstringify_files_silent(event): """Silently fstringifying the external files at c.p.""" c = event.get('c') if not c or not c.p: return t1 = time.process_time() tag = 'silent-fstringify-files' g.es(f"{tag}...") n_changed = 0 roots = g.findRootsWithPredicate(c, c.p) for root in roots: filename = g.fullPath(c, root) if os.path.exists(filename): changed = leoAst.Fstringify().fstringify_file_silent(filename) if changed: n_changed += 1 else: print('') print(f"File not found:{filename}") g.es(f"File not found:\n{filename}") t2 = time.process_time() print('') n_tot = len(roots) g.es_print(f"{n_tot} total file{g.plural(len(roots))}, " f"{n_changed} changed file{g.plural(n_changed)} " f"in {t2 - t1:5.2f} sec.")
def orange_diff_files(event): """ Show the diffs that would result from beautifying the external files at c.p. """ c = event.get('c') if not c or not c.p: return t1 = time.process_time() tag = 'beautify-files-diff' g.es(f"{tag}...") settings = orange_settings(c) roots = g.findRootsWithPredicate(c, c.p) for root in roots: filename = g.fullPath(c, root) if os.path.exists(filename): print('') print(f"{tag}: {g.shortFileName(filename)}") changed = leoAst.Orange( settings=settings).beautify_file_diff(filename) changed_s = 'changed' if changed else 'unchanged' g.es(f"{changed_s:>9}: {g.shortFileName(filename)}") else: print('') print(f"{tag}: file not found:{filename}") g.es(f"file not found:\n{filename}") t2 = time.process_time() print('') g.es_print( f"{tag}: {len(roots)} file{g.plural(len(roots))} in {t2 - t1:5.2f} sec." )
def run(self): '''Run Pylint on all Python @<file> nodes in c.p's tree.''' c, root = self.c, self.c.p if not self.import_lint(): return self.rc_fn = self.get_rc_file() if not self.rc_fn: return # Make sure Leo is on sys.path. leo_path = g.os_path_finalize_join(g.app.loadDir, '..') if leo_path not in sys.path: sys.path.append(leo_path) # Ignore @nopylint trees. def predicate(p): for parent in p.self_and_parents(): if g.match_word(parent.h, 0, '@nopylint'): return False return p.isAnyAtFileNode() and p.h.strip().endswith('.py') roots = g.findRootsWithPredicate(c, root, predicate=predicate) data = [(self.get_fn(p), p.copy()) for p in roots] data = [z for z in data if z[0] is not None] if not data: g.es('pylint: no files found', color='red') return for fn, p in data: self.run_pylint(fn, p)
def run(self, p=None, force=False, pyflakes_errors_only=False): '''Run Pyflakes on all Python @<file> nodes in c.p's tree.''' c = self.c root = p or c.p # Make sure Leo is on sys.path. leo_path = g.os_path_finalize_join(g.app.loadDir, '..') if leo_path not in sys.path: sys.path.append(leo_path) t1 = time.time() def predicate(p): return p.isAnyAtFileNode() and p.h.strip().endswith('.py') roots = g.findRootsWithPredicate(c, root, predicate) if root: paths = [self.finalize(z) for z in roots] # These messages are important for clarity. log_flag = not force total_errors = self.check_all(log_flag, paths, pyflakes_errors_only) if total_errors > 0: g.es('ERROR: pyflakes: %s error%s' % (total_errors, g.plural(total_errors))) elif force: g.es('OK: pyflakes: %s file%s in %s' % (len(paths), g.plural(paths), g.timeSince(t1))) elif not pyflakes_errors_only: g.es('OK: pyflakes') ok = total_errors == 0 else: ok = True return ok
def run(self, p=None, force=False, pyflakes_errors_only=False): """Run Pyflakes on all Python @<file> nodes in c.p's tree.""" if not pyflakes: return True # Pretend all is fine. c = self.c root = p or c.p # Make sure Leo is on sys.path. leo_path = g.os_path_finalize_join(g.app.loadDir, '..') if leo_path not in sys.path: sys.path.append(leo_path) t1 = time.time() roots = g.findRootsWithPredicate(c, root, predicate=None) if roots: # These messages are important for clarity. log_flag = not force total_errors = self.check_all(log_flag, pyflakes_errors_only, roots) if total_errors > 0: g.es( f"ERROR: pyflakes: {total_errors} error{g.plural(total_errors)}" ) elif force: g.es(f"OK: pyflakes: " f"{len(roots)} file{g.plural(roots)} " f"in {g.timeSince(t1)}") elif not pyflakes_errors_only: g.es('OK: pyflakes') ok = total_errors == 0 else: ok = True return ok
def find_missing_docstrings(event): """Report missing docstrings in the log, with clickable links.""" c = event.get('c') if not c: return #@+others # Define functions #@+node:ekr.20190615181104.1: *4* function: has_docstring def has_docstring(lines, n): """ Returns True if function/method/class whose definition starts on n-th line in lines has a docstring """ # By Виталије Милошевић. for line in lines[n:]: s = line.strip() if not s or s.startswith('#'): continue if s.startswith(('"""', "'''")): return True return False #@+node:ekr.20190615181104.2: *4* function: is_a_definition def is_a_definition(line): """Return True if line is a definition line.""" # By Виталије Милошевић. # It may be useful to skip __init__ methods because their docstring # is usually docstring of the class return (line.startswith(('def ', 'class ')) and not line.partition(' ')[2].startswith('__init__')) #@+node:ekr.20190615182754.1: *4* function: is_root def is_root(p): """ A predicate returning True if p is an @<file> node that is not under @nopylint. """ for parent in p.self_and_parents(): if g.match_word(parent.h, 0, '@nopylint'): return False return p.isAnyAtFileNode() and p.h.strip().endswith('.py') #@-others count, files, found, t1 = 0, 0, [], time.process_time() for root in g.findRootsWithPredicate(c, c.p, predicate=is_root): files += 1 for p in root.self_and_subtree(): lines = p.b.split('\n') for i, line in enumerate(lines): if is_a_definition(line) and not has_docstring(lines, i): count += 1 if root.v not in found: found.append(root.v) g.es_print('') g.es_print(root.h) print(line) g.es_clickable_link(c, p, i + 1, line) # *Local* index. break g.es_print('') g.es_print(f"found {count} missing docstring{g.plural(count)} " f"in {files} file{g.plural(files)} " f"in {time.process_time() - t1:5.2f} sec.")
def run(self, p=None, force=False, pyflakes_errors_only=False): '''Run Pyflakes on all Python @<file> nodes in c.p's tree.''' c = self.c root = p or c.p # Make sure Leo is on sys.path. leo_path = g.os_path_finalize_join(g.app.loadDir, '..') if leo_path not in sys.path: sys.path.append(leo_path) t1 = time.time() roots = g.findRootsWithPredicate(c, root, predicate=None) if root: paths = [self.finalize(z) for z in roots] # These messages are important for clarity. log_flag = not force total_errors = self.check_all(log_flag, paths, pyflakes_errors_only, roots=roots) if total_errors > 0: g.es('ERROR: pyflakes: %s error%s' % ( total_errors, g.plural(total_errors))) elif force: g.es('OK: pyflakes: %s file%s in %s' % ( len(paths), g.plural(paths), g.timeSince(t1))) elif not pyflakes_errors_only: g.es('OK: pyflakes') ok = total_errors == 0 else: ok = True return ok
def command_helper(self, event, kind, preview, verbose): def predicate(p): return self.filename(p) # Find all roots. t1 = time.time() c = self.c self.kind = kind p = event.p if event and hasattr(event, 'p') else c.p roots = g.findRootsWithPredicate(c, p, predicate=predicate) if not roots: g.warning('No @adoc nodes in', p.h) return [] # Write each root to a file. i_paths = [] for p in roots: try: i_path = self.filename(p) # #1398. i_path = c.expand_path_expression(i_path) i_path = g.os_path_finalize(i_path) with open(i_path, 'w', encoding='utf-8', errors='replace') as self.output_file: self.write_root(p) i_paths.append(i_path) except IOError: g.es_print(f"Can not open {i_path!r}") except Exception: g.es_print(f"Unexpected exception opening {i_path!r}") g.es_exception() # Convert each file to html. o_paths = [] for i_path in i_paths: o_path = self.compute_opath(i_path) o_paths.append(o_path) if kind == 'adoc': self.run_asciidoctor(i_path, o_path) elif kind == 'pandoc': self.run_pandoc(i_path, o_path) elif kind == 'sphinx': self.run_sphinx(i_path, o_path) else: g.trace('BAD KIND') return None if kind != 'sphinx': print(f"{kind}: wrote {o_path}") if preview: if kind == 'sphinx': g.es_print('preview not available for sphinx') else: # open .html files in the default browser. g.execute_shell_commands(o_paths) t2 = time.time() if verbose: n = len(i_paths) g.es_print(f"{kind}: wrote {n} file{g.plural(n)} " f"in {(t2-t1):4.2f} sec.") return i_paths
def run(self, p): """Run mypy on all Python @<file> nodes in c.p's tree.""" c = self.c root = p.copy() # Make sure Leo is on sys.path. leo_path = g.os_path_finalize_join(g.app.loadDir, '..') if leo_path not in sys.path: sys.path.append(leo_path) roots = g.findRootsWithPredicate(c, root, predicate=None) self.check_all(roots)
def processTopTree(self, p): """Call processTree for @rst and @slides node p's subtree or p's ancestors.""" def predicate(p): return self.is_rst_node(p) or g.match_word(p.h, 0, '@slides') roots = g.findRootsWithPredicate(self.c, p, predicate=predicate) if roots: for p in roots: self.processTree(p) else: g.warning('No @rst or @slides nodes in', p.h)
def run(self): '''Run Pylint on all Python @<file> nodes in c.p's tree.''' c, root = self.c, self.c.p rc_fn = self.get_rc_file() if not rc_fn: return # Make sure Leo is on sys.path. leo_path = g.os_path_finalize_join(g.app.loadDir, '..') if leo_path not in sys.path: sys.path.append(leo_path) roots = g.findRootsWithPredicate(c, root, predicate=None) for p in roots: self.check(p, rc_fn)
def run(self, p): """Run mypy on all Python @<file> nodes in c.p's tree.""" c = self.c if not mypy: print('install mypy with `pip install mypy`') return root = p.copy() # Make sure Leo is on sys.path. leo_path = g.os_path_finalize_join(g.app.loadDir, '..') if leo_path not in sys.path: sys.path.append(leo_path) roots = g.findRootsWithPredicate(c, root, predicate=None) g.printObj([z.h for z in roots], tag='mypy.run') self.check_all(roots)
def run_pylint(self, fn, rc_fn): '''Run pylint on fn with the given pylint configuration file.''' c = self.c try: from pylint import lint assert lint # to molify pyflakes except ImportError: if not self.pylint_install_message: self.pylint_install_message = True g.es_print('pylint is not installed') return if not os.path.exists(fn): g.es_print('pylint: file not found:', fn) return if 1: # Invoke pylint directly. # Escaping args is harder here because we are creating an args array. is_win = sys.platform.startswith('win') args = ','.join([ "'--rcfile=%s'" % (rc_fn), "'%s'" % (fn), ]) if is_win: args = args.replace('\\', '\\\\') command = '%s -c "from pylint import lint; args=[%s]; lint.Run(args)"' % ( sys.executable, args) if not is_win: command = shlex.split(command) else: # Invoke g.run_pylint. args = [ "fn=r'%s'" % (fn), "rc=r'%s'" % (rc_fn), ] # When shell is True, it's recommended to pass a string, not a sequence. command = '%s -c "import leo.core.leoGlobals as g; g.run_pylint(%s)"' % ( sys.executable, ','.join(args)) # # Run the command using the BPM. bpm = g.app.backgroundProcessManager roots = g.findRootsWithPredicate(c, c.p, predicate=None) bpm.start_process( c, command, fn=fn, kind='pylint', link_pattern=r'^\w+:(.*),.*:(.*)$', link_root=roots and roots[0], )
def run(self): '''Run Pylint on all Python @<file> nodes in c.p's tree.''' c, root = self.c, self.c.p rc_fn = self.get_rc_file() if not rc_fn: return # Make sure Leo is on sys.path. leo_path = g.os_path_finalize_join(g.app.loadDir, '..') if leo_path not in sys.path: sys.path.append(leo_path) t1 = time.time() roots = g.findRootsWithPredicate(c, root, predicate=None) for p in roots: self.check(p, rc_fn) if self.wait: g.es_print('pylint done %s' % g.timeSince(t1))
def run(self): '''Run Pylint on all Python @<file> nodes in c.p's tree.''' c, root = self.c, self.c.p rc_fn = self.get_rc_file() if not rc_fn: return # Make sure Leo is on sys.path. leo_path = g.os_path_finalize_join(g.app.loadDir, '..') if leo_path not in sys.path: sys.path.append(leo_path) roots = g.findRootsWithPredicate(c, root, predicate=None) if roots: for p in roots: self.check(p, rc_fn) else: g.es('pylint: no files found', color='red')
def blacken_files(event): """Run black on one or more files at c.p.""" c = event.get('c') if not c or not c.p: return tag = 'blacken-files' if not black: g.es_print(f"{tag} can not import black") return for root in g.findRootsWithPredicate(c, c.p): path = g.fullPath(c, root) if path and os.path.exists(path): g.es_print(f"{tag}: {path}") g.execute_shell_commands(f"&black --skip-string-normalization {path}") else: print(f"{tag}: file not found:{path}") g.es(f"{tag}: file not found:\n{path}")
def run(self): '''Run Pylint on all Python @<file> nodes in c.p's tree.''' c, root = self.c, self.c.p rc_fn = self.get_rc_file() if not rc_fn: return # Make sure Leo is on sys.path. leo_path = g.os_path_finalize_join(g.app.loadDir, '..') if leo_path not in sys.path: sys.path.append(leo_path) t1 = time.time() def predicate(p): return p.isAnyAtFileNode() and p.h.strip().endswith('.py') roots = g.findRootsWithPredicate(c, root, predicate) for p in roots: self.check(p, rc_fn) if self.wait: g.es_print('pylint done %s' % g.timeSince(t1))
def flake8_command(event): """ Run flake8 on all nodes of the selected tree, or the first @<file> node in an ancestor. """ tag = 'flake8-files' if not flake8: g.es_print(f"{tag} can not import flake8") return c = event and event.get('c') if not c or not c.p: return python = sys.executable for root in g.findRootsWithPredicate(c, c.p): path = g.fullPath(c, root) if path and os.path.exists(path): g.es_print(f"{tag}: {path}") g.execute_shell_commands(f'&"{python}" -m flake8 "{path}"') else: g.es_print(f"{tag}: file not found:{path}")
def blacken_files_diff(event): """ Show the diffs that would result from blacking the external files at c.p. """ c = event.get('c') if not c or not c.p: return tag = 'blacken-files-diff' if not black: g.es_print(f"{tag} can not import black") return for root in g.findRootsWithPredicate(c, c.p): path = g.fullPath(c, root) if path and os.path.exists(path): g.es_print(f"{tag}: {path}") g.execute_shell_commands(f"&black --skip-string-normalization --diff {path}") else: print(f"{tag}: file not found:{path}") g.es(f"{tag}: file not found:\n{path}")
def run_pylint(self, fn, rc_fn): '''Run pylint on fn with the given pylint configuration file.''' c = self.c try: from pylint import lint assert lint # to molify pyflakes except ImportError: if not self.pylint_install_message: self.pylint_install_message = True g.es_print('pylint is not installed') return if not os.path.exists(fn): g.es_print('pylint: file not found:', fn) return if 1: # Invoke pylint directly. # Escaping args is harder here because we are creating an args array. is_win = sys.platform.startswith('win') args = ','.join(["'--rcfile=%s'" % (rc_fn), "'%s'" % (fn),]) if is_win: args = args.replace('\\','\\\\') command = '%s -c "from pylint import lint; args=[%s]; lint.Run(args)"' % ( sys.executable, args) if not is_win: command = shlex.split(command) else: # Invoke g.run_pylint. args = ["fn=r'%s'" % (fn), "rc=r'%s'" % (rc_fn),] # When shell is True, it's recommended to pass a string, not a sequence. command = '%s -c "import leo.core.leoGlobals as g; g.run_pylint(%s)"' % ( sys.executable, ','.join(args)) # # Run the command using the BPM. bpm = g.app.backgroundProcessManager roots = g.findRootsWithPredicate(c, c.p, predicate=None) bpm.start_process(c, command, fn=fn, kind='pylint', link_pattern = r'^\w+:(.*),.*:(.*)$', link_root = roots and roots[0], )
def run(self, last_path=None): """Run Pylint on all Python @<file> nodes in c.p's tree.""" c, root = self.c, self.c.p if not lint: g.es_print('pylint is not installed') return False self.rc_fn = self.get_rc_file() if not self.rc_fn: return False # Make sure Leo is on sys.path. leo_path = g.os_path_finalize_join(g.app.loadDir, '..') if leo_path not in sys.path: sys.path.append(leo_path) # Ignore @nopylint trees. def predicate(p): for parent in p.self_and_parents(): if g.match_word(parent.h, 0, '@nopylint'): return False return p.isAnyAtFileNode() and p.h.strip().endswith( ('.py', '.pyw')) # #2354. roots = g.findRootsWithPredicate(c, root, predicate=predicate) data = [(self.get_fn(p), p.copy()) for p in roots] data = [z for z in data if z[0] is not None] if not data and last_path: # Default to the last path. fn = last_path for p in c.all_positions(): if p.isAnyAtFileNode() and g.fullPath(c, p) == fn: data = [(fn, p.copy())] break if not data: g.es('pylint: no files found', color='red') return None for fn, p in data: self.run_pylint(fn, p) # #1808: return the last data file. return data[-1] if data else False
def run(self, p): """Run Pyflakes on all Python @<file> nodes in p's tree.""" ok = True if not pyflakes: return ok c = self.c root = p # Make sure Leo is on sys.path. leo_path = g.os_path_finalize_join(g.app.loadDir, '..') if leo_path not in sys.path: sys.path.append(leo_path) roots = g.findRootsWithPredicate(c, root, predicate=None) if roots: # These messages are important for clarity. total_errors = self.check_all(roots) if total_errors > 0: g.es( f"ERROR: pyflakes: {total_errors} error{g.plural(total_errors)}" ) ok = total_errors == 0 else: ok = True return ok
def find_missing_docstrings(event): '''Report missing docstrings in the log, with clickable links.''' c = event.get('c') if not c: return #@+others # Define functions #@+node:ekr.20190615181104.1: *4* function: has_docstring def has_docstring(lines, n): ''' Returns True if function/method/class whose definition starts on n-th line in lines has a docstring ''' # By Виталије Милошевић. for line in lines[n:]: s = line.strip() if not s or s.startswith('#'): continue if s.startswith(('"""', "'''")): return True return False #@+node:ekr.20190615181104.2: *4* function: is_a_definition def is_a_definition(line): '''Return True if line is a definition line.''' # By Виталије Милошевић. # It may be useful to skip __init__ methods because their docstring # is usually docstring of the class return (line.startswith(('def ', 'class ')) and not line.partition(' ')[2].startswith('__init__')) #@+node:ekr.20190615182754.1: *4* function: is_root def is_root(p): ''' A predicate returning True if p is an @<file> node that is not under @nopylint. ''' for parent in p.self_and_parents(): if g.match_word(parent.h, 0, '@nopylint'): return False return p.isAnyAtFileNode() and p.h.strip().endswith('.py') #@+node:ekr.20190615180900.1: *4* function: clickable_link def clickable_link(p, i): '''Return a clickable link to line i of p.b.''' link = p.get_UNL(with_proto=True, with_count=True, with_index=True) return "%s,%d" % (link, i) #@-others count, found, t1 = 0, [], time.clock() for root in g.findRootsWithPredicate(c, c.p, predicate=is_root): for p in root.self_and_subtree(): lines = p.b.split('\n') for i, line in enumerate(lines): if is_a_definition(line) and not has_docstring(lines, i): count += 1 if root.v not in found: found.append(root.v) g.es_print('') g.es_print(root.h) print(line) g.es(line, nodeLink=clickable_link(p, i + 1)) break g.es_print('') g.es_print('found %s missing docstring%s in %s file%s in %5.2f sec.' % (count, g.plural(count), len(found), g.plural(len(found)), (time.clock() - t1)))
def ad_command(self, event=None): #@+<< adoc command docstring >> #@+node:ekr.20190515115100.1: *3* << adoc command docstring >> ''' The adoc command writes all @adoc nodes in the selected tree to the files given in each @doc node. If no @adoc nodes are found, the command looks up the tree. Each @adoc node should have the form: `@adoc x.adoc`. Relative file names are relative to the base directory. See below. By default, the adoc command creates AsciiDoctor headings from Leo headlines. However, the following kinds of nodes are treated differently: - @ignore-tree: Ignore the node and its descendants. - @ignore-node: Ignore the node. - @no-head: Ignore the headline. Do not generate a heading. After running the adoc command, use the asciidoctor command to convert the x.adoc files to x.html. Settings -------- AsciiDoctor itself provides many settings, including:: = Title :stylesdir: mystylesheets/ :stylesheet: mystyles.css These can also be specified on the command line:: asciidoctor -a stylesdir=mystylesheets/ -a stylesheet=mystyles.css @string adoc-base-directory specifies the base for relative file names. The default is c.frame.openDirectory Scripting interface ------------------- Scripts may invoke the adoc command as follows:: event = g.Bunch(base_dicrectory=my_directory, p=some_node) c.asciiDoctorCommands.ad_command(event=event) This @button node runs the adoc command and coverts all results to .html:: import os paths = c.asciiDoctorCommands.ad_command(event=g.Bunch(p=p)) paths = [z.replace('/', os.path.sep) for z in paths] input_paths = ' '.join(paths) g.execute_shell_commands(['asciidoctor %s' % input_paths]) ''' #@-<< adoc command docstring >> def predicate(p): return self.ad_filename(p) # Find all roots. t1 = time.time() c = self.c p = event.p if event and hasattr(event, 'p') else c.p if event and hasattr(event, 'base_directory'): self.base_directory = event.base_directory else: directory = c.config.getString('adoc-base-directory') self.base_directory = directory or c.frame.openDirectory roots = g.findRootsWithPredicate(c, p, predicate=predicate) if not roots: g.warning('No @ascii-doctor nodes in', p.h) return [] # Write each root. paths = [] for p in roots: path = self.write_root(p) if path: paths.append(path) t2 = time.time() g.es_print('adoc: wrote %s file%s in %4.2f sec.' % ( len(paths), g.plural(len(paths)), t2 - t1)) return paths