def check(self, code, filename): """Run flake8 on code and return the output.""" options = { 'reporter': self.get_report() } type_map = { 'select': [], 'ignore': [], 'builtins': '', 'max-line-length': 0, 'max-complexity': 0 } self.build_options(options, type_map, transform=lambda s: s.replace('-', '_')) if persist.debug_mode(): persist.printf('{} options: {}'.format(self.name, options)) if self.pyflakes_checker_class is not None: # Reset the builtins to the initial value used by pyflakes. builtins = set(self.pyflakes_checker_module.builtin_vars).union(self.pyflakes_checker_module._MAGIC_GLOBALS) self.pyflakes_checker_class.builtIns = builtins linter = self.module.get_style_guide(**options) return linter.input_file( filename=os.path.basename(filename), lines=code.splitlines(keepends=True) )
def initialize(cls): """Initialize the class after plugin load.""" super().initialize() if cls.module is None: return # This is tricky. Unfortunately pyflakes chooses to store # builtins in a class variable and union that with the builtins option # on every execution. This results in the builtins never being removed. # To fix that, we get a reference to the pyflakes.checker module and # pyflakes.checker.Checker class used by flake8. We can then reset # the Checker.builtIns class variable on each execution. try: from pkg_resources import iter_entry_points except ImportError: persist.printf('WARNING: {} could not import pkg_resources.iter_entry_points'.format(cls.name)) else: for entry in iter_entry_points('flake8.extension'): check = entry.load() if check.name == 'pyflakes': from pyflakes import checker cls.pyflakes_checker_module = checker cls.pyflakes_checker_class = check break
def run(self, cmd, code): """Check if file is 'lintable' and perform linting.""" # It has to start with `"syntax_test"` # and has a specific first line, technically. # We only require one of each # (to also lint unsaved views). basename = os.path.basename(self.filename) if not basename or not basename.startswith("syntax_test"): # This actually gets reported by the test runner, # so we only check for an additionally qualifying file # if the filename check fails. first_line = code[:code.find("\n")] match = re.match(r'^(\S*) SYNTAX TEST "([^"]*)"', first_line) if not match: return # The syntax test runner only operates on resource files that the resource loader can load, # which must reside in a "Packages" folder # and has the restriction of only working on saved files. # Instead, we create a temporary file somewhere in the packages folder # and pass that. with _temporary_resource_file(code, prefix="syntax_test_") as resource_path: assertions, test_output_lines = sublime_api.run_syntax_test( resource_path) output = "\n".join(test_output_lines) if persist.debug_mode(): persist.printf('{}: "{}" assertions: {}'.format( p_name, basename, assertions)) # SublimeLinter internally already prints the output we return # persist.printf('{}: "{}" output: \n {}'.format(p_name, basename, # "\n ".join(test_output_lines))) return output
def lint(self, path, content): """Lint the content of a file.""" output = "" try: persist.debug("Connecting to Julia lint server ({}, {})".format(self.address, self.port)) # noqa output = self._lint(path, content) except Exception as e: persist.debug(e) if not self.auto_start: persist.printf("Julia lint server is not running") else: # if self.proc is not None: # if self.proc.poll() is None: # persist.debug("Local Julia lint server was started") # return # else: # raise subprocess.SubprocessError(self.proc.returncode) persist.printf("Launching Julia lint server on localhost port {}".format(self.port)) # noqa self.start() persist.printf("Julia lint server starting up, server will be operational shortly") # noqa try: # Wait to give Julia time to start sleep(5) output = self._lint(path, content) except Exception as e: persist.debug(e) persist.printf("Julia lint server failed to start") else: persist.printf("Julia lint server now operational") return output
def find_gopaths(self): """ search for potential GOPATHs. """ # collect existing Go path info goroot = set(os.path.normpath(s) for s in os.environ.get('GOROOT', '').split(os.pathsep)) gopath = set(os.path.normpath(s) for s in os.environ.get('GOPATH', '').split(os.pathsep)) if '.' in gopath: gopath.remove('.') gopath = list(gopath) # search for potential GOPATHs upstream from filename # (reversed to ensure deepest path is first searched) dirparts = os.path.dirname(self.filename).split(os.sep) for i in range(len(dirparts) - 1, 1, -1): if dirparts[i].lower() != "src": continue p = os.path.normpath(os.sep.join(dirparts[:i])) if p not in goroot and p not in gopath: gopath.append(p) if persist.debug_mode(): persist.printf("{}: {} {}".format(self.name, os.path.basename(self.filename or '<unsaved>'), "guessed GOPATH=" + os.pathsep.join(gopath))) return os.pathsep.join(gopath)
def cmd(self): """Return the command line to execute.""" command = [self.executable, '--result-format', 'json', '--format'] try: file_format = {'plain text': 'plain', 'markdown': 'markdown', 'wiki': 'wiki', 'javaproperties': 'properties', 'latex': 'latex', 'asciidoc': 'asciidoc', }[persist.get_syntax(self.view)] command.append(file_format) except KeyError: raise KeyError('Illegal syntax. \'{}\''.format(self.view)) settings = self.get_view_settings() conf_file_path = settings.get('conf', '') if conf_file_path != '': if os.path.exists(conf_file_path): command.append('--conf') command.append(conf_file_path) else: persist.printf('ERROR: Config file is not exist. \'{}\''.format(conf_file_path)) command.append('@') return command
def check(self, code, filename): """Run pycodestyle on code and return the output.""" options = {'reporter': self.get_report()} type_map = { 'select': [], 'ignore': [], 'max-line-length': 0, 'max-complexity': 0 } self.build_options(options, type_map, transform=lambda s: s.replace('-', '_')) final_options = options.copy() # Try to read options from pycodestyle default configuration files (.pycodestyle, tox.ini). # If present, they will override the ones defined by Sublime Linter's config. try: # `onError` will be called by `process_options` when no pycodestyle configuration file is found. # Override needed to supress OptionParser.error() output in the default parser. def onError(msg): pass from pycodestyle import process_options, get_parser parser = get_parser() parser.error = onError pycodestyle_options, _ = process_options([os.curdir], True, True, parser=parser) # Merge options only if the pycodestyle config file actually exists; # pycodestyle always returns a config filename, even when it doesn't exist! if os.path.isfile(pycodestyle_options.config): pycodestyle_options = vars(pycodestyle_options) pycodestyle_options.pop('reporter', None) for opt_n, opt_v in pycodestyle_options.items(): if isinstance(final_options.get(opt_n, None), list): final_options[opt_n] += opt_v else: final_options[opt_n] = opt_v except SystemExit: # Catch and ignore parser.error() when no config files are found. pass if persist.debug_mode(): persist.printf('{} ST options: {}'.format(self.name, options)) persist.printf('{} options: {}'.format(self.name, final_options)) checker = self.module.StyleGuide(**final_options) return checker.input_file(filename=os.path.basename(filename), lines=code.splitlines(keepends=True))
def check(self, code, filename): """Run pep8 on code and return the output.""" options = { 'reporter': self.get_report() } type_map = { 'select': [], 'ignore': [], 'max-line-length': 0, 'max-complexity': 0 } self.build_options(options, type_map, transform=lambda s: s.replace('-', '_')) final_options = options.copy() # Try to read options from pep8 default configuration files (.pep8, tox.ini). # If present, they will override the ones defined by Sublime Linter's config. try: # `onError` will be called by `process_options` when no pep8 configuration file is found. # Override needed to supress OptionParser.error() output in the default parser. def onError(msg): pass from pep8 import process_options, get_parser parser = get_parser() parser.error = onError pep8_options, _ = process_options([os.curdir], True, True, parser=parser) # Merge options only if the pep8 config file actually exists; # pep8 always returns a config filename, even when it doesn't exist! if os.path.isfile(pep8_options.config): pep8_options = vars(pep8_options) pep8_options.pop('reporter', None) for opt_n, opt_v in pep8_options.items(): if isinstance(final_options.get(opt_n, None), list): final_options[opt_n] += opt_v else: final_options[opt_n] = opt_v except SystemExit: # Catch and ignore parser.error() when no config files are found. pass if persist.debug_mode(): persist.printf('{} ST options: {}'.format(self.name, options)) persist.printf('{} options: {}'.format(self.name, final_options)) checker = self.module.StyleGuide(**final_options) return checker.input_file( filename=os.path.basename(filename), lines=code.splitlines(keepends=True) )
def run(self, cmd, code): """ transparently add potential GOPATHs before running. """ self.env = {'GOPATH': self.find_gopaths()} # copy debug output from Linter.run() if persist.debug_mode(): persist.printf('{}: {} {}'.format(self.name, os.path.basename(self.filename or '<unsaved>'), cmd or '<builtin>')) return self.tmpfile(cmd, code, suffix=self.get_tempfile_suffix())
def find_script_by_python_env(python_env_path, script): """Return full path to a script, given a python environment base dir.""" posix = sublime.platform() in ('osx', 'linux') if posix: full_path = os.path.join(python_env_path, 'bin', script) else: full_path = os.path.join(python_env_path, 'Scripts', script + '.exe') persist.printf("trying {}".format(full_path)) if os.path.exists(full_path): return full_path return None
def can_lint(cls, syntax): """ Override so we can get additional syntaxes via user settings. """ extra_syntaxes = cls.settings().get('extra_syntaxes', []) if not isinstance(extra_syntaxes, list) or not all(isinstance(s, str) for s in extra_syntaxes): persist.printf("Error: Invalid setting for 'extra_settings'. Must be a list of syntax names.") extra_syntaxes = [] supported_syntaxes = cls._base_syntax_list + extra_syntaxes if syntax.lower() in (s.lower() for s in supported_syntaxes): if cls.executable_path != '': return True else: persist.debug("{} not in {}".format(syntax.lower(), [s.lower() for s in supported_syntaxes])) return False
def _temporary_resource_file(text, prefix='', suffix=''): """Create a temporary file in ST's "resource" folder, using tempfile.mkstemp. Yields the relative resource path as a context manager and removes it when the scope is exited. Files are stored in a Temp folder relative to the Data folder, which is removed afterwards if it does not contain any other files. """ import tempfile # Ensure the folder exists if not os.path.exists(_temp_path): os.mkdir(_temp_path) try: fd, temp_file_path = tempfile.mkstemp(prefix=prefix, suffix=suffix, dir=_temp_path) if persist.debug_mode(): persist.printf("{}: created temporary file at {}".format( p_name, temp_file_path)) try: with open(fd, 'w', encoding='utf-8') as f: f.write(text) temp_file_resource_path = "/".join( ["Packages", _temp_dir_name, os.path.basename(temp_file_path)]) yield temp_file_resource_path finally: os.remove(temp_file_path) except FileNotFoundError: _remove_temp_path() finally: # And remove the folder, if it's empty. # Otherwise wait for a "restart". try: os.rmdir(_temp_path) except OSError as e: if persist.debug_mode(): persist.printf( "{}: unable to delete temporary folder; {}".format( p_name, e))
def _remove_temp_path(): """Try to clean our temp dir if it exists.""" if os.path.exists(_temp_path): if os.path.isdir(_temp_path): def onerror(function, path, excinfo): persist.printf( "{}: Unable to delete '{}' while cleaning up temporary directory" .format(p_name, path)) import traceback traceback.print_exc(*excinfo) import shutil shutil.rmtree(_temp_path, onerror=onerror) else: persist.printf( "{}: For some reason, '{}' is a file. Removing...".format( p_name, _temp_path)) os.remove(_temp_path)
def check(self, code, filename): """ Call directly the yaml module, and handles the exception. Return str. Very similar to the SublimeLinter-json linter, except yaml is not in the python core library. """ yaml = self.module try: list(yaml.parse(code, Loader=yaml.BaseLoader)) except yaml.error.YAMLError as exc: if persist.settings.get('debug'): persist.printf('{} - {} : {}'.format(self.name, type(exc), exc)) message = '{} : {} {}'.format(type(exc).__name__, exc.problem, exc.context) return ':{}:{}: {}\n'.format(exc.problem_mark.line, exc.problem_mark.column, message) except Exception as exc: persist.printf('{} - uncaught exception - {} : {}'.format(self.name, type(exc), exc)) return ''
def _communicate(cmd): """Short wrapper around subprocess.check_output to eat all errors.""" env = util.create_environment() info = None # On Windows, start process without a window if os.name == 'nt': info = subprocess.STARTUPINFO() info.dwFlags |= subprocess.STARTF_USESTDHANDLES | subprocess.STARTF_USESHOWWINDOW info.wShowWindow = subprocess.SW_HIDE try: return subprocess.check_output(cmd, env=env, startupinfo=info, universal_newlines=True) except Exception as err: persist.printf("executing {} failed: reason: {}".format(cmd, str(err))) return ''
def check(self, code, filename): """ Call directly the yaml module, and handles the exception. Return str. Very similar to the SublimeLinter-json linter, except yaml is not in the python core library. """ yaml = self.module try: for x in yaml.safe_load_all(code): # exhausting generator so all documents are checked pass except yaml.error.YAMLError as exc: if persist.settings.get('debug'): persist.printf('{} - {} : {}'.format(self.name, type(exc), exc)) message = '{} : {} {}'.format(type(exc).__name__, exc.problem, exc.context) return ':{}:{}: {}\n'.format(exc.problem_mark.line, exc.problem_mark.column, message) except Exception as exc: persist.printf('{} - uncaught exception - {} : {}'.format(self.name, type(exc), exc)) return ''
def _communicate(cmd): """Short wrapper around subprocess.check_output to eat all errors.""" env = util.create_environment() info = None # On Windows, start process without a window if os.name == 'nt': info = subprocess.STARTUPINFO() info.dwFlags |= subprocess.STARTF_USESTDHANDLES | subprocess.STARTF_USESHOWWINDOW info.wShowWindow = subprocess.SW_HIDE try: return subprocess.check_output( cmd, env=env, startupinfo=info, universal_newlines=True ) except Exception as err: persist.printf( "executing {} failed: reason: {}".format(cmd, str(err)) ) return ''
def initialize(cls): """Initialize.""" persist.printf(cls) super().initialize() settings = cls.settings() # Server will a cls.server = JuliaLintServerDaemon( settings['server_address'], settings['server_port'], settings['automatically_start_server'], settings['timeout'], ) warn_levels = ['W\d\d\d'] if settings['show_info_warnings']: warn_levels.append('I\d\d\d') # Set the regex based upon whether or not show_info_warnings is set cls.regex = re.compile( r""" ^(?P<filename>.+?\.jl):(?P<line>\d+) \s+ (?P<message> (?: (?P<error>E\d\d\d) | (?P<warning>{}) ) \s+ (?: (?P<near>.*?):\s ) \s* .*? ) \s*$ """.format("|".join(warn_levels)), re.VERBOSE, )
def check(self, code, filename): """Run frosted.check on code and return the output.""" output = StringIO() Reporter = self.get_reporter() options = { 'filename': filename, 'reporter': Reporter(output, output), 'verbose': True } type_map = { 'ignore': [] } transform = lambda s: self.__transform_options.get(s, s.replace('-', '_')) self.build_options(options, type_map, transform) if persist.debug_mode(): persist.printf('{} options: {}'.format(self.name, options)) self.module.check(code, **options) return output.getvalue()
def cmd(self): """ Return the command line to be executed. We override this method, so we can change executable, add extra flags and include paths based on settings. """ # take linter folder BASE_PATH = os.path.abspath(os.path.dirname(__file__)) # take executable file from linter class (in this case tclsh) cmd = self.executable settings = self.get_view_settings() """ Build syntax database basis using tcl, tm files form project folder, if file opened in project otherwise, do not create it. database is .syntaxdb file in project folder currently each time new database is build each time linter starts """ bd = builder(cmd, os.path.join(BASE_PATH, 'nagelfar.kit')) bd.rebuild(get_project_folder()) # Add negelfar.kit - the linter os-independent executable file which is executed by tclsh cmd += ' \"' + os.path.join(BASE_PATH, 'nagelfar.kit') + '\" ' dbs = {} try: dbs['tcl_db'] = settings.get('tcl_db', self.default_settings['tcl_db']) except KeyError: if persist.settings.get('debug'): persist.printf('tcl_db not found in dict') try: dbs['additional_db'] = settings.get( 'additional_db', self.default_settings['additional_db']) except KeyError: if persist.settings.get('debug'): persist.printf('additional not found in dict') # depends of the user settings, add or not additional parameters to linter if len(dbs) > 0: cmd += ' -s ' if 'tcl_db' in dbs: cmd += dbs['tcl_db'] + ' ' #TODO: only on Windows?? cmd = cmd.replace('\\', '\\\\') if 'additional_db' in dbs: cmd += apply_template(' '.join( [shlex.quote(include) for include in dbs['additional_db']])) if persist.settings.get('debug'): persist.printf('cmd to execute: ' + cmd) return cmd
def check(self, code, filename): """Run pydocstyle on code and return the output.""" args = self.build_args(self.get_view_settings(inline=True)) if persist.settings.get('debug'): persist.printf('{} args: {}'.format(self.name, args)) conf = self.module.config.ConfigurationParser() with partialpatched(conf, '_parse_args', args=args + [filename], values=None): conf.parse() errors = [] for fname, checked_codes, ignore_decorators in \ conf.get_files_to_check(): errors.extend( self.module.check( [fname], select=checked_codes, ignore_decorators=ignore_decorators)) return errors
def rebuild(self, masterPath): db_file = os.path.join(masterPath, '.syntaxdb') if os.path.exists( db_file) and os.path.getmtime(db_file) + 600.0 > time.time(): if persist.settings.get('debug'): persist.printf( '.syntaxdb exists and is was created within 10min ' + str(os.path.getmtime(db_file)) + ' os time ' + str(time.time())) return persist.printf('Rebuilding in folder {}'.format(masterPath)) if masterPath is None: persist.printf('Nothing to rebuild') return self._scaner.scan(masterPath, ['.tcl', '.tm']) si = STARTUPINFO() si.dwFlags |= STARTF_USESHOWWINDOW files = [] for file in self._scaner: if search('.*syntaxbuild.tcl', file) or search( '.*syntaxdb.tcl', file): continue #persist.printf('Rebuilding for file {}'.format(file)) files.append(file) p = Popen([self._executable, join(self._nagelfar), '-header', db_file] + files, stdin=PIPE, stdout=PIPE, stderr=PIPE, startupinfo=si) output, err = p.communicate() if persist.settings.get('debug'): persist.printf('output: ' + str(output) + ', error: ' + str(err))
def run(self, cmd, code): """ Search code for annotations, mark lines that contain them. We do the marking here since there is no point in searching the lines twice. We return nothing (None) to abort any further processing. """ options = {} type_map = {'errors': [], 'warnings': []} self.build_options(options, type_map) for option in options: values = [] for value in options[option]: if value: # Add \b word separator fences around the value # if it begins or ends with a word character. value = re.escape(value) if value[0].isalnum() or value[0] == '_': value = r'\b' + value if value[-1].isalnum() or value[-1] == '_': value += r'\b' values.append(value) options[option] = '|'.join(values) regex = re.compile(self.match_re.format_map(options)) output = [] for i, line in enumerate(code.splitlines()): match = regex.match(line) if match: col = match.start('message') word = match.group('error') message = match.group('message') if word: error_type = highlight.ERROR else: word = match.group('warning') error_type = highlight.WARNING if persist.debug_mode(): output.append('line {}, col {}: {}'.format( i + 1, col + 1, message)) self.highlight.range(i, col, length=len(word), error_type=error_type) self.error(i, col, message, error_type) if output and persist.debug_mode(): persist.printf('{}: {} output:\n{}'.format( self.name, os.path.basename(self.filename), '\n'.join(output)))
def run(self, cmd, code): """ Search code for annotations, mark lines that contain them. We do the marking here since there is no point in searching the lines twice. We return nothing (None) to abort any further processing. """ options = {} type_map = { 'errors': [], 'warnings': [] } self.build_options(options, type_map) for option in options: values = [] for value in options[option]: if value: # Add \b word separator fences around the value # if it begins or ends with a word character. value = re.escape(value) if value[0].isalnum() or value[0] == '_': value = r'\b' + value if value[-1].isalnum() or value[-1] == '_': value += r'\b' values.append(value) options[option] = '|'.join(values) regex = re.compile(self.match_re.format_map(options)) output = [] for i, line in enumerate(code.splitlines()): match = regex.match(line) if match: col = match.start('message') word = match.group('error') message = match.group('message') if word: error_type = highlight.ERROR else: word = match.group('warning') error_type = highlight.WARNING if persist.debug_mode(): output.append('line {}, col {}: {}'.format(i + 1, col + 1, message)) self.highlight.range(i, col, length=len(word), error_type=error_type) self.error(i, col, message, error_type) if output and persist.debug_mode(): persist.printf('{}: {} output:\n{}'.format(self.name, os.path.basename(self.filename), '\n'.join(output)))
def _onerror(function, path, excinfo): persist.printf( "mypy: Unable to delete '{}' while cleaning up temporary directory". format(path)) import traceback traceback.print_exc(*excinfo)
def context_sensitive_executable_path(self, cmd): """Try to find an executable for a given cmd.""" settings = self.get_view_settings() # If the user explicitly set an executable, it takes precedence. # We expand environment variables. E.g. a user could have a project # structure where a virtual environment is always located within # the project structure. She could then simply specify # `${project_path}/venv/bin/flake8`. Note that setting `@python` # to a path will have a similar effect. executable = settings.get('executable', '') if executable: executable = util.expand_variables(executable) persist.printf( "{}: wanted executable is '{}'".format(self.name, executable) ) if util.can_exec(executable): return True, executable persist.printf( "ERROR: {} deactivated, cannot locate '{}' " .format(self.name, executable) ) # no fallback, the user specified something, so we err return True, None # `@python` can be number or a string. If it is a string it should # point to a python environment, NOT a python binary. # We expand environment variables. E.g. a user could have a project # structure where virtual envs are located always like such # `some/where/venvs/${project_base_name}` or she has the venv # contained in the project dir `${project_path}/venv`. She then # could edit the global settings once and can be sure that always the # right linter installed in the virtual environment gets executed. python = settings.get('@python', None) if isinstance(python, str): python = util.expand_variables(python) persist.printf( "{}: wanted @python is '{}'".format(self.name, python) ) cmd_name = cmd[0] if isinstance(cmd, (list, tuple)) else cmd if python: if isinstance(python, str): executable = util.find_script_by_python_env( python, cmd_name ) if not executable: persist.printf( "WARNING: {} deactivated, cannot locate '{}' " "for given @python '{}'" .format(self.name, cmd_name, python) ) # Do not fallback, user specified something we didn't find return True, None return True, executable else: executable = util.find_script_by_python_version( cmd_name, str(python) ) # If we didn't find anything useful, use the legacy # code from SublimeLinter for resolving that version. if executable is None: persist.printf( "{}: Still trying to resolve {}, now trying " "SublimeLinter's legacy code." .format(self.name, python) ) _, executable, *_ = util.find_python( str(python), cmd_name ) if executable is None: persist.printf( "WARNING: {} deactivated, cannot locate '{}' " "for given @python '{}'" .format(self.name, cmd_name, python) ) return True, None persist.printf( "{}: Using {} for given @python '{}'" .format(self.name, executable, python) ) return True, executable # If we're here the user didn't specify anything. This is the default # experience. So we kick in some 'magic' chdir = self.get_chdir(settings) executable = util.ask_pipenv(cmd[0], chdir) if executable: persist.printf( "{}: Using {} according to 'pipenv'" .format(self.name, executable) ) return True, executable # Should we try a `pyenv which` as well? Problem: I don't have it, # it's MacOS only. persist.printf( "{}: trying to use globally installed {}" .format(self.name, cmd_name) ) # fallback, similiar to a which(cmd) executable = util.find_executable(cmd_name) if executable is None: persist.printf( "WARNING: cannot locate '{}'. Fill in the '@python' or " "'executable' setting." .format(self.name) ) return True, executable
import os.path import re import sublime from SublimeLinter.lint import PythonLinter # save and reference preferenses etc from SublimeLinter.lint import persist # If the linter outputs errors only on stderr or stdout, set error_stream to util.STREAM_STDERR or util.STREAM_STDOUT from SublimeLinter.lint import util # packaged dependencies is the folder in our plugin sys.path.append(os.path.join(os.path.dirname(__file__), "./dist/")) import boto3 if persist.settings.get('debug'): persist.printf('{} dependencies: {}'.format(__name__, boto3)) persist.printf('{} dependencies: {}'.format(__name__, persist)) persist.printf('{} dependencies: {}'.format(__name__, util)) """ awscli boto botocore colorama concurrent dateutil docutils jmespath pyasn1 rsa
def __init__(self, executable, nagelfar_path): persist.printf('Builder initialized') self._executable = executable self._nagelfar = nagelfar_path self._scaner = pathScanner()