Last active
December 5, 2018 17:53
-
-
Save fish2000/103061d3242ce9075f63a7ef2ffbff07 to your computer and use it in GitHub Desktop.
Revisions
-
fish2000 revised this gist
Dec 5, 2018 . 1 changed file with 15 additions and 25 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -18,8 +18,7 @@ # locate these programs in the default python bin directory or to a full # path if the checker program is installed elsewhere. # - If for some reason you want to use the built-in sytax check when either # pychecker or pyflakes are installed, set TM_PYCHECKER to "builtin". from __future__ import absolute_import, print_function @@ -29,7 +28,7 @@ import traceback from html import escape __version__ = "2.0.2" PY3 = False if sys.version_info < (3, 0): @@ -61,16 +60,15 @@ def warning_link_urls(): """ Compose all formatted warning URLs in a user-facing message: """ one, two, three, four, five = format_warning_urls() return f""" <p class="warning">Please install {one}, {two}, {three}, {four} or {five} to enable extensive code checking.</p> """.strip() # patterns to match output of checker programs PYCHECKER_RE = re.compile(r"^(?:\s*)(.*?\.pyc?):(\d+):(?:\s*)(.*)(?:\s*)$") def textmate_url(file, line=None, column=None): """ Compose a Textmate callback URL, for sending the cursor to a location within an active Textmate buffer: """ url = f"txmt://open?url=file://{quote(file)}" if type(line) is int: url += f"&line={line}" @@ -185,26 +183,22 @@ def textmate_url(file, line=None, column=None): padding: 2px 0; } </style> </head> <body>""" HTML_HEADER_BODY = """ <div id="body"> <p><strong class="title">%s</strong></p> <p><strong>%s</strong></p> <br> <div id="output">""" HTML_FOOTER = """ </div> </div> </body> </html>""" CHECKERS = ["pychecker", "pyflakes", "pylint", "pep8", "flake8"] @@ -372,14 +366,12 @@ def run_checker_program(checker_bin, whitespace += "<br> " number = int(idx) + 1 print(f'''<div class="message"> <span class="number">{number:02}</span> <a href="{url}">{filename}:{lineno}</a> {whitespace} <span class="message-text">{message}</a> </div>''') idx += 1 @@ -405,11 +397,9 @@ def run_checker_program(checker_bin, returncode = 'NULL' p.terminate() print(f'''<div id="exit-status"> <br>Exit status: {returncode} </div>''') def main(script_path): checker_bin, checker_opts, checker_ver = find_checker_program() -
fish2000 revised this gist
Dec 5, 2018 . 1 changed file with 76 additions and 47 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -40,11 +40,29 @@ basestring = str unicode = str warning_urls = { "PyChecker" : "http://pychecker.sourceforge.net/", "PyFlakes" : "http://divmod.org/projects/pyflakes", "PyLint" : "http://www.logilab.org/857", "PEP-8" : "http://pypi.python.org/pypi/pep8", "Flake8" : "http://pypi.python.org/pypi/flake8/" } def format_warning_urls(): """ Format the warning URLs as necessary for TextMate to open them: """ out = [] for checker_name, checker_url in warning_urls.items(): out.append(f""" <a href="javascript:TextMate.system('open {checker_url}', null)">{checker_name}</a> """.strip()) return tuple(out) def warning_link_urls(): """ Compose all formatted warning URLs in a user-facing message: """ one, two, three, four, five = format_warning_urls() return f""" <p class="warning">Please install {one}, {two}, {three}, {four} or {five} for more extensive code checking.</p> """.strip() # patterns to match output of checker programs PYCHECKER_RE = re.compile(r"^(?:\s*)(.*?\.pyc?):(\d+):(?:\s*)(.*)(?:\s*)$") @@ -147,11 +165,33 @@ def textmate_url(file, line=None, column=None): } span.stderr { color: red; } p { margin: 0; } p.warning { padding: 0px; font-family: Consolas, Monaco; font-size: 13pt; border-top: 1px solid #333; border-bottom: 1px solid #333; border-left: 0px none; border-right: 0px none; margin: 1em; margin-left: 0; margin-right: 0; background-color: #EFEFEF; } div#output p { padding: 2px 0; } </style> </head> <body> """ HTML_HEADER_BODY = """ <div id="body"> <p><strong class="title">%s</strong></p> <p><strong>%s</strong></p> @@ -210,9 +250,8 @@ def check_syntax(script_path): except SyntaxError as e: url = textmate_url(script_path, int(e.lineno), int(e.offset)) script = escape(os.path.basename(script_path)) print(f'<a href="{url}">{script}:{e.lineno}</a> {e.msg}') except: for line in traceback.format_exception(sys.exc_info()): stripped = line.lstrip() @@ -286,7 +325,7 @@ def find_checker_program(): def run_checker_program(checker_bin, checker_opts, script_path, version_string): import subprocess basepath = os.getenv("TM_PROJECT_DIRECTORY") @@ -305,39 +344,43 @@ def run_checker_program(checker_bin, except subprocess.TimeoutExpired: p.kill() stdout, stderr = p.communicate(timeout=None) if stdout is None: stdout = b'' if stderr is None: stderr = b'' outlines = stdout.decode(UTF8_ENCODING).splitlines() issue_count = len(outlines) print(HTML_HEADER_BODY % (version_string, f'{issue_count} issues found')) idx = 0 for line in outlines: match = PYCHECKER_RE.search(line) if match: filename, lineno, message = match.groups() url = textmate_url(filename, int(lineno)) if basepath is not None and filename.startswith(basepath): filename = filename[len(basepath)+1:] # naive linewrapping, but it seems to work well-enough whitespace = "" if len(filename) + len(message) > 80: whitespace += "<br> " number = int(idx) + 1 print(f''' <div class="message"> <span class="number">{number:02}</span> <a href="{url}">{filename}:{lineno}</a> {whitespace} <span class="message-text">{message}</a> </div> ''') idx += 1 else: @@ -370,42 +413,28 @@ def run_checker_program(checker_bin, def main(script_path): checker_bin, checker_opts, checker_ver = find_checker_program() basepath = os.getenv("TM_PROJECT_DIRECTORY") version_string = f"PyCheckMate {__version__} – {checker_ver}" warning_string = "" if not checker_bin: warning_string += warning_link_urls() if basepath: project_dir = os.path.basename(basepath) script_name = os.path.basename(script_path) title = f"{escape(script_name)} — {escape(project_dir)}" else: title = escape(script_path) print(HTML_HEADER_FORMAT % title) if warning_string: print(warning_string) run_checker_program(checker_bin, checker_opts, script_path, version_string) print(HTML_FOOTER) -
fish2000 revised this gist
Dec 5, 2018 . 1 changed file with 24 additions and 17 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -4,7 +4,7 @@ # PyCheckMate, a PyChecker output beautifier for TextMate. # Copyright (c) Jay Soffian, 2005. <jay at soffian dot org> # Inspired by Domenico Carbotta's PyMate. # Extensively overhauled for version 2.0 by Alexander Böhn. # # License: Artistic. # @@ -49,9 +49,17 @@ # patterns to match output of checker programs PYCHECKER_RE = re.compile(r"^(?:\s*)(.*?\.pyc?):(\d+):(?:\s*)(.*)(?:\s*)$") def textmate_url(file, line=None, column=None): """ Compose a Textmate callback URL, for sending the cursor to a location within an active Textmate buffer: """ url = f"txmt://open?url=file://{quote(file)}" if type(line) is int: url += f"&line={line}" if type(column) is int: url += f"&column={column}" return url HTML_HEADER_FORMAT = """ <html> <head> @@ -200,10 +208,9 @@ def check_syntax(script_path): compile(source, script_path, "exec") print("None<br>") except SyntaxError as e: url = textmate_url(script_path, int(e.lineno), int(e.offset)) print('<a href="%s">%s:%s</a> %s' % (url, escape(os.path.basename(script_path)), e.lineno, e.msg)) except: @@ -309,7 +316,7 @@ def run_checker_program(checker_bin, match = PYCHECKER_RE.search(line) if match: filename, lineno, msg = match.groups() url = textmate_url(filename, int(lineno)) if basepath is not None and filename.startswith(basepath): filename = filename[len(basepath)+1:] @@ -320,17 +327,17 @@ def run_checker_program(checker_bin, tpl = ''' <div class="message"> <span class="number">%(number)02d</span> <a href="%(url)s">%(filename)s:%(lineno)d</a> %(whitespace)s <span class="message-text">%(message)s</a> </div> ''' print(tpl % dict(url=url, filename=filename, lineno=int(lineno), whitespace=add_br, message=msg, number=int(idx) + 1)) idx += 1 else: @@ -409,5 +416,5 @@ def main(script_path): if len(sys.argv) == 2: sys.exit(main(sys.argv[1])) else: print(f"Usage: {os.path.basename(sys.argv[0])} <file.py>", file=sys.stderr) sys.exit(1) -
fish2000 revised this gist
Nov 29, 2018 . 1 changed file with 4 additions and 10 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -174,7 +174,10 @@ def which(binary_name, pathvar=None): """ from distutils.spawn import find_executable if not hasattr(which, 'pathvar'): prefix_bin = os.path.join(sys.prefix, 'bin') executable_bin = os.path.split(sys.executable)[0] which.pathvar = os.getenv("PATH", DEFAULT_PATH) which.pathvar += f":{prefix_bin}:{executable_bin}" return find_executable(binary_name, pathvar or which.pathvar) or "" UTF8_ENCODING = 'UTF-8' @@ -225,16 +228,7 @@ def find_checker_program(): for checker in CHECKERS: basename = os.path.split(checker)[1] if checker == basename: checker = which(basename) if not os.path.isfile(checker): continue -
fish2000 revised this gist
Nov 29, 2018 . 1 changed file with 11 additions and 12 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -345,16 +345,16 @@ def run_checker_program(checker_bin, # THEY TOLD ME TO FLUSH THE PIPES SO I FLUSHED THE PIPES sys.stdout.flush() if stderr: for line in stderr.decode(UTF8_ENCODING).splitlines(): # strip whitespace off front and replace with so that # we can allow the browser to wrap long lines but we don't lose # leading indentation otherwise. stripped = line.lstrip() pad = " " * (len(line) - len(stripped)) line = escape(stripped.rstrip()) print(f'<span class="stderr">{pad}{line}</span><br>') sys.stdout.flush() returncode = p.returncode if returncode is None: @@ -398,7 +398,6 @@ def main(script_path): print(HTML_HEADER_FORMAT % (title, version_string, 'xx issues found')) if warning_string: print(warning_string) @@ -408,8 +407,8 @@ def main(script_path): script_path) print(HTML_FOOTER) sys.stdout.flush() return 0 if __name__ == "__main__": -
fish2000 revised this gist
Nov 29, 2018 . 1 changed file with 58 additions and 53 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -160,6 +160,7 @@ CHECKERS = ["pychecker", "pyflakes", "pylint", "pep8", "flake8"] DEFAULT_TIMEOUT = 60 # seconds DEFAULT_PATH = ":".join(filter(os.path.exists, ("/usr/local/bin", "/bin", "/usr/bin", "/sbin", "/usr/sbin"))) @@ -298,66 +299,71 @@ def run_checker_program(checker_bin, stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: stdout, stderr = p.communicate(timeout=DEFAULT_TIMEOUT) except subprocess.TimeoutExpired: p.kill() stdout, stderr = p.communicate(timeout=None) if stdout is None: stdout = b'' if stderr is None: stderr = b'' idx = 0 for line in stdout.decode(UTF8_ENCODING).splitlines(): match = PYCHECKER_RE.search(line) if match: filename, lineno, msg = match.groups() href = TXMT_URL1_FORMAT % (quote(filename), lineno) if basepath is not None and filename.startswith(basepath): filename = filename[len(basepath)+1:] # naive linewrapping, but it seems to work well-enough add_br = "" if len(filename) + len(msg) > 80: add_br += "<br> " tpl = ''' <div class="message"> <span class="number">%(number)02d</span> <a href="%(href)s">%(filename)s:%(lineno)d</a> %(whitespace)s <span class="message-text">%(message)s</a> </div> ''' print(tpl % dict(href=href, filename=filename, lineno=int(lineno), whitespace=add_br, message=msg, number=int(idx) + 1)) idx += 1 else: print(f'{line}<br>') # THEY TOLD ME TO FLUSH THE PIPES SO I FLUSHED THE PIPES sys.stdout.flush() for line in stderr.decode(UTF8_ENCODING).splitlines(): # strip whitespace off front and replace with so that # we can allow the browser to wrap long lines but we don't lose # leading indentation otherwise. stripped = line.lstrip() pad = " " * (len(line) - len(stripped)) line = escape(stripped.rstrip()) print(f'<span class="stderr">{pad}{line}</span><br>') sys.stdout.flush() returncode = p.returncode if returncode is None: returncode = 'NULL' p.terminate() print(f''' <div id="exit-status"> <br>Exit status: {returncode} </div> ''') @@ -391,8 +397,7 @@ def main(script_path): print(HTML_HEADER_FORMAT % (title, version_string, 'xx issues found')) sys.stdout.flush() if warning_string: @@ -401,10 +406,10 @@ def main(script_path): run_checker_program(checker_bin, checker_opts, script_path) print(HTML_FOOTER) sys.stdout.flush() return 0 if __name__ == "__main__": -
fish2000 revised this gist
Nov 29, 2018 . 1 changed file with 7 additions and 7 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -199,9 +199,9 @@ def check_syntax(script_path): href = TXMT_URL2_FORMAT % (quote(script_path), e.lineno, e.offset) print('<a href="%s">%s:%s</a> %s' % (href, escape(os.path.basename(script_path)), e.lineno, e.msg)) except: for line in traceback.format_exception(sys.exc_info()): stripped = line.lstrip() @@ -324,11 +324,11 @@ def run_checker_program(checker_bin, <span class="message-text">%(message)s</a> </div> ''' print(tpl % dict(href=href, filename=filename, lineno=int(lineno), whitespace=add_br, message=msg, number=int(idx) + 1), file=sys.stdout) idx += 1 -
fish2000 revised this gist
Nov 29, 2018 . 1 changed file with 28 additions and 26 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -52,7 +52,8 @@ # careful editing these, they are format strings TXMT_URL1_FORMAT = "txmt://open?url=file://%s&line=%s" TXMT_URL2_FORMAT = "txmt://open?url=file://%s&line=%s&column=%s" HTML_HEADER_FORMAT = """ <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>PyCheckMate %s</title> @@ -150,7 +151,8 @@ <div id="output"> """ HTML_FOOTER = """ </div> </div> </body> </html> @@ -204,8 +206,8 @@ def check_syntax(script_path): for line in traceback.format_exception(sys.exc_info()): stripped = line.lstrip() pad = " " * (len(line) - len(stripped)) line = escape(stripped.rstrip()) print(f'<span class="stderr">{pad}{line}</span><br>') def find_checker_program(): tm_pychecker = os.getenv("TM_PYCHECKER") @@ -237,44 +239,44 @@ def find_checker_program(): continue if basename == "pychecker": with os.popen(f'"{checker}" -V 2>/dev/null') as p: version = p.readline().strip() if version: version = f"PyChecker {version}" return (checker, opts, version) elif basename == "pylint": with os.popen(f'"{checker}" --version 2>/dev/null') as p: version = p.readline().strip() if version: version = re.sub('^pylint\s*', '', version) version = re.sub(',$', '', version) version = f"Pylint {version}" opts += ('--output-format=parseable',) return (checker, opts, version) elif basename == "pyflakes": # pyflakes doesn't have a version string embedded anywhere, # so run it against itself to make sure it's functional with os.popen(f'"{checker}" "{checker}" 2>&1 >/dev/null') as p: output = p.readlines() if not output: return (checker, opts, "PyFlakes") elif basename == "pep8": with os.popen(f'"{checker}" --version 2>/dev/null') as p: version = p.readline().strip() if version: version = f"PEP 8 {version}" global PYCHECKER_RE PYCHECKER_RE = re.compile(r"^(.*?\.pyc?):(\d+):(?:\d+:)?\s+(.*)$") return (checker, opts, version) elif basename == "flake8": with os.popen(f'"{checker}" --version 2>/dev/null') as p: version = p.readline().strip() if version: version = f"flake8 {version}" PYCHECKER_RE = re.compile(r"^(.*?\.pyc?):(\d+):(?:\d+:)?\s+(.*)$") return (checker, opts, version) @@ -307,8 +309,8 @@ def run_checker_program(checker_bin, if match: filename, lineno, msg = match.groups() href = TXMT_URL1_FORMAT % (quote(filename), lineno) if basepath is not None and filename.startswith(basepath): filename = filename[len(basepath)+1:] # naive linewrapping, but it seems to work well-enough add_br = "" @@ -344,7 +346,7 @@ def run_checker_program(checker_bin, stripped = line.lstrip() pad = " " * (len(line) - len(stripped)) line = escape(stripped.rstrip()) print(f'<span class="stderr">{pad}{line}</span><br>', file=sys.stderr) sys.stderr.flush() break @@ -353,15 +355,15 @@ def run_checker_program(checker_bin, if returncode is None: p.terminate() print(f''' <div id="exit-status"> <br>Exit status: {returncode or 'NULL'} </div> ''') def main(script_path): checker_bin, checker_opts, checker_ver = find_checker_program() version_string = f"PyCheckMate {__version__} – {checker_ver}" warning_string = "" if not checker_bin: href_format = \ @@ -383,13 +385,13 @@ def main(script_path): if basepath: project_dir = os.path.basename(basepath) script_name = os.path.basename(script_path) title = f"{escape(script_name)} — {escape(project_dir)}" else: title = escape(script_path) print(HTML_HEADER_FORMAT % (title, version_string, 'xx issues found'), file=sys.stdout) sys.stdout.flush() @@ -401,13 +403,13 @@ def main(script_path): script_path) sys.stdout.flush() print(HTML_FOOTER, file=sys.stdout) sys.stdout.flush() return 0 if __name__ == "__main__": if len(sys.argv) == 2: sys.exit(main(sys.argv[1])) else: print(f"Usage: {sys.argv[0]} <file.py>", file=sys.stderr) sys.exit(1) -
fish2000 revised this gist
Nov 29, 2018 . 1 changed file with 9 additions and 4 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -285,19 +285,22 @@ def run_checker_program(checker_bin, script_path): import subprocess basepath = os.getenv("TM_PROJECT_DIRECTORY") cmd = [checker_bin] if checker_opts: cmd.extend(checker_opts) cmd.append(script_path) p = subprocess.Popen(cmd, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) while 1: stdout, stderr = p.communicate() if stdout is None: break idx = 0 for line in stdout.decode(UTF8_ENCODING).splitlines(): match = PYCHECKER_RE.search(line) @@ -306,6 +309,7 @@ def run_checker_program(checker_bin, href = TXMT_URL1_FORMAT % (quote(filename), lineno) if basepath is not None and str(filename).startswith(basepath): filename = str(filename[len(basepath)+1:]) # naive linewrapping, but it seems to work well-enough add_br = "" if len(filename) + len(msg) > 80: @@ -348,11 +352,12 @@ def run_checker_program(checker_bin, returncode = p.returncode if returncode is None: p.terminate() print(''' <div id="exit-status"> <br>Exit status: %s </div> ''' % returncode or 'NULL') def main(script_path): checker_bin, checker_opts, checker_ver = find_checker_program() -
fish2000 revised this gist
Nov 29, 2018 . 1 changed file with 7 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -346,9 +346,13 @@ def run_checker_program(checker_bin, break returncode = p.returncode if returncode is None: p.terminate() print(str(''' <div id="exit-status"> <br>Exit status: %s </div> ''' % returncode or 'NULL')) def main(script_path): checker_bin, checker_opts, checker_ver = find_checker_program() @@ -369,14 +373,14 @@ def main(script_path): pylint_url, pep8_url, flake8_url) basepath = os.getenv("TM_PROJECT_DIRECTORY") if basepath: project_dir = os.path.basename(basepath) script_name = os.path.basename(script_path) title = "%s — %s" % (escape(script_name), escape(project_dir)) else: title = escape(script_path) print(str(HTML_HEADER_FORMAT % (title, version_string, -
fish2000 revised this gist
Nov 29, 2018 . 1 changed file with 16 additions and 27 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -40,10 +40,6 @@ basestring = str unicode = str PYCHECKER_URL = "http://pychecker.sourceforge.net/" PYFLAKES_URL = "http://divmod.org/projects/pyflakes" PYLINT_URL = "http://www.logilab.org/857" @@ -190,9 +186,6 @@ def utf8_encode(source): return source.encode(UTF8_ENCODING) return source def check_syntax(script_path): with open(script_path, 'r') as handle: source = ''.join(handle.readlines() + ["\n"]) @@ -217,6 +210,7 @@ def check_syntax(script_path): def find_checker_program(): tm_pychecker = os.getenv("TM_PYCHECKER") opts = filter(None, os.getenv('TM_PYCHECKER_OPTIONS', '').split()) version = '' if tm_pychecker == "builtin": return ('', None, "Syntax check only") @@ -243,18 +237,16 @@ def find_checker_program(): continue if basename == "pychecker": with os.popen('"%s" -V 2>/dev/null' % (checker)) as p: version = p.readline().strip() if version: version = "PyChecker %s" % version return (checker, opts, version) elif basename == "pylint": with os.popen('"%s" --version 2>/dev/null' % (checker)) as p: version = p.readline().strip() if version: version = re.sub('^pylint\s*', '', version) version = re.sub(',$', '', version) version = "Pylint %s" % version @@ -264,27 +256,24 @@ def find_checker_program(): elif basename == "pyflakes": # pyflakes doesn't have a version string embedded anywhere, # so run it against itself to make sure it's functional with os.popen('"%s" "%s" 2>&1 >/dev/null' % (checker, checker)) as p: output = p.readlines() if not output: return (checker, opts, "PyFlakes") elif basename == "pep8": with os.popen('"%s" --version 2>/dev/null' % (checker)) as p: version = p.readline().strip() if version: version = "PEP 8 %s" % version global PYCHECKER_RE PYCHECKER_RE = re.compile(r"^(.*?\.pyc?):(\d+):(?:\d+:)?\s+(.*)$") return (checker, opts, version) elif basename == "flake8": with os.popen('"%s" --version 2>/dev/null' % (checker)) as p: version = p.readline().strip() if version: version = "flake8 %s" % version PYCHECKER_RE = re.compile(r"^(.*?\.pyc?):(\d+):(?:\d+:)?\s+(.*)$") return (checker, opts, version) -
fish2000 revised this gist
Nov 29, 2018 . 1 changed file with 60 additions and 50 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -160,9 +160,23 @@ </html> """ CHECKERS = ["pychecker", "pyflakes", "pylint", "pep8", "flake8"] DEFAULT_PATH = ":".join(filter(os.path.exists, ("/usr/local/bin", "/bin", "/usr/bin", "/sbin", "/usr/sbin"))) def which(binary_name, pathvar=None): """ Deduces the path corresponding to an executable name, as per the UNIX command `which`. Optionally takes an override for the $PATH environment variable. Always returns a string - an empty one for those executables that cannot be found. """ from distutils.spawn import find_executable if not hasattr(which, 'pathvar'): which.pathvar = os.getenv("PATH", DEFAULT_PATH) return find_executable(binary_name, pathvar or which.pathvar) or "" UTF8_ENCODING = 'UTF-8' @@ -176,24 +190,20 @@ def utf8_encode(source): return source.encode(UTF8_ENCODING) return source class Error(Exception): pass def check_syntax(script_path): with open(script_path, 'r') as handle: source = ''.join(handle.readlines() + ["\n"]) try: print("Syntax Errors...<br><br>") compile(source, script_path, "exec") print("None<br>") except SyntaxError as e: href = TXMT_URL2_FORMAT % (quote(script_path), e.lineno, e.offset) print(str('<a href="%s">%s:%s</a> %s' % (href, escape(os.path.basename(script_path)), e.lineno, e.msg))) @@ -205,44 +215,41 @@ def check_syntax(script_path): print('<span class="stderr">%s%s</span><br>' % (pad, line)) def find_checker_program(): tm_pychecker = os.getenv("TM_PYCHECKER") opts = filter(None, os.getenv('TM_PYCHECKER_OPTIONS', '').split()) if tm_pychecker == "builtin": return ('', None, "Syntax check only") if tm_pychecker is not None: if not tm_pychecker in CHECKERS: CHECKERS.insert(0, tm_pychecker) for checker in CHECKERS: basename = os.path.split(checker)[1] if checker == basename: # look for checker in same bin directory as python -- # it might be symlinked: bindir = os.path.split(sys.executable)[0] checker = os.path.join(bindir, basename) if not os.path.isfile(checker): # continue searching python's installation directory: checker = os.path.join(sys.prefix, "bin", basename) if not os.path.isfile(checker): # search the PATH checker = which(basename) if not os.path.isfile(checker): continue if basename == "pychecker": p = os.popen('"%s" -V 2>/dev/null' % (checker)) version = p.readline().strip() status = p.close() if status is None and version: version = "PyChecker %s" % version return (checker, opts, version) elif basename == "pylint": p = os.popen('"%s" --version 2>/dev/null' % (checker)) version = p.readline().strip() @@ -253,7 +260,7 @@ def find_checker_program(): version = "Pylint %s" % version opts += ('--output-format=parseable',) return (checker, opts, version) elif basename == "pyflakes": # pyflakes doesn't have a version string embedded anywhere, # so run it against itself to make sure it's functional @@ -262,7 +269,7 @@ def find_checker_program(): status = p.close() if status is None and not output: return (checker, opts, "PyFlakes") elif basename == "pep8": p = os.popen('"%s" --version 2>/dev/null' % (checker)) version = p.readline().strip() @@ -272,7 +279,7 @@ def find_checker_program(): global PYCHECKER_RE PYCHECKER_RE = re.compile(r"^(.*?\.pyc?):(\d+):(?:\d+:)?\s+(.*)$") return (checker, opts, version) elif basename == "flake8": p = os.popen('"%s" --version 2>/dev/null' % (checker)) version = p.readline().strip() @@ -281,36 +288,35 @@ def find_checker_program(): version = "flake8 %s" % version PYCHECKER_RE = re.compile(r"^(.*?\.pyc?):(\d+):(?:\d+:)?\s+(.*)$") return (checker, opts, version) return ('', None, "Syntax check only") def run_checker_program(checker_bin, checker_opts, script_path): import subprocess basepath = os.getenv("TM_PROJECT_DIRECTORY") cmd = [] cmd.append(checker_bin) if checker_opts: cmd.extend(checker_opts) cmd.append(script_path) p = subprocess.Popen(cmd, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) while 1: stdout, stderr = p.communicate() if stdout is None: break idx = 0 for line in stdout.decode(UTF8_ENCODING).splitlines(): match = PYCHECKER_RE.search(line) if match: filename, lineno, msg = match.groups() href = TXMT_URL1_FORMAT % (quote(filename), lineno) if basepath is not None and str(filename).startswith(basepath): filename = str(filename[len(basepath)+1:]) # naive linewrapping, but it seems to work well-enough add_br = "" if len(filename) + len(msg) > 80: @@ -369,7 +375,11 @@ def main(script_path): flake8_url = href_format % (FLAKE8_URL, "flake8") warning_string = \ "<p>Please install %s, %s, %s, %s or %s for more extensive code checking." \ "</p><br>" % (pychecker_url, pyflakes_url, pylint_url, pep8_url, flake8_url) basepath = os.getenv("TM_PROJECT_DIRECTORY") if basepath: @@ -379,20 +389,20 @@ def main(script_path): else: title = script_path print(str(HTML_HEADER_FORMAT % (title, version_string, 'xx issues found')), file=sys.stdout) sys.stdout.flush() if warning_string: print(warning_string) run_checker_program(checker_bin, checker_opts, script_path) sys.stdout.flush() print(str(HTML_FOOTER), file=sys.stdout) sys.stdout.flush() return 0 -
fish2000 revised this gist
Nov 21, 2018 . 1 changed file with 4 additions and 13 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -303,26 +303,18 @@ def run_checker_program(checker_bin, checker_opts, script_path): break idx = 0 for line in stdout.decode(UTF8_ENCODING).splitlines(): global PYCHECKER_RE match = PYCHECKER_RE.search(line) if match: filename, lineno, msg = match.groups() global TXMT_URL1_FORMAT href = TXMT_URL1_FORMAT % (quote(filename), lineno) # if basepath is not None and str(filename).startswith(basepath): # filename = str(filename[len(basepath)+1:]) # naive linewrapping, but it seems to work well-enough add_br = "" if len(filename) + len(msg) > 80: add_br += "<br> " tpl = ''' <div class="message"> <span class="number">%(number)02d</span> @@ -339,12 +331,11 @@ def run_checker_program(checker_bin, checker_opts, script_path): number=int(idx) + 1), file=sys.stdout) idx += 1 else: print(f'{line}<br>') # THEY TOLD ME TO FLUSH THE PIPES SO I FLUSHED THE PIPES sys.stdout.flush() for line in stderr.decode(UTF8_ENCODING).splitlines(): -
fish2000 revised this gist
Nov 21, 2018 . 1 changed file with 2 additions and 120 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -4,6 +4,7 @@ # PyCheckMate, a PyChecker output beautifier for TextMate. # Copyright (c) Jay Soffian, 2005. <jay at soffian dot org> # Inspired by Domenico Carbotta's PyMate. # Extensively overhauled for verions 2.0 by Alexander Böhn # # License: Artistic. # @@ -27,9 +28,8 @@ import sys import traceback from html import escape __version__ = "2.0" PY3 = False if sys.version_info < (3, 0): @@ -180,123 +180,6 @@ def utf8_encode(source): class Error(Exception): pass ### ### Program code ### @@ -409,7 +292,6 @@ def run_checker_program(checker_bin, checker_opts, script_path): if checker_opts: cmd.extend(checker_opts) cmd.append(script_path) p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, -
fish2000 created this gist
Nov 21, 2018 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,532 @@ #!/usr/bin/env python # encoding: utf-8 # # PyCheckMate, a PyChecker output beautifier for TextMate. # Copyright (c) Jay Soffian, 2005. <jay at soffian dot org> # Inspired by Domenico Carbotta's PyMate. # # License: Artistic. # # Usage: # - Out of the box, pycheckmate.py will perform only a basic syntax check # by attempting to compile the python code. # - Install PyChecker or PyFlakes for more extensive checking. If both are # installed, PyChecker will be used. # - TM_PYCHECKER may be set to control which checker is used. Set it to just # "pychecker", "pyflakes", "pep8", "flake8", or "pylint", or "frosted" to # locate these programs in the default python bin directory or to a full # path if the checker program is installed elsewhere. # - If for some reason you want to use the built-in sytax check when either # pychecker or pyflakes are installed, you may set TM_PYCHECKER to # "builtin". from __future__ import absolute_import, print_function import os import re import sys import traceback from html import escape from select import select __version__ = "1.2" PY3 = False if sys.version_info < (3, 0): from urllib import quote else: from urllib.parse import quote PY3 = True basestring = str unicode = str ### ### Constants ### PYCHECKER_URL = "http://pychecker.sourceforge.net/" PYFLAKES_URL = "http://divmod.org/projects/pyflakes" PYLINT_URL = "http://www.logilab.org/857" PEP8_URL = "http://pypi.python.org/pypi/pep8" FLAKE8_URL = "http://pypi.python.org/pypi/flake8/" # patterns to match output of checker programs PYCHECKER_RE = re.compile(r"^(?:\s*)(.*?\.pyc?):(\d+):(?:\s*)(.*)(?:\s*)$") # careful editing these, they are format strings TXMT_URL1_FORMAT = "txmt://open?url=file://%s&line=%s" TXMT_URL2_FORMAT = "txmt://open?url=file://%s&line=%s&column=%s" HTML_HEADER_FORMAT = """<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>PyCheckMate %s</title> <style type="text/css"> body { background-color: #D8E2F1; margin: 0; } div#body { border-style: dotted; border-width: 1px 0; border-color: #666; margin: 10px 0; padding: 10px; background-color: #C9D9F0; } div#output { padding: 0; margin: 0; color: #121212; font-family: Consolas, Monaco; font-size: 11pt; } div#output div.message { vertical-align: middle; display: inline-block; margin: 0.5em; padding: 0.5em; margin-left: 0px; margin-right: 1em; padding-left: 2px; padding-right: 1em; margin-top: 10px; padding-top: 0px; border-radius: 10px; background-color: #D9E9FF; color: #121212; font-family: Consolas, Monaco; font-size: 11pt; } div#output div.message span.number { padding: 0; margin: 0; margin-left: 10px; color: #121212; font-family: Georgia, Times New Roman; font-size: 3em; } div#output div.message span.message-text { padding: 0; margin: 0; margin-left: 2.5em; } div#output div.message a { color: darkorange; } div#exit-status { padding: 0; margin: 0; padding-top: 1em; font-family: Consolas, Monaco; font-size: 11pt; } strong { margin-left: 3.0em; font-family: Aksidenz-Grotesk, Helvetica Neue, Helvetica, Arial; text-transform: uppercase; } strong.title { margin-top: 1em; font-size: 18pt; text-transform: uppercase; } span.stderr { color: red; } p { margin: 0; padding: 2px 0; } </style> </head> <body> <div id="body"> <p><strong class="title">%s</strong></p> <p><strong>%s</strong></p> <br> <div id="output"> """ HTML_FOOTER = """</div> </div> </body> </html> """ ### ### Helper classes ### UTF8_ENCODING = 'UTF-8' def utf8_encode(source): """ Encode a source as bytes using the UTF-8 codec """ if PY3: if type(source) is bytes: return source return bytes(source, encoding=UTF8_ENCODING) if type(source) is unicode: return source.encode(UTF8_ENCODING) return source class Error(Exception): pass class MyPopen(object): """Modifed version of standard popen2.Popen class that does what I need. Runs command with stdin redirected from /dev/null and monitors its stdout and stderr. Each time poll() is called a tuple of (stdout, stderr) is returned where stdout and stderr are lists of zero or more lines of output from the command. status() should be called before calling poll() and if it returns other than -1 then the child has terminated and poll() will return no additional output. At that point drain() should be called to return the last bit of output. As a simplication, readlines() can be called until it returns (None, None) """ try: MAXFD = os.sysconf('SC_OPEN_MAX') except (AttributeError, ValueError): MAXFD = 256 def __init__(self, cmd): stdout_r, stdout_w = os.pipe() stderr_r, stderr_w = os.pipe() self._status = -1 self._drained = 0 self._pid = os.fork() if self._pid == 0: # child devnull = open("/dev/null") os.dup2(devnull.fileno(), 0) os.dup2(stdout_w, 1) os.dup2(stderr_w, 2) devnull.close() self._run_child(cmd) else: # parent os.close(stdout_w) os.close(stderr_w) self._stdout = stdout_r self._stderr = stderr_r self._stdout_buf = "" self._stderr_buf = "" def _run_child(self, cmd): if isinstance(cmd, basestring): cmd = ['/bin/sh', '-c', cmd] for i in range(3, self.MAXFD): try: os.close(i) except OSError: pass try: os.execvp(cmd[0], cmd) finally: os._exit(1) def status(self): """Returns exit status of child or -1 if still running.""" if self._status < 0: try: pid, this_status = os.waitpid(self._pid, os.WNOHANG) if pid == self._pid: self._status = this_status except os.error: pass return self._status def poll(self, timeout=None): """Returns (stdout, stderr) from child.""" bufs = { self._stdout : self._stdout_buf, self._stderr : self._stderr_buf } fds, dummy, dummy = select(bufs.keys(), [], [], timeout) for fd in fds: data = os.read(fd, 4096) bufs[fd] += str(data) self._stdout_buf = "" self._stderr_buf = "" stdout_lines = bufs[self._stdout].splitlines() stderr_lines = bufs[self._stderr].splitlines() if stdout_lines and not bufs[self._stdout].endswith("\n"): self._stdout_buf = stdout_lines.pop() if stderr_lines and not bufs[self._stderr].endswith("\n"): self._stderr_buf = stderr_lines.pop() return (stdout_lines, stderr_lines) def drain(self): stdout, stderr = [self._stdout_buf], [self._stderr_buf] while 1: data = os.read(self._stdout, 4096) if not data: break stdout.append(data) while 1: data = os.read(self._stderr, 4096) if not data: break stderr.append(data) self._stdout_buf = "" self._stderr_buf = "" self._drained = 1 stdout_lines = ''.join(stdout).splitlines() stderr_lines = ''.join(stderr).splitlines() return (stdout_lines, stderr_lines) def readlines(self): if self._drained: return None, None elif self.status() == -1: return self.poll() else: return self.drain() def close(self): os.close(self._stdout) os.close(self._stderr) ### ### Program code ### def check_syntax(script_path): f = open(script_path, 'r') source = ''.join(f.readlines()+["\n"]) f.close() try: print("Syntax Errors...<br><br>") compile(source, script_path, "exec") print("None<br>") except SyntaxError as e: href = TXMT_URL2_FORMAT % (quote(script_path), e.lineno, e.offset) print(str('<a href="%s">%s:%s</a> %s' % (href, escape(os.path.basename(script_path)), e.lineno, e.msg))) except: for line in traceback.format_exception(sys.exc_info()): stripped = line.lstrip() pad = " " * (len(line) - len(stripped)) line = str(escape(stripped.rstrip())) print('<span class="stderr">%s%s</span><br>' % (pad, line)) def find_checker_program(): checkers = ["pychecker", "pyflakes", "pylint", "pep8", "flake8"] tm_pychecker = os.getenv("TM_PYCHECKER") opts = filter(None, os.getenv('TM_PYCHECKER_OPTIONS', '').split()) if tm_pychecker == "builtin": return ('', None, "Syntax check only") if tm_pychecker is not None: checkers.insert(0, tm_pychecker) for checker in checkers: basename = os.path.split(checker)[1] if checker == basename: # look for checker in same bin directory as python (might be # symlinked) bindir = os.path.split(sys.executable)[0] checker = os.path.join(bindir, basename) if not os.path.isfile(checker): # look where python is installed checker = os.path.join(sys.prefix, "bin", basename) if not os.path.isfile(checker): # search the PATH p = os.popen("/usr/bin/which '%s'" % basename) checker = p.readline().strip() p.close() if not os.path.isfile(checker): continue if basename == "pychecker": p = os.popen('"%s" -V 2>/dev/null' % (checker)) version = p.readline().strip() status = p.close() if status is None and version: version = "PyChecker %s" % version return (checker, opts, version) elif basename == "pylint": p = os.popen('"%s" --version 2>/dev/null' % (checker)) version = p.readline().strip() status = p.close() if status is None and version: version = re.sub('^pylint\s*', '', version) version = re.sub(',$', '', version) version = "Pylint %s" % version opts += ('--output-format=parseable',) return (checker, opts, version) elif basename == "pyflakes": # pyflakes doesn't have a version string embedded anywhere, # so run it against itself to make sure it's functional p = os.popen('"%s" "%s" 2>&1 >/dev/null' % (checker, checker)) output = p.readlines() status = p.close() if status is None and not output: return (checker, opts, "PyFlakes") elif basename == "pep8": p = os.popen('"%s" --version 2>/dev/null' % (checker)) version = p.readline().strip() status = p.close() if status is None and version: version = "PEP 8 %s" % version global PYCHECKER_RE PYCHECKER_RE = re.compile(r"^(.*?\.pyc?):(\d+):(?:\d+:)?\s+(.*)$") return (checker, opts, version) elif basename == "flake8": p = os.popen('"%s" --version 2>/dev/null' % (checker)) version = p.readline().strip() status = p.close() if status is None and version: version = "flake8 %s" % version PYCHECKER_RE = re.compile(r"^(.*?\.pyc?):(\d+):(?:\d+:)?\s+(.*)$") return (checker, opts, version) return ('', None, "Syntax check only") def run_checker_program(checker_bin, checker_opts, script_path): import subprocess basepath = os.getenv("TM_PROJECT_DIRECTORY") cmd = [] cmd.append(checker_bin) if checker_opts: cmd.extend(checker_opts) cmd.append(script_path) # p = MyPopen(cmd) p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False) while 1: stdout, stderr = p.communicate() if stdout is None: break idx = 0 for line in stdout.decode(UTF8_ENCODING).splitlines(): # line = str(line.strip()) global PYCHECKER_RE match = PYCHECKER_RE.search(line) # print(str(line), file=sys.stdout) if match: filename, lineno, msg = match.groups() # href = TXMT_URL1_FORMAT % (quote(os.path.abspath(filename.strip())), # lineno) global TXMT_URL1_FORMAT href = TXMT_URL1_FORMAT % (quote(filename), lineno) # if basepath is not None and str(filename).startswith(basepath): # filename = str(filename[len(basepath)+1:]) # naive linewrapping, but it seems to work well-enough if len(filename) + len(msg) > 80: add_br = "<br> " else: add_br = " " # line = '<a href="%s">%s:%d</a>%s%s' % ( # href, filename, int(lineno), add_br, # escape(msg)) tpl = ''' <div class="message"> <span class="number">%(number)02d</span> <a href="%(href)s">%(filename)s:%(lineno)d</a> %(whitespace)s <span class="message-text">%(message)s</a> </div> ''' print(tpl % dict(href=str(href), filename=str(filename), lineno=int(lineno), whitespace=str(add_br), message=str(msg), number=int(idx) + 1), file=sys.stdout) idx += 1 else: print(f'{line}<br>') # else: # line = escape(line) # print(str("%s<br>" % line)) sys.stdout.flush() for line in stderr.decode(UTF8_ENCODING).splitlines(): # strip whitespace off front and replace with so that # we can allow the browser to wrap long lines but we don't lose # leading indentation otherwise. stripped = line.lstrip() pad = " " * (len(line) - len(stripped)) line = escape(stripped.rstrip()) print('<span class="stderr">%s%s</span><br>' % (pad, line), file=sys.stderr) sys.stderr.flush() break returncode = p.returncode print(str('<div id="exit-status"><br>Exit status: %s</div>' % returncode)) if returncode is None: p.terminate() def main(script_path): checker_bin, checker_opts, checker_ver = find_checker_program() version_string = "PyCheckMate %s – %s" % (__version__, checker_ver) warning_string = "" if not checker_bin: href_format = \ "<a href=\"javascript:TextMate.system('open %s', null)\">%s</a>" pychecker_url = href_format % (PYCHECKER_URL, "PyChecker") pyflakes_url = href_format % (PYFLAKES_URL, "PyFlakes") pylint_url = href_format % (PYLINT_URL, "Pylint") pep8_url = href_format % (PEP8_URL, "PEP 8") flake8_url = href_format % (FLAKE8_URL, "flake8") warning_string = \ "<p>Please install %s, %s, %s, %s or %s for more extensive code checking." \ "</p><br>" % (pychecker_url, pyflakes_url, pylint_url, pep8_url, flake8_url) basepath = os.getenv("TM_PROJECT_DIRECTORY") if basepath: project_dir = os.path.basename(basepath) script_name = os.path.basename(script_path) title = "%s — %s" % (escape(script_name), escape(project_dir)) else: title = script_path print(str(HTML_HEADER_FORMAT % (title, version_string, 'xx issues found')), file=sys.stdout) sys.stdout.flush() if warning_string: print(warning_string) run_checker_program(checker_bin, checker_opts, script_path) sys.stdout.flush() # if checker_bin: # run_checker_program(checker_bin, checker_opts, script_path) # else: # check_syntax(script_path) print(str(HTML_FOOTER), file=sys.stdout) sys.stdout.flush() return 0 if __name__ == "__main__": if len(sys.argv) == 2: sys.exit(main(sys.argv[1])) else: print("Usage: %s <file.py>" % sys.argv[0], file=sys.stderr) sys.exit(1)