예제 #1
0
    def auto_build(self):
        current_project_dir, current_project_name = Common.locate_cabal_project_from_view(self.view)

        if current_project_name and current_project_dir:
            build_mode = Settings.get_project_setting(self.view.window().active_view(), 'auto_build_mode',
                                                      Settings.PLUGIN.auto_build_mode)

            build_command = self.MODE_BUILD_COMMAND.get(build_mode)
            if not build_command:
                Common.output_error(self.view.window(), "SublimeHaskell: invalid auto_build_mode '%s'" % build_mode)
                return

            # Duplicate the dictionary corresponding to the build command. We might modify it later.
            config = dict(self.BUILD_TOOL_CONFIG[build_command])
            addl_config = None

            if build_mode.endswith('-then-tests'):
                has_tests = False
                projects = self.get_projects()

                if current_project_name in projects and 'description' in projects[current_project_name]:
                    has_tests = projects[current_project_name]['description'].get('tests') is not None

                if has_tests:
                    addl_config = 'test'
            elif build_mode.endswith('-then-bench'):
                addl_config = 'bench'

            if addl_config is not None:
                for tool, steps in self.BUILD_TOOL_CONFIG[addl_config]['steps'].items():
                    config['steps'][tool].extend(steps)

            Logging.log('auto build: final config:\n{0}'.format(pprint.pformat(config)))

            self.run_build(current_project_name, current_project_dir, config)
예제 #2
0
def select_project(window, on_selected, filter_project=None):
    projs = [(name, info) for (name, info) in get_projects().items()
             if not filter_project or filter_project(name, info)]

    def run_selected(psel):
        on_selected(psel[0], psel[1]['path'])

    if len(projs) == 0:
        Common.show_status_message(
            "No projects found, did you add a '.cabal' file?",
            is_ok=False,
            priority=5)
        return
    if len(projs) == 1:  # There's only one project, build it
        run_selected(projs[0])
        return

    _, cabal_project_name = Common.get_cabal_project_dir_and_name_of_view(
        window.active_view())
    Logging.log('Current project: {0}'.format(cabal_project_name))

    # Sort by name
    projs.sort(key=lambda p: p[0])

    current_project_idx = next(
        (i for i, p in enumerate(projs) if p[0] == cabal_project_name), -1)

    def on_done(idx):
        if idx != -1:
            run_selected(projs[idx])

    window.show_quick_panel(list(map(lambda m: [m[0], m[1]['path']], projs)),
                            on_done, 0, current_project_idx)
예제 #3
0
    def connect(self, host, port):
        for i in range(0, self.N_SOCKETS):
            sock = self.create_connection(i, host, port)
            if sock is not None:
                hsconn = HsDevConnection(sock, self.request_q, i)
                self.socket_pool.insert(i, hsconn)
            else:
                # Something went wrong, terminate all connnections:
                for sock in self.socket_pool:
                    try:
                        sock.close()
                    except OSError:
                        pass
                self.socket_pool = []
                self.request_q = None
                return False

        # We were successful, start all of the socket threads...
        for sock in self.socket_pool:
            sock.start_connection()

        self.rcvr_thread = threading.Thread(target=self.receiver)
        self.rcvr_thread.start()

        Logging.log('Connections established to \'hsdev\' server.',
                    Logging.LOG_INFO)
        return True
예제 #4
0
    def go_chain(self, cmds):
        try:
            if not cmds:
                self.status_msg.stop()
                hsdev.client.autofix_show(self.msgs,
                                          on_response=self.on_autofix)
            else:
                cmd, tail_cmds = cmds[0], cmds[1:]
                (chain_fn, modify_args, modify_msgs, kwargs) = cmd

                def on_resp(msgs):
                    self.messages.extend(modify_msgs(msgs))
                    self.msgs.extend(msgs)
                    self.go_chain(tail_cmds)

                def on_err(_err, _details):
                    self.status_msg.fail()
                    self.go_chain([])

                chain_fn(modify_args(self.filename),
                         contents=self.contents,
                         wait=False,
                         on_response=on_resp,
                         on_error=on_err,
                         **kwargs)
        except OSError:
            Logging.log(
                'hsdev chain failed, see console window (<ctrl>-<backtick>) traceback',
                Logging.LOG_ERROR)
            print(traceback.format_exc())
            self.status_msg.fail()
            self.status_msg.stop()
예제 #5
0
    def exec_with_wrapper(exec_with, install_dir, cmd_list):
        '''Wrapper function for inserting the execution wrapper, e.g., 'cabal exec' or 'stack exec'

        :returns: Process object from ProcHelper.
        '''

        proc_args = {}
        if exec_with is not None:
            if exec_with == 'cabal':
                cmd_list = ['cabal', 'exec'] + cmd_list
                cmd_list.insert(3, '--')
            elif exec_with == 'stack':
                cmd_list = ['stack', 'exec'] + cmd_list
                cmd_list.insert(3, '--')
            else:
                errmsg = 'HsDevBackend.exec_with_wrapper: Unknown execution prefix \'{0}\''.format(
                    exec_with)
                raise RuntimeError(errmsg)

            if install_dir is not None:
                proc_args['cwd'] = Utils.normalize_path(install_dir)
        else:
            cmd = Which.which(cmd_list[0],
                              ProcHelper.ProcHelper.get_extended_path())
            if cmd is not None:
                cmd_list[0] = cmd

        Logging.log('HsDevBackend.exec_with_wrapper: {0}'.format(cmd_list),
                    Logging.LOG_DEBUG)
        return ProcHelper.ProcHelper(cmd_list, **proc_args)
예제 #6
0
    def select_project(self, on_selected, filter_project):
        projs = [(name, info) for (name, info) in self.get_projects().items()
                 if not filter_project or filter_project(name, info)]

        def run_selected(psel):
            on_selected(psel[0], psel[1]['path'])

        if not projs:
            Common.show_status_message("No active projects found.",
                                       is_ok=False,
                                       priority=5)
        elif len(projs) == 1:
            # There's only one project, build it
            run_selected(projs[0])
        else:
            _, cabal_project_name = Common.locate_cabal_project_from_view(
                self.window.active_view())
            Logging.log('Current project: {0}'.format(cabal_project_name))

            # Sort by name
            projs.sort(key=lambda p: p[0])

            current_project_idx = next(
                (i for i, p in enumerate(projs) if p[0] == cabal_project_name),
                -1)

            def on_done(idx):
                if idx != -1:
                    run_selected(projs[idx])

            self.window.show_quick_panel(
                list(map(lambda m: [m[0], m[1]['path']], projs)), on_done, 0,
                current_project_idx)
예제 #7
0
    def select_project(self, on_selected, filter_project):
        '''Select a project from a generated project list. Execution flow continues into the :py:function:`on_selected`
        function with the project's name and the project's base directory. The :py:function:`filter_project` filters
        projects before they are shown (see :py:method:`get_projects`.)
        '''
        projs = [(name, info) for (name, info) in self.get_projects().items()
                 if not filter_project or filter_project(name, info)]

        def run_selected(psel):
            on_selected(psel[0], psel[1]['path'])

        if not projs:
            Common.sublime_status_message("No active projects found.")
        elif len(projs) == 1:
            # There's only one project, build it
            run_selected(projs[0])
        else:
            _, cabal_project_name = Common.locate_cabal_project_from_view(self.view)
            Logging.log('Current project: {0}'.format(cabal_project_name))

            # Sort by name
            projs.sort(key=lambda p: p[0])

            current_project_idx = next((i for i, p in enumerate(projs) if p[0] == cabal_project_name), -1)

            def on_done(idx):
                if idx != -1:
                    run_selected(projs[idx])

            self.view.window().show_quick_panel([[m[0], m[1].get('path', '??')] for m in projs], on_done, 0,
                                                current_project_idx)
예제 #8
0
def try_decode_bytes(src):
    try:
        return decode_bytes(src)
    except Exception as e:
        Logging.log('Error decoding bytes from subprocess: {}'.format(src),
                    Logging.LOG_ERROR)
        return '\n'
예제 #9
0
    def invoke_tool(command, tool_name, inp='', on_result=None, filename=None, on_line=None, check_enabled=True,
                    **popen_kwargs):
        if check_enabled and not Settings.PLUGIN.__getattribute__(Utils.tool_enabled(tool_name)):
            return None

        source_dir = get_source_dir(filename)

        def mk_result(result):
            return on_result(result) if on_result else result

        try:
            with ProcHelper(command, cwd=source_dir, **popen_kwargs) as proc:
                exit_code, stdout, stderr = proc.wait(inp)
                if exit_code != 0:
                    raise Exception('{0} exited with exit code {1} and stderr: {2}'.format(tool_name, exit_code, stderr))

                if on_line:
                    for line in io.StringIO(stdout):
                        on_line(mk_result(line))
                else:
                    return mk_result(stdout)

        except OSError as os_exc:
            if os_exc.errno == errno.ENOENT:
                errmsg = "SublimeHaskell: {0} was not found!\n'{1}' is set to False".format(tool_name,
                                                                                            Utils.tool_enabled(tool_name))
                Common.output_error_async(sublime.active_window(), errmsg)
                Settings.PLUGIN.__setattr__(Utils.tool_enabled(tool_name), False)
            else:
                Logging.log('{0} fails with {1}, command: {2}'.format(tool_name, os_exc, command), Logging.LOG_ERROR)

            return None

        return None
예제 #10
0
    def auto_build(self):
        current_project_dir, current_project_name = Common.locate_cabal_project_from_view(self.view)

        if current_project_name and current_project_dir:
            build_mode = Settings.get_project_setting(self.view.window().active_view(), 'auto_build_mode',
                                                      Settings.PLUGIN.auto_build_mode)

            build_command = self.MODE_BUILD_COMMAND.get(build_mode)
            if not build_command:
                Common.output_error(self.view.window(), "SublimeHaskell: invalid auto_build_mode '%s'" % build_mode)
                return

            # Duplicate the dictionary corresponding to the build command. We might modify it later.
            config = dict(self.BUILD_TOOL_CONFIG[build_command])
            addl_config = None

            if build_mode.endswith('-then-tests'):
                has_tests = False
                projects = self.get_projects()

                if current_project_name in projects and 'description' in projects[current_project_name]:
                    has_tests = projects[current_project_name]['description'].get('tests') is not None

                if has_tests:
                    addl_config = 'test'
            elif build_mode.endswith('-then-bench'):
                addl_config = 'bench'

            if addl_config is not None:
                for tool, steps in self.BUILD_TOOL_CONFIG[addl_config]['steps'].items():
                    config['steps'][tool].extend(steps)

            Logging.log('auto build: final config:\n{0}'.format(pprint.pformat(config)))

            self.run_build(current_project_name, current_project_dir, config)
예제 #11
0
    def read_response(self):
        resp_stdout = []
        resp_stderr = []
        try:
            got_reply = False
            while not got_reply:
                resp = self.ghcmod.process.stdout.readline()
                if resp == '':
                    # EOF???
                    got_reply = True
                else:
                    prefix = resp[0:3]
                    resp = resp.rstrip()[3:]
                    if prefix == self.GHCMOD_OUTPUT_MARKER:
                        if resp == 'OK':
                            got_reply = True
                        else:
                            resp_stdout.append(resp.rstrip())
                    elif prefix == self.GHCMOD_ERROR_MARKER:
                        Logging.log('{0}: {1}'.format(self.diag_prefix, resp))
                        resp_stderr.append(resp)
                    elif prefix == 'NG ':
                        sys.stdout.write('Error response: ' + resp)
                        got_reply = True
                    else:
                        sys.stdout.write(
                            'Unexpected reply from ghc-mod client: ' + resp)
                        got_reply = True
        except OSError:
            self.shutdown()

        return (resp_stdout, resp_stderr)
예제 #12
0
    def run_chain(self, cmds, msg, fly_mode=False):
        self.messages = []
        self.msgs = []
        self.corrections = []
        self.fly_mode = fly_mode
        self.filename = self.view.file_name()
        if not self.filename:
            return
        self.contents = {}
        if self.view.is_dirty():
            self.contents[self.filename] = self.view.substr(
                sublime.Region(0, self.view.size()))
        if not self.fly_mode:
            ParseOutput.hide_output(self.view)
        if cmds:
            self.status_msg = Common.status_message_process(msg + ': ' +
                                                            self.filename,
                                                            priority=2)
            self.status_msg.start()

            if not hsdev.agent_connected():
                Logging.log('hsdev chain fails: hsdev not connected',
                            Logging.LOG_ERROR)
                self.status_msg.fail()
                self.status_msg.stop()
            else:
                self.go_chain(cmds)
예제 #13
0
    def initialize(self):
        if self.current_state(self.INITIAL):
            with self.action_lock:
                # Can only start a backend iff in the INITIAL state.
                Logging.log(
                    'Starting backend \'{0}\''.format(
                        self.current_backend_name), Logging.LOG_INFO)
                backend_info = self.possible_backends.get(
                    self.current_backend_name, {})
                backend_clazz = self.BACKEND_META.get(
                    backend_info.get('backend')
                    or Backend.NullHaskellBackend.backend_name())
                if backend_clazz is not None:
                    the_backend = backend_clazz(
                        self, **backend_info.get('options', {}))
                else:
                    the_backend = None

                if the_backend is not None:
                    self.state_startup(the_backend)
                    self.state_connect(the_backend)
                    self.state_active(the_backend)

                    if self.current_state(self.INITIAL):
                        # Something failed during startup, revert to the null backend
                        self.set_backend(Backend.NullHaskellBackend(self))
                    elif not self.current_state(self.ACTIVE):
                        state_str = self.STATES_TO_NAME.get(
                            self.state, str(self.state))
                        Logging.log(
                            'BackendManager: Invalid state after starting backend: {0}'
                            .format(state_str), Logging.LOG_ERROR)
예제 #14
0
    def select_project(self, on_selected, filter_project):
        '''Select a project from a generated project list. Execution flow continues into the :py:function:`on_selected`
        function with the project's name and the project's base directory. The :py:function:`filter_project` filters
        projects before they are shown (see :py:method:`get_projects`.)
        '''
        projs = [(name, info) for (name, info) in self.get_projects().items()
                 if not filter_project or filter_project(name, info)]

        def run_selected(psel):
            on_selected(psel[0], psel[1]['path'])

        if not projs:
            Common.sublime_status_message("No active projects found.")
        elif len(projs) == 1:
            # There's only one project, build it
            run_selected(projs[0])
        else:
            _, cabal_project_name = Common.locate_cabal_project_from_view(self.view)
            Logging.log('Current project: {0}'.format(cabal_project_name))

            # Sort by name
            projs.sort(key=lambda p: p[0])

            current_project_idx = next((i for i, p in enumerate(projs) if p[0] == cabal_project_name), -1)

            def on_done(idx):
                if idx != -1:
                    run_selected(projs[idx])

            self.view.window().show_quick_panel([[m[0], m[1].get('path', '??')] for m in projs], on_done, 0,
                                                current_project_idx)
예제 #15
0
 def run_chain(self, cmds, msg, fly_mode=False):
     self.messages = []
     self.msgs = []
     self.corrections = []
     self.fly_mode = fly_mode
     self.filename = self.view.file_name()
     if self.filename:
         self.contents = {}
         if self.view.is_dirty():
             self.contents[self.filename] = self.view.substr(
                 sublime.Region(0, self.view.size()))
         if not self.fly_mode:
             ParseOutput.hide_output(self.view)
             if not hsdev.agent_connected():
                 Logging.log('hsdev chain fails: hsdev not connected',
                             Logging.LOG_ERROR)
                 sublime.error_message(
                     'check_lint.run_chain: Cannot execute command chain, hsdev not connected.'
                 )
             else:
                 if cmds:
                     self.status_msg = Common.status_message_process(
                         msg + ': ' + self.filename, priority=2)
                     self.status_msg.start()
                     self.go_chain(cmds)
                 else:
                     sublime.error_message(
                         'Empty command chain (check_lint.run_chain)')
예제 #16
0
    def go_chain(self, cmds):
        if not cmds:
            self.status_msg.stop()
            hsdev.client.autofix_show(self.msgs, on_response=self.on_autofix)
        else:
            cmd, tail_cmds = cmds[0], cmds[1:]
            agent_func, modify_args, modify_msgs, kwargs = cmd

            def go_chain_resp(msgs):
                Logging.log('go_chain_resp:\n{0}'.format(pprint.pformat(msgs)),
                            Logging.LOG_DEBUG)
                self.messages.extend(modify_msgs(msgs))
                self.msgs.extend(msgs)
                self.go_chain(tail_cmds)

            def go_chain_err(_err, details):
                Logging.log(
                    'go_chain_err: details\n{0}'.format(
                        pprint.pformat(details)), Logging.LOG_DEBUG)
                self.status_msg.fail()
                self.go_chain([])

            Logging.log('go_chain: executing\n{0}'.format(pprint.pformat(cmd)),
                        Logging.LOG_DEBUG)
            agent_func(modify_args(self.filename),
                       contents=self.contents,
                       wait=False,
                       on_response=go_chain_resp,
                       on_error=go_chain_err,
                       **kwargs)
예제 #17
0
 def drop_completions_async(self, file_name=None):
     Logging.log('drop prepared completions', Logging.LOG_DEBUG)
     with self.cache as cache_:
         if file_name is None:
             cache_.files.clear()
         elif file_name in cache_.files:
             del cache_.files[file_name]
예제 #18
0
    def make_augmented_path():
        ''' Generate the augmented PATH for subprocesses: adds the appropriate cabal/stack local install directory
        ($HOME/.local/bin for *nix, %APPDATA%/local/bin for Windows) and updates PATH with `add_to_PATH` extras.
        '''
        std_places = []
        if Settings.PLUGIN.add_standard_dirs:
            std_places.append("$HOME/.local/bin" if not Utils.is_windows() else
                              "%APPDATA%/local/bin")
            if Utils.is_macosx():
                std_places.append('$HOME/Library/Haskell/bin')
            std_places += CabalConfigRdr.cabal_config()
            std_places = [
                dir
                for dir in [Utils.normalize_path(path) for path in std_places]
                if os.path.isdir(dir)
            ]

        add_to_path = list(
            filter(os.path.isdir,
                   map(Utils.normalize_path, Settings.PLUGIN.add_to_path)))

        Logging.log("std_places = {0}".format(std_places), Logging.LOG_INFO)
        Logging.log("add_to_PATH = {0}".format(add_to_path), Logging.LOG_INFO)

        return os.pathsep.join(add_to_path + std_places)
예제 #19
0
    def run(self, edit, **kwargs):
        current_file_name = kwargs.get('filename', self.view.file_name())
        project_name = Common.locate_cabal_project_from_view(self.view)[1]
        backend = BackendManager.active_backend()

        imp_module = Utils.head_of(backend.module(project_name, file=current_file_name))
        if imp_module:
            imports = sorted(imp_module.imports, key=lambda i: i.position.line)

            supported, result = backend.clean_imports(current_file_name)
            print(result)
            if supported:
                if len(imports) == len(result):
                    Logging.log('replacing imports for {0}'.format(current_file_name), Logging.LOG_TRACE)
                    erased = 0
                    for imp, new_imp in zip(imports, result):
                        point = self.view.text_point(imp.position.line - 1 - erased, 0)
                        if new_imp.endswith('()'):
                            self.view.erase(edit, self.view.full_line(point))
                            erased = erased + 1
                        else:
                            self.view.replace(edit, self.view.line(point), new_imp)
                else:
                    Common.sublime_status_message('different number of imports: {0} and {1}'.format(len(imports), len(result)))
            else:
                if len(result) == 1:
                    Common.sublime_status_message(result[0])
                else:
                    sublime.message_dialog('\n'.join(result))
        else:
            Common.sublime_status_message('Clean Imports failed: module not scanned')
예제 #20
0
 def mark_all_files(self):
     for window in sublime.windows():
         with self.dirty_files as dirty_files:
             dirty_files.update([
                 (f, None) for f in [v.file_name() for v in window.views()]
                 if f and (f.endswith('.hs') or f.endswith('.hsc')) and f not in dirty_files
             ])
             Logging.log("dirty files: : {0}".format(dirty_files), Logging.LOG_DEBUG)
예제 #21
0
 def lint(self, files=None, contents=None, hlint=None):
     retval = cmd(
         'lint', {
             'files': files_and_contents(files or [], contents or {}),
             'hlint-opts': hlint or []
         })
     Logging.log('hsdev.lint: retval\n{0}'.format(pprint.pformat(retval)))
     return retval
예제 #22
0
 def set_connected(self):
     if self.is_connecting():
         self.connected.set()
         self.connecting.clear()
     else:
         Logging.log(
             'HsDev.set_connected called while not in connecting state',
             Logging.LOG_DEBUG)
예제 #23
0
 def reconnect(self):
     if self.connect_fun is not None:
         Logging.log('Reconnecting to hsdev...', Logging.LOG_INFO)
         HsCallback.call_callback(self.on_reconnect,
                                  name='HsDev.on_reconnect')
         self.connect_fun()
     else:
         Logging.log('No reconnect function')
예제 #24
0
 def on_hsdev_enabled(self, key, value):
     if key == 'enable_hsdev':
         if value:
             Logging.log("starting hsdev", Logging.LOG_INFO)
             self.start_hsdev()
         else:
             Logging.log("stopping hsdev", Logging.LOG_INFO)
             self.stop_hsdev()
예제 #25
0
    def call(self, command, opts, on_response, on_notify, on_error, wait, timeout):
        # log
        if not self.verify_connected():
            return None if wait else False

        opts = opts or {}
        with self.serial_lock:
            req_serial = str(self.request_serial)
            self.request_serial = self.request_serial + 1
        opts.update({'no-file': True, 'id': req_serial, 'command': command})

        wait_receive = threading.Event() if wait else None
        result_dict = {}

        def client_call_response(resp):
            result_dict['result'] = resp
            HsCallback.call_callback(on_response, resp)
            if wait_receive is not None:
                wait_receive.set()

        def client_call_error(exc, details):
            HsCallback.call_callback(on_error, exc, details)
            if wait_receive is not None:
                wait_receive.set()

        args_cmd = 'hsdev {0}'.format(command)
        self.setup_receive_callbacks(req_serial, args_cmd, client_call_response, on_notify, client_call_error, opts)

        call_cmd = u'HsDevClient.call[{0}] cmd \'{1}\' opts\n{2}'.format(req_serial, command, pprint.pformat(opts))
        if Settings.COMPONENT_DEBUG.all_messages or Settings.COMPONENT_DEBUG.send_messages:
            print(call_cmd)

        try:
            # Randomly choose a connection from the pool -- even if we end up having a stuck sender, this should minimize
            # the probability of a complete hang.
            random.choice(self.socket_pool).send_request(opts)

            if wait:
                wait_receive.wait(timeout)
                if not wait_receive.is_set():
                    with self.request_map as requests:
                        req = pprint.pformat(requests[req_serial][1])
                        errmsg = 'HsDevClient.call: wait_receive event timed out for id {0}\n{1}'.format(req_serial, req)
                        Logging.log(errmsg, Logging.LOG_ERROR)
                        # Delete the request; result_dict will still have nothing in it (presumably)
                        del requests[req_serial]

            if Settings.COMPONENT_DEBUG.socket_pool:
                with self.request_map as request_map:
                    print('id {0} request_map {1} queue {2}'.format(req_serial, len(request_map), self.request_q.qsize()))

            return result_dict.get('result') if wait else True

        except OSError:
            self.connection_lost('call', sys.exc_info()[1])
            self.backend_mgr.lost_connection()
            return False
예제 #26
0
    def mark_all_files(self):
        for window in sublime.windows():
            with self.dirty_files as dirty_files:
                dirty_files.update([(f, None) for f in [v.file_name() for v in window.views()] \
                                      if f and (f.endswith('.hs') or f.endswith('.hsc')) and f not in dirty_files])
                Logging.log("dirty files: : {0}".format(dirty_files), Logging.LOG_DEBUG)

            with self.dirty_paths as dirty_paths:
                dirty_paths.extend(window.folders())
예제 #27
0
    def run(self):
        if not Settings.PLUGIN.enable_hsdev:
            return

        Settings.PLUGIN.add_change_callback('enable_hsdev',
                                            self.on_hsdev_enabled)
        Settings.PLUGIN.add_change_callback('inspect_modules',
                                            self.on_inspect_modules_changed)

        self.start_hsdev()

        while True:
            if Settings.PLUGIN.enable_hsdev and not self.client.ping():
                Logging.log('hsdev ping: no pong', Logging.LOG_WARNING)

            scan_paths = []
            with self.dirty_paths as dirty_paths:
                scan_paths = dirty_paths[:]
                dirty_paths[:] = []

            files_to_reinspect = []
            with self.dirty_files as dirty_files:
                files_to_reinspect = dirty_files[:]
                dirty_files[:] = []

            projects = []
            files = []

            if len(files_to_reinspect) > 0:
                projects = []
                files = []
                for finspect in files_to_reinspect:
                    projdir = Common.get_cabal_project_dir_of_file(finspect)
                    if projdir is not None:
                        projects.append(projdir)
                    else:
                        files.append(finspect)

            projects = list(set(projects))
            files = list(set(files))

            self.inspect(paths=scan_paths, projects=projects, files=files)

            load_cabal = []
            with self.cabal_to_load as cabal_to_load:
                load_cabal = cabal_to_load[:]
                cabal_to_load[:] = []

            for cabal in load_cabal:
                Worker.run_async('inspect cabal {0}'.format(cabal),
                                 self.inspect_cabal, cabal)

            if files_to_reinspect and Settings.PLUGIN.enable_hdocs:
                self.client_back.docs(files=files_to_reinspect)

            self.reinspect_event.wait(HsDevLocalAgent.sleep_timeout)
            self.reinspect_event.clear()
예제 #28
0
 def worker_run(self):
     while True:
         name, worker_fn, args, kwargs = self.jobs.get()
         try:
             Logging.log('worker: {0}'.format(name), Logging.LOG_DEBUG)
             worker_fn(*args, **kwargs)
         except Exception:
             Logging.log('worker: job {0} failed, see console window traceback'.format(name), Logging.LOG_ERROR)
             traceback.print_exc()
예제 #29
0
    def collect_completions(self, backend, modinfo, lookup):
        if Settings.COMPONENT_DEBUG.completions:
            print('ghc-mod collect_completions for {0}'.format(modinfo))

        modname, is_qualified, qualname = modinfo
        mod_imported = [symbols.Import(modname, is_qualified, qualname)]
        syms = backend.command_backend('browse -d -o ' + modname) if backend is not None else []
        Logging.log('ghc-mod collect_completions: syms {0}'.format(syms), Logging.LOG_DEBUG)
        return [self.parse_syminfo(sym, mod_imported) for sym in syms if sym.startswith(lookup)]
예제 #30
0
 def call_error(self, err, details):
     self.log_time()
     comb_details = ', '.join(
         ['{0}: {1}'.format(k, v) for k, v in details.items()])
     Logging.log(
         '{0} returns error: {1}\n{2}'.format(self.command, err,
                                              comb_details),
         Logging.LOG_ERROR)
     call_callback(self.on_error, err, details)
예제 #31
0
    def connection_lost(self, func_name, reason):
        self.close()
        Logging.log('{0}: connection to hsdev lost: {1}'.format(func_name, reason), Logging.LOG_ERROR)

        # send error to callbacks
        with self.request_map as requests:
            for (callbacks, _, _) in requests.values():
                callbacks.call_on_error('connection lost', {})
            requests.clear()
예제 #32
0
def run_build(view, project_name, project_dir, config):
    # Don't build if a build is already running for this project
    # We compare the project_name for simplicity (projects with same
    # names are of course possible, but unlikely, so we let them wait)
    if project_name in PROJECTS_BEING_BUILT:
        Logging.log(
            "Waiting for build action on '%s' to complete." % project_name,
            Logging.LOG_WARNING)
        Common.show_status_message('Already building %s' % project_name,
                                   is_ok=False,
                                   priority=5)
        return

    # Set project as building
    PROJECTS_BEING_BUILT.add(project_name)

    build_tool_name = Settings.PLUGIN.haskell_build_tool
    if build_tool_name == 'stack' and not is_stack_project(
            project_dir):  # rollback to cabal
        build_tool_name = 'cabal'

    tool = BUILD_TOOL[build_tool_name]

    # Title of tool: Cabal, Stack
    tool_title = tool['name']
    # Title of action: Cleaning, Building, etc.
    action_title = config['message']
    # Tool name: cabal
    tool_name = tool['command']
    # Tool arguments (commands): build, clean, etc.
    tool_steps = config['steps'][build_tool_name]

    # Config override
    override_config = Settings.get_project_setting(view, 'stack_config_file')

    override_args = []
    if override_config:
        override_args = ['--stack-yaml', override_config]
    # Assemble command lines to run (possibly multiple steps)
    commands = [[tool_name] + step + override_args for step in tool_steps]

    Logging.log('running build commands: {0}'.format(commands),
                Logging.LOG_TRACE)

    def done_callback():
        # Set project as done being built so that it can be built again
        PROJECTS_BEING_BUILT.remove(project_name)

    # Run them
    ParseOutput.run_chain_build_thread(view,
                                       project_dir,
                                       '{0} {1} with {2}'.format(
                                           action_title, project_name,
                                           tool_title),
                                       commands,
                                       on_done=done_callback)
예제 #33
0
 def update_markers_across_views(self):
     '''Mark the regions in open views where errors were found.
     '''
     begin_time = time.clock()
     for win in sublime.windows():
         for view in win.views():
             self.update_markers_in_view(view)
     end_time = time.clock()
     Logging.log('total time to mark {0} diagnostics: {1} seconds'.format(len(self.messages), end_time - begin_time),
                 Logging.LOG_DEBUG)
예제 #34
0
    def call_error(self, err, details):
        self.log_time()
        comb_details = ', '.join(['{0}: {1}'.format(k, v) for k, v in details.items()])
        Logging.log('{0} returns error: {1}\n{2}'.format(self.command, err, comb_details), Logging.LOG_ERROR)

        retval = None
        for err_func in reversed(self.on_error):
            retval = err_func(err, details)

        return retval
예제 #35
0
    def __init__(self, project, project_dir, opt_args):
        if debug_any():
            print('Starting \'ghc-mod\' for project {0}'.format(project))

        self.ghcmod = None
        self.action_lock = None
        self.stderr_drain = None
        self.cmd = []
        self.diag_prefix = 'ghc-mod: project ' + project

        win = sublime.active_window()
        msg = 'Error and diagnostic output from ' + self.diag_prefix
        banner = '~' * len(msg)
        self.output_panel = Common.output_panel(
            win,
            panel_name=self.diag_prefix,
            text='\n'.join([banner, msg, banner, '', '']))
        panel_settings = self.output_panel.settings()
        panel_settings.set("auto_indent", False)
        panel_settings.set("smart_indent", False)

        # if self.exec_with is not None:
        #     if self.exec_with == 'cabal':
        #         cmd += ['cabal']
        #     elif self.exec_with == 'stack':
        #         cmd += ['stack']

        self.cmd += ['ghc-mod']

        # if self.exec_with is not None:
        #     cmd += ['--']

        self.cmd += [
            '-b', '\\n', '--line-prefix',
            self.GHCMOD_OUTPUT_MARKER + ',' + self.GHCMOD_ERROR_MARKER
        ]
        self.cmd += opt_args
        self.cmd += ['legacy-interactive']

        if debug_any():
            print('ghc-mod command: {0}'.format(self.cmd))

        self.ghcmod = ProcHelper.ProcHelper(self.cmd, cwd=project_dir)
        if self.ghcmod.process is not None:
            self.ghcmod.process.stdin = io.TextIOWrapper(
                self.ghcmod.process.stdin, 'utf-8')
            self.ghcmod.process.stdout = io.TextIOWrapper(
                self.ghcmod.process.stdout, 'utf-8')
            self.action_lock = threading.Lock()
            self.stderr_drain = OutputCollector.DescriptorDrain(
                self.diag_prefix, self.ghcmod.process.stderr)
            self.stderr_drain.start()
        else:
            Logging.log('Did not start ghc-mod ({0}) successfully.'.format(
                self.diag_prefix))
예제 #36
0
 def update_markers_across_views(self):
     '''Mark the regions in open views where errors were found.
     '''
     begin_time = time.clock()
     for win in sublime.windows():
         for view in win.views():
             self.update_markers_in_view(view)
     end_time = time.clock()
     Logging.log(
         'total time to mark {0} diagnostics: {1} seconds'.format(
             len(self.messages), end_time - begin_time), Logging.LOG_DEBUG)
예제 #37
0
 def connect_backend(self):
     Logging.log('Connecting to \'hsdev\' server at {0}:{1}'.format(self.hostname, self.port), Logging.LOG_INFO)
     retval = True
     self.client = HsDevClient.HsDevClient(self.backend_mgr)
     if self.client.connect(self.hostname, self.port):
         # For a local hsdev server that we started, send the link command so that it exits when we exit.
         if self.is_local_hsdev:
             self.link()
     else:
         Logging.log('Connections to \'hsdev\' server unsuccessful, see tracebacks to diagnose.', Logging.LOG_ERROR)
         retval = False
     return retval
예제 #38
0
    def start_backend(self):
        retval = True
        if self.is_local_hsdev:
            Logging.log('Starting local \'hsdev\' server', Logging.LOG_INFO)

            use_log_level = (self.version >= [0, 2, 3, 2])
            log_config = Settings.PLUGIN.hsdev_log_config
            log_level = Settings.PLUGIN.hsdev_log_level

            cmd = self.concat_args([(True, ["hsdev"]),
                                    (True, ["run"]),
                                    (self.port, ["--port", str(self.port)]),
                                    (self.cache, ["--cache", self.cache]),
                                    (self.log_file, ["--log", self.log_file]),
                                    (not use_log_level and log_config, ["--log-config", log_config]),
                                    (use_log_level, ["--log-level", log_level])])

            hsdev_proc = ProcHelper.exec_with_wrapper(self.exec_with, self.install_dir, cmd)
            if hsdev_proc.process is not None:
                # Use TextIOWrapper here because it combines decoding with newline handling,
                # which means less to maintain.
                hsdev_proc.process.stdout = io.TextIOWrapper(hsdev_proc.process.stdout, 'utf-8')
                hsdev_proc.process.stderr = io.TextIOWrapper(hsdev_proc.process.stderr, 'utf-8')

                # Read and wait for hsdev's startup messge. 15 seconds should be enough time for the message to appear.
                # Otherwise, kill the thread because we don't want to get stuck waiting forever.
                startup_reader = HsDevStartupReader(hsdev_proc.process.stdout)
                startup_reader.start()
                startup_reader.wait_startup(15.0)
                if startup_reader.successful():
                    port = startup_reader.port()
                    if port != self.port:
                        Logging.log('hsdev: server port changed, was {0}, now {1}'.format(self.port, port), Logging.LOG_WARNING)
                        self.port = port
                    self.drain_stdout = OutputCollector.DescriptorDrain('hsdev stdout', hsdev_proc.process.stdout)
                    self.drain_stderr = OutputCollector.DescriptorDrain('hsdev stderr', hsdev_proc.process.stderr)
                    self.drain_stdout.start()
                    self.drain_stderr.start()
                    self.hsdev_process = hsdev_proc

                    Logging.log('Local \'hsdev\' server started successfully.', Logging.LOG_INFO)
                else:
                    # This is a bit of a "Hail Mary!" because readline() could just hang forever. Just to make sure,
                    # kill the process too!
                    startup_reader.stop()
                    hsdev_proc.process.kill()
                    if hsdev_proc.process_err is not None:
                        Logging.log('Possible reason for timeout: {0}'.format(hsdev_proc.process_err))
                    self.hsdev_process = None
                    retval = False

                    sublime.error_message('Timed out waiting for \'hsdev\' to start up.')
            else:
                errmsg = 'Could not start local \'hsdev\' server because:\n\n' + hsdev_proc.process_err
                sublime.error_message(errmsg)
                self.hsdev_process = None
                retval = False

        return retval
예제 #39
0
    def run(self):
        self.end_event.clear()

        while not self.end_event.is_set():
            srvout = self.stdout.readline().strip()
            Logging.log('hsdev initial: {0}'.format(srvout), Logging.LOG_DEBUG)
            if srvout != '':
                start_confirm = re.search(r'[Ss]erver started at port (?P<port>\d+)$', srvout)
                if start_confirm:
                    self.hsdev_port = int(start_confirm.group('port'))
                    Logging.log('hsdev initial: \'hsdev\' server started at port {0}'.format(self.hsdev_port))
                    self.end_event.set()
            else:
                # Got EOF, stop loop.
                self.end_event.set()
예제 #40
0
    def create_connection(self, idx, host, port):
        for retry in range(1, self.CONNECT_TRIES):
            try:
                Logging.log('[pool {0}]: connecting to hsdev server (attempt {1})...'.format(idx, retry), Logging.LOG_INFO)
                # Use 'localhost' instead of the IP dot-quad for systems (and they exist) that are solely
                # IPv6. Makes this code friendlier to IPv4 and IPv6 hybrid systems.
                return socket.create_connection((host, port))

            except (socket.gaierror, IOError):
                # Captures all of the socket exceptions:
                msg = '[pool {0}]: Failed to connect to hsdev {1}:{2}:'.format(idx, host, port)
                Logging.log(msg, Logging.LOG_ERROR)
                print(traceback.format_exc())
                time.sleep(self.CONNECT_DELAY)

        return None
예제 #41
0
    def run_build(self, project_name, project_dir, config):
        # Don't build if a build is already running for this project
        # We compare the project_name for simplicity (projects with same
        # names are of course possible, but unlikely, so we let them wait)
        if project_name in self.PROJECTS_BEING_BUILT:
            Logging.log("Waiting for build action on '%s' to complete." % project_name, Logging.LOG_WARNING)
            Common.sublime_status_message('Already building {0}'.format(project_name))
            return

        # Set project as building
        self.PROJECTS_BEING_BUILT.add(project_name)

        Logging.log('project build tool: {0}'.format(Settings.get_project_setting(self.view, 'haskell_build_tool')),
                    Logging.LOG_DEBUG)
        Logging.log('settings build tool: {0}'.format(Settings.PLUGIN.haskell_build_tool), Logging.LOG_DEBUG)

        build_tool_name = Settings.get_project_setting(self.view, 'haskell_build_tool', Settings.PLUGIN.haskell_build_tool)
        if build_tool_name == 'stack' and not self.is_stack_project(project_dir):  # rollback to cabal
            build_tool_name = 'cabal'

        tool = self.BUILD_TOOL[build_tool_name]

        # Title of tool: Cabal, Stack
        tool_title = tool['name']
        # Title of action: Cleaning, Building, etc.
        action_title = config['message']
        # Tool name: cabal
        tool_name = tool['command']
        # Tool arguments (commands): build, clean, etc.
        tool_steps = config['steps'][build_tool_name]

        # Config override
        override_args = []
        override_config = Settings.get_project_setting(self.view, 'active_stack_config') if tool_name == 'stack' else ''
        if override_config:
            override_args = ['--stack-yaml', override_config]
        # Assemble command lines to run (possibly multiple steps)
        commands = [[tool_name] + override_args + step if isinstance(step, list) else step for step in tool_steps]

        Logging.log('running build commands: {0}'.format(commands), Logging.LOG_TRACE)

        # Run them
        ## banner = '{0} {1} with {2}\ncommands:\n{3}'.format(action_title, project_name, tool_title, commands)
        banner = '{0} {1} with {2}'.format(action_title, project_name, tool_title)
        Logging.log(banner, Logging.LOG_DEBUG)
        Utils.run_async('wait_for_chain_to_complete', self.wait_for_chain_to_complete, self.view, project_name, project_dir,
                        banner, commands)
예제 #42
0
    def __init__(self, backend_mgr, local=True, port=HSDEV_DEFAULT_PORT, host=HSDEV_DEFAULT_HOST, **kwargs):
        super().__init__(backend_mgr)
        Logging.log('{0}.__init__({1}, {2})'.format(type(self).__name__, host, port), Logging.LOG_INFO)

        # Sanity checking:
        exec_with = kwargs.get('exec-with')
        install_dir = kwargs.get('install-dir')
        if bool(exec_with) ^ bool(install_dir):
            if install_dir is None:
                sublime.error_message('\n'.join(['\'exec_with\' requires an \'install_dir\'.',
                                                 '',
                                                 'Please check your \'backends\' configuration and retry.']))
                raise RuntimeError('\'exec_with\' requires an \'install_dir\'.')
            else:
                sublime.error_message('\n'.join(['\'install_dir\' requires an \'exec_with\'.',
                                                 '',
                                                 'Please check your \'backends\' configuration and retry.']))
                raise RuntimeError('\'install_dir\' requires an \'exec_with\'.')
        elif exec_with and exec_with not in ['stack', 'cabal', 'cabal-new-build']:
            sublime.error_message('\n'.join(['Invalid backend \'exec_with\': {0}'.format(exec_with),
                                             '',
                                             'Valid values are "cabal", "cabal-new-build" or "stack".',
                                             'Please check your \'backends\' configuration and retry.']))
            raise RuntimeError('Invalid backend \'exec_with\': {0}'.format(exec_with))

        # Local hsdev server process and params
        self.is_local_hsdev = local
        self.hsdev_process = None
        self.cache = os.path.join(Common.sublime_haskell_cache_path(), 'hsdev')
        self.log_file = os.path.join(Common.sublime_haskell_cache_path(), 'hsdev', 'hsdev.log')
        self.exec_with = exec_with
        self.install_dir = Utils.normalize_path(install_dir) if install_dir is not None else None
        # Keep track of the hsdev version early. Needed to patch command line arguments later.
        self.version = HsDevBackend.hsdev_version(self.exec_with, self.install_dir)

        self.drain_stdout = None
        self.drain_stderr = None
        # Connection params
        self.port = port
        self.hostname = host
        if self.is_local_hsdev:
            self.hostname = self.HSDEV_DEFAULT_HOST
        self.client = None
        self.serial_lock = threading.RLock()
        self.request_serial = 1
예제 #43
0
    def make_augmented_path():
        ''' Generate the augmented PATH for subprocesses: adds the appropriate cabal/stack local install directory
        ($HOME/.local/bin for *nix, %APPDATA%/local/bin for Windows) and updates PATH with `add_to_PATH` extras.
        '''
        std_places = []
        if Settings.PLUGIN.add_standard_dirs:
            std_places.append("$HOME/.local/bin" if not Utils.is_windows() else "%APPDATA%/local/bin")
            if Utils.is_macosx():
                std_places.append('$HOME/Library/Haskell/bin')
            std_places += CabalConfigRdr.cabal_config()
            std_places = [dir for dir in [Utils.normalize_path(path) for path in std_places] if os.path.isdir(dir)]

        add_to_path = list(filter(os.path.isdir, map(Utils.normalize_path, Settings.PLUGIN.add_to_path)))

        Logging.log("std_places = {0}".format(std_places), Logging.LOG_INFO)
        Logging.log("add_to_PATH = {0}".format(add_to_path), Logging.LOG_INFO)

        return os.pathsep.join(add_to_path + std_places)
예제 #44
0
def exec_with_wrapper(exec_with, install_dir, cmd_list):
    '''Wrapper function for inserting the execution wrapper, e.g., 'cabal exec' or 'stack exec'

    :returns: Process object from ProcHelper.
    '''

    proc_args = {}
    if exec_with is not None:
        cmd_list = exec_wrapper_cmd(exec_with, cmd_list)
        if install_dir is not None:
            proc_args['cwd'] = Utils.normalize_path(install_dir)
        else:
            raise RuntimeError('ProcHelper.exec_with_wrapper: invalid install_dir (None)')
    else:
        cmd = Which.which(cmd_list[0], ProcHelper.get_extended_path())
        if cmd is not None:
            cmd_list[0] = cmd

    Logging.log('ProcHelper.exec_with_wrapper: {0} in {1}'.format(cmd_list, proc_args.get('cwd')), Logging.LOG_DEBUG)
    return ProcHelper(cmd_list, **proc_args)
예제 #45
0
    def connect(self, host, port):
        for i in range(0, self.N_SOCKETS):
            sock = self.create_connection(i, host, port)
            if sock is not None:
                hsconn = HsDevConnection(sock, self, i)
                self.socket_pool.insert(i, hsconn)
            else:
                # Something went wrong, terminate all connnections:
                for sock in self.socket_pool:
                    try:
                        sock.close()
                    except OSError:
                        pass
                self.socket_pool = []
                return False

        # We were successful, start all of the socket threads, make them available...
        for sock in self.socket_pool:
            sock.start_connection()

        Logging.log('Connections established to \'hsdev\' server.', Logging.LOG_INFO)
        return True
예제 #46
0
    def dispatch_response(self, resp):
        resp_id = resp.get('id')
        if not resp_id and 'request' in resp:
            # Error without an id, id is in the 'request' key's content.
            orig_req = json.loads(resp['request'])
            resp_id = orig_req.get('id')
        if resp_id:
            with self.request_map as requests:
                reqdata = requests.get(resp_id)
                if reqdata:
                    callbacks, wait_event, _ = reqdata

                    # Unconditionally call the notify callback:
                    if 'notify' in resp:
                        if Settings.COMPONENT_DEBUG.callbacks:
                            print('id {0}: notify callback'.format(resp_id))
                        callbacks.call_notify(resp['notify'])

                    if 'result' in resp or 'error' in resp:
                        if wait_event:
                            requests[resp_id] = (callbacks, wait_event, resp)
                            # The wait-er calls callbacks, cleans up after the request.
                            wait_event.set()
                        else:
                            # Enqueue for async receiver: Put the (resp_id, resp, reqdata) tuple onto the queue
                            del requests[resp_id]
                            if 'result' in resp:
                                Utils.run_async('result {0}'.format(resp_id), callbacks.call_response, resp['result'])
                            elif 'error' in resp:
                                err = resp.pop("error")
                                Utils.run_async('error {0}'.format(resp_id), callbacks.call_error, err, resp)
                else:
                    msg = 'HsDevClient.receiver: request data expected for {0}, none found.'.format(resp_id)
                    Logging.log(msg, Logging.LOG_WARNING)
        elif Logging.is_log_level(Logging.LOG_ERROR):
            print('HsDevClient.receiver: request w/o id\n{0}'.format(pprint.pformat(resp)))
예제 #47
0
    def is_available(**kwargs):
        # Yes, this is slightly redundant because eventually __init__ does the same thing for a class
        # instance.
        exec_with = kwargs.get('exec-with')
        install_dir = kwargs.get('install-dir')
        local = kwargs.get('local', False)
        exec_install_set = not bool(exec_with) ^ bool(install_dir)
        backend_name = kwargs.get('backend_name', 'not specified.')
        if exec_install_set or local:
            if not exec_install_set:
                # Either exec-with or install-dir isn't set, so the corresponding configuration target is unavailable.
                return False

            hsdev_ver = HsDevBackend.hsdev_version(exec_with, install_dir)
            str_version = '.'.join([str(v) for v in hsdev_ver])
            Logging.log('hsdev version: {0}'.format(str_version), Logging.LOG_INFO)
            retval = hsdev_ver >= HsDevBackend.HSDEV_MIN_VER and hsdev_ver < HsDevBackend.HSDEV_MAX_VER
            if not retval:
                if retval != HsDevBackend.HSDEV_NOT_FOUND:
                    min_version = '.'.join([str(v) for v in HsDevBackend.HSDEV_MIN_VER])
                    max_version = '.'.join([str(v) for v in HsDevBackend.HSDEV_MAX_VER])
                    msg = '\n'.join(['Backend configuration: "{0}"'.format(backend_name),
                                     '',
                                     'Incompatible hsdev, detected version ' + str_version,
                                     'Version should be \u2265 ' + min_version + ' and < ' + max_version])
                else:
                    msg = '\n'.join(['Backend configuration: "{0}"'.format(backend_name),
                                     '',
                                     'Tried executing hsdev to get a version number, not successful.',
                                     'Is hsdev installed (or built, if using stack or cabal exec wrappers)?'])
                sublime.message_dialog(msg)
            return retval

        # Assume that a remote backend is actually available. Ultimately, we might not connect to it, but
        # it is available to us as a backend.
        return True
예제 #48
0
    def initialize(self):
        if self.current_state(self.INITIAL):
            with self.action_lock:
                # Can only start a backend iff in the INITIAL state.
                Logging.log('Starting backend \'{0}\''.format(self.current_backend_name), Logging.LOG_INFO)
                backend_info = self.possible_backends.get(self.current_backend_name, {})
                backend_clazz = self.BACKEND_META.get(backend_info.get('backend') or Backend.NullHaskellBackend.backend_name())
                if backend_clazz is not None:
                    the_backend = backend_clazz(self, **backend_info.get('options', {}))
                else:
                    the_backend = None

                if the_backend is not None:
                    self.state_startup(the_backend)
                    self.state_connect(the_backend)
                    self.state_active(the_backend)

                    if self.current_state(self.INITIAL):
                        # Something failed during startup, revert to the null backend
                        self.set_backend(Backend.NullHaskellBackend(self))
                    elif not self.current_state(self.ACTIVE):
                        state_str = self.STATES_TO_NAME.get(self.state, str(self.state))
                        Logging.log('BackendManager: Invalid state after starting backend: {0}'.format(state_str),
                                    Logging.LOG_ERROR)
예제 #49
0
 def reinspect_all(self):
     Logging.log('reinspect all', Logging.LOG_TRACE)
     with BackendManager.inspector() as insp:
         insp.start_inspect()
예제 #50
0
 def set_selected(self, i):
     if i < 0 or i >= len(self.corrections):
         Logging.log('AutoFixState.set_selected({0}): out of bound'.format(i), Logging.LOG_ERROR)
         return
     self.selected = i
     self.mark()
예제 #51
0
    def call(self, command, opts, callbacks, wait, timeout):
        if not self.verify_connected():
            return None if wait else False

        opts = opts or {}
        opts.update({'no-file': True, 'id': callbacks.ident, 'command': command})

        wait_receive = threading.Event() if wait else None

        if debug_recv():
            def client_call_error(exc, details):
                print('{0}.client_call_error: exc {1} details {2}'.format(type(self).__name__, exc, details))

            callbacks.on_error.append(client_call_error)

        with self.request_map as requests:
            requests[callbacks.ident] = (callbacks, wait_receive, None)

        if debug_send():
            print(u'HsDevClient.call[{0}] cmd \'{1}\' opts\n{2}'.format(callbacks.ident, command, pprint.pformat(opts)))

        try:
            # Randomly choose a connection from the pool -- even if we end up having a stuck sender, this should minimize
            # the probability of a complete hang.
            random.choice(self.socket_pool).send_request(opts)

            retval = True
            if wait:
                if debug_send():
                    print(u'HsDevClient.call: waiting to receive, timeout = {0}.'.format(timeout))
                wait_receive.wait(timeout)
                if debug_send():
                    print(u'HsDevClient.call: done waiting.')

                with self.request_map as requests:
                    if wait_receive.is_set():
                        callbacks, _, resp = requests[callbacks.ident]
                        del requests[callbacks.ident]

                        retval = resp
                        if 'result' in resp:
                            retval = callbacks.call_response(resp['result'])
                        elif 'error' in resp:
                            err = resp.pop("error")
                            retval = callbacks.call_error(err, resp)

                        if Settings.COMPONENT_DEBUG.callbacks:
                            print('HsDevClient.call: wait {0} result {1}'.format(callbacks.ident, retval))
                    else:
                        req = pprint.pformat(opts)
                        errmsg = 'HsDevClient.call: wait_receive event timed out for id {0}\n{1}'.format(callbacks.ident, req)
                        Logging.log(errmsg, Logging.LOG_ERROR)

            if Settings.COMPONENT_DEBUG.socket_pool:
                with self.request_map as request_map:
                    print('id {0} request_map {1}'.format(callbacks.ident, len(request_map)))

            return retval

        except OSError:
            self.connection_lost('call', sys.exc_info()[1])
            self.backend_mgr.lost_connection()
            return False
예제 #52
0
 def log_time(self):
     Logging.log('{0}: {1} seconds'.format(self.command, self.time()), Logging.LOG_TRACE)