def transport_args(self): """Arguments for the transport process.""" args = [ sys.executable, '-u', osp.join(LOCATION, 'transport', 'main.py'), '--folder', self.folder, '--transport-debug', str(get_debug_level()) ] # Replace host and port placeholders host_and_port = '--server-host {host} --server-port {port} '.format( host=self.server_host, port=self.server_port) args += host_and_port.split(' ') # Add socket ports args += ['--zmq-in-port', str(self.zmq_out_port), '--zmq-out-port', str(self.zmq_in_port)] # Adjustments for stdio/tcp if self.stdio: args += ['--stdio-server'] if get_debug_level() > 0: args += ['--server-log-file', self.server_log_file] args += self.server_args else: args += ['--external-server'] return args
def start(self): self.zmq_out_socket = self.context.socket(zmq.PAIR) self.zmq_out_port = self.zmq_out_socket.bind_to_random_port('tcp://*') self.zmq_in_socket = self.context.socket(zmq.PAIR) self.zmq_in_socket.set_hwm(0) self.zmq_in_port = self.zmq_in_socket.bind_to_random_port('tcp://*') self.transport_args += [ '--zmq-in-port', self.zmq_out_port, '--zmq-out-port', self.zmq_in_port ] self.lsp_server_log = subprocess.PIPE if get_debug_level() > 0: lsp_server_file = 'lsp_server_{0}.log'.format(self.language) log_file = get_conf_path(osp.join('lsp_logs', lsp_server_file)) if not osp.exists(osp.dirname(log_file)): os.makedirs(osp.dirname(log_file)) self.lsp_server_log = open(log_file, 'w') if not self.external_server: logger.info('Starting server: {0}'.format(' '.join( self.server_args))) creation_flags = 0 if WINDOWS: creation_flags = (subprocess.CREATE_NEW_PROCESS_GROUP | 0x08000000) # CREATE_NO_WINDOW self.lsp_server = subprocess.Popen(self.server_args, stdout=self.lsp_server_log, stderr=subprocess.STDOUT, creationflags=creation_flags) # self.transport_args += self.server_args self.stdout_log = subprocess.PIPE self.stderr_log = subprocess.PIPE if get_debug_level() > 0: stderr_log_file = 'lsp_transport_{0}_err.log'.format(self.language) log_file = get_conf_path(osp.join('lsp_logs', stderr_log_file)) if not osp.exists(osp.dirname(log_file)): os.makedirs(osp.dirname(log_file)) self.stderr_log = open(log_file, 'w') new_env = dict(os.environ) python_path = os.pathsep.join(sys.path)[1:] new_env['PYTHONPATH'] = python_path self.transport_args = list(map(str, self.transport_args)) logger.info('Starting transport: {0}'.format(' '.join( self.transport_args))) self.transport_client = subprocess.Popen(self.transport_args, stdout=self.stdout_log, stderr=self.stderr_log, env=new_env) fid = self.zmq_in_socket.getsockopt(zmq.FD) self.notifier = QSocketNotifier(fid, QSocketNotifier.Read, self) # self.notifier.activated.connect(self.debug_print) self.notifier.activated.connect(self.on_msg_received)
def start_server(self): """Start server.""" # This is not necessary if we're trying to connect to an # external server if self.external_server: return # Set server log file server_log_file = None if get_debug_level() > 0: # Create server log file server_log_fname = 'server_{0}_{1}.log'.format( self.language, os.getpid()) server_log_file = get_conf_path( osp.join('lsp_logs', server_log_fname)) if not osp.exists(osp.dirname(server_log_file)): os.makedirs(osp.dirname(server_log_file)) if self.stdio: if self.language == 'python': self.server_args += ['--log-file', server_log_file] self.transport_args += ['--server-log-file', server_log_file] # Start server with logging options if self.language == 'python': if get_debug_level() == 2: self.server_args.append('-v') elif get_debug_level() == 3: self.server_args.append('-vv') logger.info('Starting server: {0}'.format(' '.join(self.server_args))) # Set the PyLS current working to an empty dir inside # our config one. This avoids the server to pick up user # files such as random.py or string.py instead of the # standard library modules named the same. if self.language == 'python': cwd = get_conf_path('empty_cwd') if not osp.exists(cwd): os.mkdir(cwd) else: cwd = None # Setup server self.server = QProcess(self) self.server.errorOccurred.connect(self.handle_process_errors) self.server.setWorkingDirectory(cwd) self.server.setProcessChannelMode(QProcess.MergedChannels) if server_log_file is not None: self.server.setStandardOutputFile(server_log_file) # Start server self.server.start(self.server_args[0], self.server_args[1:])
def start(self): self.zmq_out_socket = self.context.socket(zmq.PAIR) self.zmq_out_port = self.zmq_out_socket.bind_to_random_port('tcp://*') self.zmq_in_socket = self.context.socket(zmq.PAIR) self.zmq_in_socket.set_hwm(0) self.zmq_in_port = self.zmq_in_socket.bind_to_random_port('tcp://*') self.transport_args += ['--zmq-in-port', self.zmq_out_port, '--zmq-out-port', self.zmq_in_port] self.lsp_server_log = subprocess.PIPE if get_debug_level() > 0: lsp_server_file = 'lsp_server_logfile.log' log_file = get_conf_path(osp.join('lsp_logs', lsp_server_file)) if not osp.exists(osp.dirname(log_file)): os.makedirs(osp.dirname(log_file)) self.lsp_server_log = open(log_file, 'w') if not self.external_server: logger.info('Starting server: {0}'.format( ' '.join(self.server_args))) creation_flags = 0 if WINDOWS: creation_flags = subprocess.CREATE_NEW_PROCESS_GROUP self.lsp_server = subprocess.Popen( self.server_args, stdout=self.lsp_server_log, stderr=subprocess.STDOUT, creationflags=creation_flags) # self.transport_args += self.server_args self.stdout_log = subprocess.PIPE self.stderr_log = subprocess.PIPE if get_debug_level() > 0: stderr_log_file = 'lsp_client_{0}.log'.format(self.language) log_file = get_conf_path(osp.join('lsp_logs', stderr_log_file)) if not osp.exists(osp.dirname(log_file)): os.makedirs(osp.dirname(log_file)) self.stderr_log = open(log_file, 'w') new_env = dict(os.environ) python_path = os.pathsep.join(sys.path)[1:] new_env['PYTHONPATH'] = python_path self.transport_args = map(str, self.transport_args) self.transport_client = subprocess.Popen(self.transport_args, stdout=self.stdout_log, stderr=self.stderr_log, env=new_env) fid = self.zmq_in_socket.getsockopt(zmq.FD) self.notifier = QSocketNotifier(fid, QSocketNotifier.Read, self) # self.notifier.activated.connect(self.debug_print) self.notifier.activated.connect(self.on_msg_received)
def __init__(self, parent, server_settings={}, folder=getcwd_or_home(), language='python'): QObject.__init__(self) # LSPMethodProviderMixIn.__init__(self) self.manager = parent self.zmq_in_socket = None self.zmq_out_socket = None self.zmq_in_port = None self.zmq_out_port = None self.transport_client = None self.language = language self.initialized = False self.ready_to_close = False self.request_seq = 1 self.req_status = {} self.watched_files = {} self.req_reply = {} self.transport_args = [ sys.executable, '-u', osp.join(LOCATION, 'transport', 'main.py') ] self.external_server = server_settings.get('external', False) self.stdio = server_settings.get('stdio', False) # Setting stdio on implies that external_server is off if self.stdio and self.external_server: error = ('If server is set to use stdio communication, ' 'then it cannot be an external server') logger.error(error) raise AssertionError(error) self.folder = folder self.plugin_configurations = server_settings.get('configurations', {}) self.client_capabilites = CLIENT_CAPABILITES self.server_capabilites = SERVER_CAPABILITES self.context = zmq.Context() server_args_fmt = server_settings.get('args', '') server_args = server_args_fmt.format(**server_settings) transport_args = self.external_server_fmt % (server_settings) self.server_args = [] if language == 'python': self.server_args += [sys.executable, '-m'] self.server_args += [server_settings['cmd']] if len(server_args) > 0: self.server_args += server_args.split(' ') self.transport_args += transport_args.split(' ') self.transport_args += ['--folder', folder] self.transport_args += ['--transport-debug', str(get_debug_level())] if not self.stdio: self.transport_args += ['--external-server'] else: self.transport_args += ['--stdio-server'] self.external_server = True
def start_interpreter(self, namespace): """Start Python interpreter""" self.clear() if self.interpreter is not None: self.interpreter.closing() self.interpreter = Interpreter(namespace, self.exitfunc, SysOutput, WidgetProxy, get_debug_level()) self.interpreter.stdout_write.data_avail.connect(self.stdout_avail) self.interpreter.stderr_write.data_avail.connect(self.stderr_avail) self.interpreter.widget_proxy.sig_set_readonly.connect( self.setReadOnly) self.interpreter.widget_proxy.sig_new_prompt.connect(self.new_prompt) self.interpreter.widget_proxy.sig_edit.connect(self.edit_script) self.interpreter.widget_proxy.sig_wait_input.connect(self.wait_input) if self.multithreaded: self.interpreter.start() # Interpreter banner banner = create_banner(self.message) self.write(banner, prompt=True) # Initial commands for cmd in self.commands: self.run_command(cmd, history=False, new_prompt=False) # First prompt self.new_prompt(self.interpreter.p1) self.refresh.emit() return self.interpreter
def setup_logging(cli_options): """Setup logging with cli options defined by the user.""" if cli_options.debug_info or get_debug_level() > 0: levels = {2: logging.INFO, 3: logging.DEBUG} log_level = levels[get_debug_level()] log_format = '%(asctime)s [%(levelname)s] [%(name)s] -> %(message)s' if cli_options.debug_output == 'file': log_file = 'spyder-debug.log' else: log_file = None logging.basicConfig(level=log_level, format=log_format, filename=log_file, filemode='w+')
def on_msg_received(self): """Process received messages.""" self.notifier.setEnabled(False) while True: try: # events = self.zmq_in_socket.poll(1500) resp = self.zmq_in_socket.recv_pyobj(flags=zmq.NOBLOCK) try: method = resp['method'] logger.debug( '{} response: {}'.format(self.language, method)) except KeyError: pass if 'error' in resp: logger.debug('{} Response error: {}' .format(self.language, repr(resp['error']))) if self.language == 'python': # Show PyLS errors in our error report dialog only in # debug or development modes if get_debug_level() > 0 or DEV: message = resp['error'].get('message', '') traceback = (resp['error'].get('data', {}). get('traceback')) if traceback is not None: traceback = ''.join(traceback) traceback = traceback + '\n' + message self.sig_server_error.emit(traceback) req_id = resp['id'] if req_id in self.req_reply: self.req_reply[req_id](None, {'params': []}) elif 'method' in resp: if resp['method'][0] != '$': if 'id' in resp: self.request_seq = int(resp['id']) if resp['method'] in self.handler_registry: handler_name = ( self.handler_registry[resp['method']]) handler = getattr(self, handler_name) handler(resp['params']) elif 'result' in resp: if resp['result'] is not None: req_id = resp['id'] if req_id in self.req_status: req_type = self.req_status[req_id] if req_type in self.handler_registry: handler_name = self.handler_registry[req_type] handler = getattr(self, handler_name) handler(resp['result'], req_id) self.req_status.pop(req_id) if req_id in self.req_reply: self.req_reply.pop(req_id) except RuntimeError: # This is triggered when a codeeditor instance has been # removed before the response can be processed. pass except zmq.ZMQError: self.notifier.setEnabled(True) return
def exception_occurred(self, text, is_traceback, is_pyls_error=False): """ Exception ocurred in the internal console. Show a QDialog or the internal console to warn the user. """ # Skip errors without traceback or dismiss if (not is_traceback and self.error_dlg is None) or self.dismiss_error: return if CONF.get('main', 'show_internal_errors'): if self.error_dlg is None: self.error_dlg = SpyderErrorDialog(self) self.error_dlg.set_color_scheme( CONF.get('appearance', 'selected')) self.error_dlg.close_btn.clicked.connect(self.close_error_dlg) self.error_dlg.rejected.connect(self.remove_error_dlg) self.error_dlg.details.go_to_error.connect(self.go_to_error) if is_pyls_error: title = "Internal Python Language Server error" self.error_dlg.set_title(title) self.error_dlg.title.setEnabled(False) self.error_dlg.append_traceback(text) self.error_dlg.show() elif DEV or get_debug_level(): self.switch_to_plugin()
def start_interpreter(self, namespace): """Start Python interpreter""" self.clear() if self.interpreter is not None: self.interpreter.closing() self.interpreter = Interpreter(namespace, self.exitfunc, SysOutput, WidgetProxy, get_debug_level()) self.interpreter.stdout_write.data_avail.connect(self.stdout_avail) self.interpreter.stderr_write.data_avail.connect(self.stderr_avail) self.interpreter.widget_proxy.sig_set_readonly.connect(self.setReadOnly) self.interpreter.widget_proxy.sig_new_prompt.connect(self.new_prompt) self.interpreter.widget_proxy.sig_edit.connect(self.edit_script) self.interpreter.widget_proxy.sig_wait_input.connect(self.wait_input) if self.multithreaded: self.interpreter.start() # Interpreter banner banner = create_banner(self.message) self.write(banner, prompt=True) # Initial commands for cmd in self.commands: self.run_command(cmd, history=False, new_prompt=False) # First prompt self.new_prompt(self.interpreter.p1) self.refresh.emit() return self.interpreter
def closing_plugin(self, cancelable=False): """Perform actions before parent main window is closed.""" for term in self.terms: term.close() self.server.terminate() if get_debug_level() > 0: self.server_stdout.close() self.server_stderr.close() return True
def _depopulate_tools_menu(self): """Add base actions and menus to the Tools menu.""" mainmenu = self.get_plugin(Plugins.MainMenu) if WinUserEnvDialog is not None: mainmenu.remove_item_from_application_menu( ApplicationActions.SpyderWindowsEnvVariables, menu_id=ApplicationMenus.Tools) if get_debug_level() >= 2: mainmenu.remove_item_from_application_menu( ApplicationPluginMenus.DebugLogsMenu, menu_id=ApplicationMenus.Tools)
def server_args(self): """Arguments for the server process.""" args = [] if self.language == 'python': args += [sys.executable, '-m'] args += [self._server_cmd] # Replace host and port placeholders host_and_port = self._server_args.format(host=self.server_host, port=self.server_port) if len(host_and_port) > 0: args += host_and_port.split(' ') if self.language == 'python' and get_debug_level() > 0: args += ['--log-file', self.server_log_file] if get_debug_level() == 2: args.append('-v') elif get_debug_level() == 3: args.append('-vv') return args
def setup_logging(cli_options): """Setup logging with cli options defined by the user.""" if cli_options.debug_info or get_debug_level() > 0: levels = {2: logging.INFO, 3: logging.DEBUG} log_level = levels[get_debug_level()] log_format = '%(asctime)s [%(levelname)s] [%(name)s] -> %(message)s' console_filters = cli_options.filter_log.split(',') console_filters = [x.strip() for x in console_filters] console_filters = console_filters + FILTER_NAMES console_filters = [x for x in console_filters if x != ''] handlers = [logging.StreamHandler()] if cli_options.debug_output == 'file': log_file = 'spyder-debug.log' handlers.append(logging.FileHandler(filename=log_file, mode='w+')) else: log_file = None match_func = lambda x: True if console_filters != [''] and len(console_filters) > 0: dafsa = DAFSA(console_filters) match_func = lambda x: (dafsa.lookup(x, stop_on_prefix=True) is not None) formatter = logging.Formatter(log_format) class ModuleFilter(logging.Filter): """Filter messages based on module name prefix.""" def filter(self, record): return match_func(record.name) filter = ModuleFilter() root_logger.setLevel(log_level) for handler in handlers: handler.addFilter(filter) handler.setFormatter(formatter) handler.setLevel(log_level) root_logger.addHandler(handler)
def _populate_tools_menu(self): """Add base actions and menus to the Tools menu.""" mainmenu = self.get_plugin(Plugins.MainMenu) if WinUserEnvDialog is not None: mainmenu.add_item_to_application_menu( self.winenv_action, menu_id=ApplicationMenus.Tools, section=ToolsMenuSections.Tools) if get_debug_level() >= 2: mainmenu.add_item_to_application_menu( self.debug_logs_menu, menu_id=ApplicationMenus.Tools, section=ToolsMenuSections.Extras)
def __init__(self, name, plugin, parent): """Widget constructor.""" self.terms = [] super().__init__(name, plugin, parent) # Attributes self.tab_widget = None self.menu_actions = None self.server_retries = 0 self.server_ready = False self.font = None self.port = select_port(default_port=8071) self.stdout_file = None self.stderr_file = None if get_debug_level() > 0: self.stdout_file = osp.join(os.getcwd(), 'spyder_terminal_out.log') self.stderr_file = osp.join(os.getcwd(), 'spyder_terminal_err.log') self.project_path = None self.current_file_path = None self.current_cwd = os.getcwd() # Widgets self.main = parent self.find_widget = FindTerminal(self) self.find_widget.hide() layout = QVBoxLayout() # Tab Widget self.tabwidget = Tabs(self, rename_tabs=True) self.tabwidget.currentChanged.connect(self.refresh_plugin) self.tabwidget.move_data.connect(self.move_tab) self.tabwidget.set_close_function(self.close_term) if (hasattr(self.tabwidget, 'setDocumentMode') and not sys.platform == 'darwin'): # Don't set document mode to true on OSX because it generates # a crash when the console is detached from the main window # Fixes Issue 561 self.tabwidget.setDocumentMode(True) layout.addWidget(self.tabwidget) layout.addWidget(self.find_widget) self.setLayout(layout) css = qstylizer.style.StyleSheet() css.QTabWidget.pane.setValues(border=0) self.setStyleSheet(css.toString()) self.__wait_server_to_start()
def __init__(self, parent, server_args_fmt='', server_settings={}, external_server=False, folder=getcwd(), language='python', plugin_configurations={}): QObject.__init__(self) # LSPMethodProviderMixIn.__init__(self) self.manager = parent self.zmq_in_socket = None self.zmq_out_socket = None self.zmq_in_port = None self.zmq_out_port = None self.transport_client = None self.language = language self.initialized = False self.ready_to_close = False self.request_seq = 1 self.req_status = {} self.plugin_registry = {} self.watched_files = {} self.req_reply = {} self.transport_args = [ sys.executable, '-u', osp.join(LOCATION, 'transport', 'main.py') ] self.external_server = external_server self.folder = folder self.plugin_configurations = plugin_configurations self.client_capabilites = CLIENT_CAPABILITES self.server_capabilites = SERVER_CAPABILITES self.context = zmq.Context() server_args = server_args_fmt % (server_settings) # transport_args = self.local_server_fmt % (server_settings) # if self.external_server: transport_args = self.external_server_fmt % (server_settings) self.server_args = [server_settings['cmd']] self.server_args += server_args.split(' ') self.transport_args += transport_args.split(' ') self.transport_args += ['--folder', folder] self.transport_args += ['--transport-debug', str(get_debug_level())]
def handle_exception(self, text, is_traceback, is_pyls_error=False, is_faulthandler_report=False): """ Exception ocurred in the internal console. Show a QDialog or the internal console to warn the user. """ # Skip errors without traceback or dismiss if (not is_traceback and self.error_dlg is None) or self.dismiss_error: return if self.get_option('show_internal_errors'): if self.error_dlg is None: self.error_dlg = SpyderErrorDialog(self) self.error_dlg.set_color_scheme(self.get_option('color_theme')) self.error_dlg.close_btn.clicked.connect(self.close_error_dlg) self.error_dlg.rejected.connect(self.remove_error_dlg) self.error_dlg.details.go_to_error.connect(self.go_to_error) if is_pyls_error: title = "Internal Python Language Server error" self.error_dlg.set_title(title) self.error_dlg.title.setEnabled(False) if is_faulthandler_report: title = "Segmentation fault crash" self.error_dlg.set_title(title) self.error_dlg.title.setEnabled(False) self.error_dlg.main_label.setText( _("<h3>Spyder crashed during last session</h3>")) self.error_dlg.submit_btn.setEnabled(True) self.error_dlg.steps_text.setText( _("Please provide any additional information you " "might have about the crash.")) self.error_dlg.set_require_minimum_length(False) self.error_dlg.append_traceback(text) self.error_dlg.show() elif DEV or get_debug_level(): self.change_visibility(True, True)
def __init__(self, parent, server_args_fmt='', server_settings={}, external_server=False, folder=getcwd(), language='python', plugin_configurations={}): QObject.__init__(self) # LSPMethodProviderMixIn.__init__(self) self.manager = parent self.zmq_in_socket = None self.zmq_out_socket = None self.zmq_in_port = None self.zmq_out_port = None self.transport_client = None self.language = language self.initialized = False self.ready_to_close = False self.request_seq = 1 self.req_status = {} self.plugin_registry = {} self.watched_files = {} self.req_reply = {} self.transport_args = [sys.executable, '-u', osp.join(LOCATION, 'transport', 'main.py')] self.external_server = external_server self.folder = folder self.plugin_configurations = plugin_configurations self.client_capabilites = CLIENT_CAPABILITES self.server_capabilites = SERVER_CAPABILITES self.context = zmq.Context() server_args = server_args_fmt % (server_settings) # transport_args = self.local_server_fmt % (server_settings) # if self.external_server: transport_args = self.external_server_fmt % (server_settings) self.server_args = [server_settings['cmd']] self.server_args += server_args.split(' ') self.transport_args += transport_args.split(' ') self.transport_args += ['--folder', folder] self.transport_args += ['--transport-debug', str(get_debug_level())]
def start_transport(self): """Start transport layer.""" self.transport_args = list(map(str, self.transport_args)) logger.info('Starting transport: {0}'.format(' '.join( self.transport_args))) self.transport = QProcess(self) self.transport.errorOccurred.connect(self.handle_process_errors) # Modifying PYTHONPATH to run transport in development mode or # tests if DEV or running_under_pytest(): env = QProcessEnvironment() if running_under_pytest(): env.insert('PYTHONPATH', os.pathsep.join(sys.path)[:]) else: env.insert('PYTHONPATH', os.pathsep.join(sys.path)[1:]) self.transport.setProcessEnvironment(env) # Set transport log file transport_log_file = None if get_debug_level() > 0: transport_log_fname = 'transport_{0}_{1}.log'.format( self.language, os.getpid()) transport_log_file = get_conf_path( osp.join('lsp_logs', transport_log_fname)) if not osp.exists(osp.dirname(transport_log_file)): os.makedirs(osp.dirname(transport_log_file)) # Set channel properties if self.stdio: self.transport_args += self.server_args self.transport.setProcessChannelMode(QProcess.SeparateChannels) if transport_log_file is not None: self.transport.setStandardErrorFile(transport_log_file) else: self.transport.setProcessChannelMode(QProcess.MergedChannels) if transport_log_file is not None: self.transport.setStandardOutputFile(transport_log_file) # Start transport self.transport.start(self.transport_args[0], self.transport_args[1:])
def exception_occurred(self, text, is_traceback): """ Exception ocurred in the internal console. Show a QDialog or the internal console to warn the user. """ # Skip errors without traceback or dismiss if (not is_traceback and self.error_dlg is None) or self.dismiss_error: return if CONF.get('main', 'show_internal_errors'): if self.error_dlg is None: self.error_dlg = SpyderErrorDialog(self) self.error_dlg.close_btn.clicked.connect(self.close_error_dlg) self.error_dlg.rejected.connect(self.remove_error_dlg) self.error_dlg.details.go_to_error.connect(self.go_to_error) self.error_dlg.show() self.error_dlg.append_traceback(text) elif DEV or get_debug_level(): self.dockwidget.show() self.dockwidget.raise_()
def _get_log_filename(self, kind): """ Get filename to redirect server or transport logs to in debugging mode. Parameters ---------- kind: str It can be "server" or "transport". """ if get_debug_level() == 0: return None fname = '{0}_{1}_{2}.log'.format(kind, self.language, os.getpid()) location = get_conf_path(osp.join('lsp_logs', fname)) # Create directory that contains the file, in case it doesn't # exist if not osp.exists(osp.dirname(location)): os.makedirs(osp.dirname(location)) return location
""" Source code analysis utilities """ import sys import re import os import tempfile import traceback # Local import from spyder.config.base import _, get_debug_level from spyder.utils import programs, encoding from spyder.py3compat import to_text_string, to_binary_string, PY3 DEBUG_EDITOR = get_debug_level() >= 3 #============================================================================== # Pyflakes/pep8 code analysis #============================================================================== TASKS_PATTERN = r"(^|#)[ ]*(TODO|FIXME|XXX|HINT|TIP|@todo|" \ r"HACK|BUG|OPTIMIZE|!!!|\?\?\?)([^#]*)" #TODO: this is a test for the following function def find_tasks(source_code): """Find tasks in source code (TODO, FIXME, XXX, ...)""" results = [] for line, text in enumerate(source_code.splitlines()): for todo in re.findall(TASKS_PATTERN, text): todo_text = (todo[-1].strip(' :').capitalize() if todo[-1] else todo[-2])
def __init__(self, parent): """Widget constructor.""" SpyderPluginWidget.__init__(self, parent) self.tab_widget = None self.menu_actions = None self.server_retries = 0 self.server_ready = False self.port = select_port(default_port=8071) self.cmd = find_program(self.get_option('shell')) self.CONF = CONF self.server_stdout = subprocess.PIPE self.server_stderr = subprocess.PIPE self.stdout_file = osp.join(os.getcwd(), 'spyder_terminal_out.log') self.stderr_file = osp.join(os.getcwd(), 'spyder_terminal_err.log') if get_debug_level() > 0: self.server_stdout = open(self.stdout_file, 'w') self.server_stderr = open(self.stderr_file, 'w') self.server = subprocess.Popen([ sys.executable, '-m', 'spyder_terminal.server', '--port', str(self.port), '--shell', self.cmd ], stdout=self.server_stdout, stderr=self.server_stderr) self.main = parent self.terms = [] self.untitled_num = 0 self.project_path = None self.current_file_path = None self.current_cwd = os.getcwd() self.find_widget = FindTerminal(self) self.find_widget.hide() try: # Spyder 3 self.initialize_plugin() except AttributeError: # Spyder 4 pass layout = QVBoxLayout() new_term_btn = create_toolbutton(self, icon=ima.icon('expand_selection'), tip=_('Open a new terminal'), triggered=self.create_new_term) corner_widgets = { Qt.TopRightCorner: [new_term_btn, self.options_button] } self.tabwidget = Tabs(self, menu=self._options_menu, actions=self.menu_actions, corner_widgets=corner_widgets, rename_tabs=True) if hasattr(self.tabwidget, 'setDocumentMode') \ and not sys.platform == 'darwin': # Don't set document mode to true on OSX because it generates # a crash when the console is detached from the main window # Fixes Issue 561 self.tabwidget.setDocumentMode(True) self.tabwidget.currentChanged.connect(self.refresh_plugin) self.tabwidget.move_data.connect(self.move_tab) self.tabwidget.set_close_function(self.close_term) layout.addWidget(self.tabwidget) layout.addWidget(self.find_widget) self.setLayout(layout) self.color_scheme = CONF.get('appearance', 'ui_theme') self.theme = CONF.get('appearance', 'selected') self.__wait_server_to_start()
def handle_exception(self, error_data, sender=None, internal_plugins=None): """ Exception ocurred in the internal console. Show a QDialog or the internal console to warn the user. Handle any exception that occurs during Spyder usage. Parameters ---------- error_data: dict The dictionary containing error data. The expected keys are: >>> error_data= { "text": str, "is_traceback": bool, "repo": str, "title": str, "label": str, "steps": str, } sender: spyder.api.plugins.SpyderPluginV2, optional The sender plugin. Default is None. Notes ----- The `is_traceback` key indicates if `text` contains plain text or a Python error traceback. The `title` and `repo` keys indicate how the error data should customize the report dialog and Github error submission. The `label` and `steps` keys allow customizing the content of the error dialog. """ text = error_data.get("text", None) is_traceback = error_data.get("is_traceback", False) title = error_data.get("title", "") label = error_data.get("label", "") steps = error_data.get("steps", "") # Skip errors without traceback (and no text) or dismiss if ((not text and not is_traceback and self.error_dlg is None) or self.dismiss_error): return if internal_plugins is None: internal_plugins = find_internal_plugins() if internal_plugins: internal_plugin_names = [] for __, val in internal_plugins.items(): name = getattr(val, 'NAME', getattr(val, 'CONF_SECTION')) internal_plugin_names.append(name) sender_name = getattr(val, 'NAME', getattr(val, 'CONF_SECTION')) is_internal_plugin = sender_name in internal_plugin_names else: is_internal_plugin = False repo = "spyder-ide/spyder" if sender is not None and not is_internal_plugin: repo = error_data.get("repo", None) try: plugin_name = sender.NAME except Exception: plugin_name = sender.CONF_SECTION if repo is None: raise Exception( 'External plugin "{}" does not define "repo" key in ' 'the "error_data" dictionary!'.format(plugin_name) ) if self.get_option('show_internal_errors'): if self.error_dlg is None: self.error_dlg = SpyderErrorDialog(self) self.error_dlg.set_color_scheme(self.get_option('color_theme')) self.error_dlg.close_btn.clicked.connect(self.close_error_dlg) self.error_dlg.rejected.connect(self.remove_error_dlg) self.error_dlg.details.go_to_error.connect(self.go_to_error) # Set the report repository self.error_dlg.set_github_repo_org(repo) if title: self.error_dlg.set_title(title) self.error_dlg.title.setEnabled(False) if label: self.error_dlg.main_label.setText(label) self.error_dlg.submit_btn.setEnabled(True) if steps: self.error_dlg.steps_text.setText(steps) self.error_dlg.set_require_minimum_length(False) self.error_dlg.append_traceback(text) self.error_dlg.show() elif DEV or get_debug_level(): self.change_visibility(True, True)
def start(self): self.zmq_out_socket = self.context.socket(zmq.PAIR) self.zmq_out_port = self.zmq_out_socket.bind_to_random_port('tcp://*') self.zmq_in_socket = self.context.socket(zmq.PAIR) self.zmq_in_socket.set_hwm(0) self.zmq_in_port = self.zmq_in_socket.bind_to_random_port('tcp://*') self.transport_args += ['--zmq-in-port', self.zmq_out_port, '--zmq-out-port', self.zmq_in_port] server_log = subprocess.PIPE if get_debug_level() > 0: # Create server log file server_log_fname = 'server_{0}.log'.format(self.language) server_log_file = get_conf_path(osp.join('lsp_logs', server_log_fname)) if not osp.exists(osp.dirname(server_log_file)): os.makedirs(osp.dirname(server_log_file)) server_log = open(server_log_file, 'w') # Start server with logging options if get_debug_level() == 2: self.server_args.append('-v') elif get_debug_level() == 3: self.server_args.append('-vv') if not self.external_server: logger.info('Starting server: {0}'.format( ' '.join(self.server_args))) creation_flags = 0 if os.name == 'nt': creation_flags = (subprocess.CREATE_NEW_PROCESS_GROUP | 0x08000000) # CREATE_NO_WINDOW if os.environ.get('CI') and os.name == 'nt': # The following patching avoids: # # OSError: [WinError 6] The handle is invalid # # while running our tests in CI services on Windows # (they run fine locally). # See this comment for an explanation: # https://stackoverflow.com/q/43966523/ # 438386#comment74964124_43966523 def patched_cleanup(): pass subprocess._cleanup = patched_cleanup self.lsp_server = subprocess.Popen( self.server_args, stdout=server_log, stderr=subprocess.STDOUT, creationflags=creation_flags) client_log = subprocess.PIPE if get_debug_level() > 0: # Client log file client_log_fname = 'client_{0}.log'.format(self.language) client_log_file = get_conf_path(osp.join('lsp_logs', client_log_fname)) if not osp.exists(osp.dirname(client_log_file)): os.makedirs(osp.dirname(client_log_file)) client_log = open(client_log_file, 'w') new_env = dict(os.environ) python_path = os.pathsep.join(sys.path)[1:] new_env['PYTHONPATH'] = python_path self.transport_args = list(map(str, self.transport_args)) logger.info('Starting transport: {0}' .format(' '.join(self.transport_args))) self.transport_client = subprocess.Popen(self.transport_args, stdout=subprocess.PIPE, stderr=client_log, env=new_env) fid = self.zmq_in_socket.getsockopt(zmq.FD) self.notifier = QSocketNotifier(fid, QSocketNotifier.Read, self) self.notifier.activated.connect(self.on_msg_received) # This is necessary for tests to pass locally! logger.debug('LSP {} client started!'.format(self.language))
def __init__(self, parent, server_settings={}, folder=getcwd_or_home(), language='python'): QObject.__init__(self) self.manager = parent self.zmq_in_socket = None self.zmq_out_socket = None self.zmq_in_port = None self.zmq_out_port = None self.transport_client = None self.lsp_server = None self.notifier = None self.language = language self.initialized = False self.ready_to_close = False self.request_seq = 1 self.req_status = {} self.watched_files = {} self.watched_folders = {} self.req_reply = {} # Select a free port to start the server. # NOTE: Don't use the new value to set server_setttings['port']!! # That's not required because this doesn't really correspond to a # change in the config settings of the server. Else a server # restart would be generated when doing a # workspace/didChangeConfiguration request. if not server_settings['external']: self.server_port = select_port( default_port=server_settings['port']) else: self.server_port = server_settings['port'] self.server_host = server_settings['host'] self.transport_args = [ sys.executable, '-u', osp.join(LOCATION, 'transport', 'main.py') ] self.external_server = server_settings.get('external', False) self.stdio = server_settings.get('stdio', False) # Setting stdio on implies that external_server is off if self.stdio and self.external_server: error = ('If server is set to use stdio communication, ' 'then it cannot be an external server') logger.error(error) raise AssertionError(error) self.folder = folder self.plugin_configurations = server_settings.get('configurations', {}) self.client_capabilites = CLIENT_CAPABILITES self.server_capabilites = SERVER_CAPABILITES self.context = zmq.Context() server_args_fmt = server_settings.get('args', '') server_args = server_args_fmt.format(host=self.server_host, port=self.server_port) transport_args_fmt = '--server-host {host} --server-port {port} ' transport_args = transport_args_fmt.format(host=self.server_host, port=self.server_port) self.server_args = [] if self.language == 'python': self.server_args += [sys.executable, '-m'] self.server_args += [server_settings['cmd']] if len(server_args) > 0: self.server_args += server_args.split(' ') self.server_unresponsive = False self.transport_args += transport_args.split(' ') self.transport_args += ['--folder', folder] self.transport_args += ['--transport-debug', str(get_debug_level())] if not self.stdio: self.transport_args += ['--external-server'] else: self.transport_args += ['--stdio-server'] self.external_server = True self.transport_unresponsive = False
def write_error(self, text): """Simulate stderr""" self.flush() self.write(text, flush=True, error=True) if get_debug_level(): STDERR.write(text)
def start(self): self.zmq_out_socket = self.context.socket(zmq.PAIR) self.zmq_out_port = self.zmq_out_socket.bind_to_random_port( 'tcp://{}'.format(LOCALHOST)) self.zmq_in_socket = self.context.socket(zmq.PAIR) self.zmq_in_socket.set_hwm(0) self.zmq_in_port = self.zmq_in_socket.bind_to_random_port( 'tcp://{}'.format(LOCALHOST)) self.transport_args += [ '--zmq-in-port', self.zmq_out_port, '--zmq-out-port', self.zmq_in_port ] server_log = subprocess.PIPE pid = os.getpid() if get_debug_level() > 0: # Create server log file server_log_fname = 'server_{0}_{1}.log'.format(self.language, pid) server_log_file = get_conf_path( osp.join('lsp_logs', server_log_fname)) if not osp.exists(osp.dirname(server_log_file)): os.makedirs(osp.dirname(server_log_file)) server_log = open(server_log_file, 'w') if self.stdio: server_log.close() if self.language == 'python': self.server_args += ['--log-file', server_log_file] self.transport_args += ['--server-log-file', server_log_file] # Start server with logging options if self.language != 'cpp': if get_debug_level() == 2: self.server_args.append('-v') elif get_debug_level() == 3: self.server_args.append('-vv') server_stdin = subprocess.PIPE server_stdout = server_log server_stderr = subprocess.STDOUT if not self.external_server: logger.info('Starting server: {0}'.format(' '.join( self.server_args))) creation_flags = 0 if os.name == 'nt': creation_flags = (subprocess.CREATE_NEW_PROCESS_GROUP | 0x08000000) # CREATE_NO_WINDOW if os.environ.get('CI') and os.name == 'nt': # The following patching avoids: # # OSError: [WinError 6] The handle is invalid # # while running our tests in CI services on Windows # (they run fine locally). # See this comment for an explanation: # https://stackoverflow.com/q/43966523/ # 438386#comment74964124_43966523 def patched_cleanup(): pass subprocess._cleanup = patched_cleanup self.lsp_server = subprocess.Popen(self.server_args, stdout=server_stdout, stdin=server_stdin, stderr=server_stderr, creationflags=creation_flags) client_log = subprocess.PIPE if get_debug_level() > 0: # Client log file client_log_fname = 'client_{0}_{1}.log'.format(self.language, pid) client_log_file = get_conf_path( osp.join('lsp_logs', client_log_fname)) if not osp.exists(osp.dirname(client_log_file)): os.makedirs(osp.dirname(client_log_file)) client_log = open(client_log_file, 'w') new_env = dict(os.environ) python_path = os.pathsep.join(sys.path)[1:] new_env['PYTHONPATH'] = python_path # This allows running LSP tests directly without having to install # Spyder if running_under_pytest(): new_env['PYTHONPATH'] = os.pathsep.join(sys.path)[:] # On some CI systems there are unicode characters inside PYTHOPATH # which raise errors if not removed if PY2: new_env = clean_env(new_env) self.transport_args = list(map(str, self.transport_args)) logger.info('Starting transport: {0}'.format(' '.join( self.transport_args))) if self.stdio: transport_stdin = subprocess.PIPE transport_stdout = subprocess.PIPE transport_stderr = client_log self.transport_args += self.server_args else: transport_stdout = client_log transport_stdin = subprocess.PIPE transport_stderr = subprocess.STDOUT self.transport_client = subprocess.Popen(self.transport_args, stdout=transport_stdout, stdin=transport_stdin, stderr=transport_stderr, env=new_env) fid = self.zmq_in_socket.getsockopt(zmq.FD) self.notifier = QSocketNotifier(fid, QSocketNotifier.Read, self) self.notifier.activated.connect(self.on_msg_received) # This is necessary for tests to pass locally! logger.debug('LSP {} client started!'.format(self.language))
def handle_exception(self, error_data, sender=None, internal_plugins=None): """ Exception ocurred in the internal console. Show a QDialog or the internal console to warn the user. Handle any exception that occurs during Spyder usage. Parameters ---------- error_data: dict The dictionary containing error data. The expected keys are: >>> error_data= { "text": str, "is_traceback": bool, "repo": str, "title": str, "label": str, "steps": str, } sender: spyder.api.plugins.SpyderPluginV2, optional The sender plugin. Default is None. Notes ----- The `is_traceback` key indicates if `text` contains plain text or a Python error traceback. The `title` and `repo` keys indicate how the error data should customize the report dialog and Github error submission. The `label` and `steps` keys allow customizing the content of the error dialog. """ text = error_data.get("text", None) is_traceback = error_data.get("is_traceback", False) title = error_data.get("title", "") label = error_data.get("label", "") steps = error_data.get("steps", "") # Skip errors without traceback (and no text) or dismiss if ((not text and not is_traceback and self.error_dlg is None) or self.dismiss_error): return # Retrieve internal plugins internal_plugins = PLUGIN_REGISTRY.internal_plugins # Get if sender is internal or not is_internal_plugin = True if sender is not None: sender_name = getattr(sender, 'NAME', getattr(sender, 'CONF_SECTION')) is_internal_plugin = sender_name in internal_plugins # Set repo repo = "spyder-ide/spyder" if not is_internal_plugin: repo = error_data.get("repo", None) if repo is None: raise SpyderAPIError( f"External plugin '{sender_name}' does not define 'repo' " "key in the 'error_data' dictionary in the form " "my-org/my-repo (only Github is supported).") if repo == 'spyder-ide/spyder': raise SpyderAPIError( f"External plugin '{sender_name}' 'repo' key needs to be " "different from the main Spyder repo.") if self.get_conf('show_internal_errors', section='main'): if self.error_dlg is None: self.error_dlg = SpyderErrorDialog(self) self.error_dlg.set_color_scheme( self.get_conf('selected', section='appearance')) self.error_dlg.close_btn.clicked.connect(self.close_error_dlg) self.error_dlg.rejected.connect(self.remove_error_dlg) self.error_dlg.details.sig_go_to_error_requested.connect( self.go_to_error) # Set the report repository self.error_dlg.set_github_repo_org(repo) if title: self.error_dlg.set_title(title) self.error_dlg.title.setEnabled(False) if label: self.error_dlg.main_label.setText(label) self.error_dlg.submit_btn.setEnabled(True) if steps: self.error_dlg.steps_text.setText(steps) self.error_dlg.set_require_minimum_length(False) self.error_dlg.append_traceback(text) self.error_dlg.show() elif DEV or get_debug_level(): self.change_visibility(True, True)
def start(self): self.zmq_out_socket = self.context.socket(zmq.PAIR) self.zmq_out_port = self.zmq_out_socket.bind_to_random_port('tcp://*') self.zmq_in_socket = self.context.socket(zmq.PAIR) self.zmq_in_socket.set_hwm(0) self.zmq_in_port = self.zmq_in_socket.bind_to_random_port('tcp://*') self.transport_args += [ '--zmq-in-port', self.zmq_out_port, '--zmq-out-port', self.zmq_in_port ] server_log = subprocess.PIPE if get_debug_level() > 0: # Create server log file server_log_fname = 'server_{0}.log'.format(self.language) server_log_file = get_conf_path( osp.join('lsp_logs', server_log_fname)) if not osp.exists(osp.dirname(server_log_file)): os.makedirs(osp.dirname(server_log_file)) server_log = open(server_log_file, 'w') # Start server with logging options if get_debug_level() == 2: self.server_args.append('-v') elif get_debug_level() == 3: self.server_args.append('-vv') if not self.external_server: logger.info('Starting server: {0}'.format(' '.join( self.server_args))) creation_flags = 0 if os.name == 'nt': creation_flags = (subprocess.CREATE_NEW_PROCESS_GROUP | 0x08000000) # CREATE_NO_WINDOW if os.environ.get('CI') and os.name == 'nt': # The following patching avoids: # # OSError: [WinError 6] The handle is invalid # # while running our tests in CI services on Windows # (they run fine locally). # See this comment for an explanation: # https://stackoverflow.com/q/43966523/ # 438386#comment74964124_43966523 def patched_cleanup(): pass subprocess._cleanup = patched_cleanup self.lsp_server = subprocess.Popen(self.server_args, stdout=server_log, stderr=subprocess.STDOUT, creationflags=creation_flags) client_log = subprocess.PIPE if get_debug_level() > 0: # Client log file client_log_fname = 'client_{0}.log'.format(self.language) client_log_file = get_conf_path( osp.join('lsp_logs', client_log_fname)) if not osp.exists(osp.dirname(client_log_file)): os.makedirs(osp.dirname(client_log_file)) client_log = open(client_log_file, 'w') new_env = dict(os.environ) python_path = os.pathsep.join(sys.path)[1:] new_env['PYTHONPATH'] = python_path self.transport_args = list(map(str, self.transport_args)) logger.info('Starting transport: {0}'.format(' '.join( self.transport_args))) self.transport_client = subprocess.Popen(self.transport_args, stdout=subprocess.PIPE, stderr=client_log, env=new_env) fid = self.zmq_in_socket.getsockopt(zmq.FD) self.notifier = QSocketNotifier(fid, QSocketNotifier.Read, self) self.notifier.activated.connect(self.on_msg_received) # This is necessary for tests to pass locally! logger.debug('LSP {} client started!'.format(self.language))
def setup(self): # Compute dependencies in a thread to not block the interface. self.dependencies_thread = QThread(None) # Attributes self.dialog_manager = DialogManager() self.give_updates_feedback = False self.thread_updates = None self.worker_updates = None self.updates_timer = None # Actions # Documentation actions self.documentation_action = self.create_action( ApplicationActions.SpyderDocumentationAction, text=_("Spyder documentation"), icon=self.create_icon("DialogHelpButton"), triggered=lambda: start_file(__docs_url__), context=Qt.ApplicationShortcut, register_shortcut=True, shortcut_context="_") spyder_video_url = ("https://www.youtube.com/playlist" "?list=PLPonohdiDqg9epClEcXoAPUiK0pN5eRoc") self.video_action = self.create_action( ApplicationActions.SpyderDocumentationVideoAction, text=_("Tutorial videos"), icon=self.create_icon("VideoIcon"), triggered=lambda: start_file(spyder_video_url)) # Support actions self.trouble_action = self.create_action( ApplicationActions.SpyderTroubleshootingAction, _("Troubleshooting..."), triggered=lambda: start_file(__trouble_url__)) self.report_action = self.create_action( ConsoleActions.SpyderReportAction, _("Report issue..."), icon=self.create_icon('bug'), triggered=self.sig_report_issue_requested) self.dependencies_action = self.create_action( ApplicationActions.SpyderDependenciesAction, _("Dependencies..."), triggered=self.show_dependencies, icon=self.create_icon('advanced')) self.check_updates_action = self.create_action( ApplicationActions.SpyderCheckUpdatesAction, _("Check for updates..."), triggered=self.check_updates) self.support_group_action = self.create_action( ApplicationActions.SpyderSupportAction, _("Spyder support..."), triggered=lambda: start_file(__forum_url__)) # About action self.about_action = self.create_action( ApplicationActions.SpyderAbout, _("About %s...") % "Spyder", icon=self.create_icon('MessageBoxInformation'), triggered=self.show_about, menurole=QAction.AboutRole) # Tools actions if WinUserEnvDialog is not None: self.winenv_action = self.create_action( ApplicationActions.SpyderWindowsEnvVariables, _("Current user environment variables..."), icon=self.create_icon('win_env'), tip=_("Show and edit current user environment " "variables in Windows registry " "(i.e. for all sessions)"), triggered=self.show_windows_env_variables) else: self.winenv_action = None # Application base actions self.restart_action = self.create_action( ApplicationActions.SpyderRestart, _("&Restart"), icon=self.create_icon('restart'), tip=_("Restart"), triggered=self.restart_normal, context=Qt.ApplicationShortcut, shortcut_context="_", register_shortcut=True) self.restart_debug_action = self.create_action( ApplicationActions.SpyderRestartDebug, _("&Restart in debug mode"), tip=_("Restart in debug mode"), triggered=self.restart_debug, context=Qt.ApplicationShortcut, shortcut_context="_", register_shortcut=True) # Debug logs if get_debug_level() >= 2: self.menu_debug_logs = self.create_menu( ApplicationPluginMenus.DebugLogsMenu, _("Debug logs")) # The menu can't be built at startup because Completions can # start after Application. self.menu_debug_logs.aboutToShow.connect( self.create_debug_log_actions)
"""BSD socket interface communication utilities""" # Be extra careful here. The interface is used to communicate with subprocesses # by redirecting output streams through a socket. Any exception in this module # and failure to read out buffers will most likely lock up Spyder. import os import socket import struct import threading import errno import traceback # Local imports from spyder.config.base import get_debug_level, STDERR DEBUG_EDITOR = get_debug_level() >= 3 from spyder.py3compat import pickle PICKLE_HIGHEST_PROTOCOL = 2 def temp_fail_retry(error, fun, *args): """Retry to execute function, ignoring EINTR error (interruptions)""" while 1: try: return fun(*args) except error as e: eintr = errno.WSAEINTR if os.name == 'nt' else errno.EINTR if e.args[0] == eintr: continue raise