예제 #1
0
    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)
        )
예제 #2
0
    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
예제 #4
0
    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
예제 #5
0
    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)
예제 #6
0
    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
예제 #7
0
    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))
예제 #8
0
    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)
        )
예제 #9
0
    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())
예제 #10
0
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
예제 #11
0
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)
예제 #15
0
    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 ''
예제 #16
0
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 ''
예제 #17
0
    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 ''
예제 #18
0
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 ''
예제 #19
0
    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,
        )
예제 #20
0
    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
예제 #22
0
    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))
예제 #24
0
    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)))
예제 #26
0
def _onerror(function, path, excinfo):
    persist.printf(
        "mypy: Unable to delete '{}' while cleaning up temporary directory".
        format(path))
    import traceback
    traceback.print_exc(*excinfo)
예제 #27
0
    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()