def __init__(self, ipyclient, additional_options, interpreter_versions, is_external_kernel, is_spyder_kernel, handlers, *args, **kw): # To override the Qt widget used by RichJupyterWidget self.custom_control = ControlWidget self.custom_page_control = PageControlWidget self.custom_edit = True self.spyder_kernel_comm = KernelComm() self.spyder_kernel_comm.sig_exception_occurred.connect( self.sig_exception_occurred) super(ShellWidget, self).__init__(*args, **kw) self.ipyclient = ipyclient self.additional_options = additional_options self.interpreter_versions = interpreter_versions self.is_external_kernel = is_external_kernel self.is_spyder_kernel = is_spyder_kernel self._cwd = '' # Keyboard shortcuts # Registered here to use shellwidget as the parent self.shortcuts = self.create_shortcuts() # Set the color of the matched parentheses here since the qtconsole # uses a hard-coded value that is not modified when the color scheme is # set in the qtconsole constructor. See spyder-ide/spyder#4806. self.set_bracket_matcher_color_scheme(self.syntax_style) self.shutting_down = False self.kernel_manager = None self.kernel_client = None handlers.update({ 'pdb_state': self.set_pdb_state, 'pdb_execute': self.pdb_execute, 'show_pdb_output': self.show_pdb_output, 'get_pdb_settings': self.get_pdb_settings, 'set_debug_state': self.set_debug_state, 'update_syspath': self.update_syspath, 'do_where': self.do_where, 'pdb_input': self.pdb_input, 'request_interrupt_eventloop': self.request_interrupt_eventloop, }) for request_id in handlers: self.spyder_kernel_comm.register_call_handler( request_id, handlers[request_id]) self._execute_queue = [] self.executed.connect(self.pop_execute_queue) # Show a message in our installers to explain users how to use # modules that don't come with them. self.show_modules_message = is_pynsist() or running_in_mac_app() self.shutdown_lock = Lock()
def start_server(self): """Start server.""" # This is not necessary if we're trying to connect to an # external server if self.external_server or self.stdio: return logger.info('Starting server: {0}'.format(' '.join(self.server_args))) # Create server process self.server = QProcess(self) env = self.server.processEnvironment() # Adjustments for the Python language server. if self.language == 'python': # 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. cwd = osp.join(get_conf_path(), 'lsp_paths', 'cwd') if not osp.exists(cwd): os.makedirs(cwd) if os.name == "nt": # On Windows, some modules (notably Matplotlib) # cause exceptions if they cannot get the user home. # So, we need to pass the USERPROFILE env variable to # the PyLSP. if "USERPROFILE" in os.environ: env.insert("USERPROFILE", os.environ["USERPROFILE"]) # The PyLSP can't start on pip installations if APPDATA # is missing and the user has installed their packages on # that directory. # Fixes spyder-ide/spyder#17661 if (not (is_anaconda() or is_pynsist()) and "APPDATA" in os.environ): env.insert("APPDATA", os.environ["APPDATA"]) else: # There's no need to define a cwd for other servers. cwd = None # Most LSP servers spawn other processes, which may require # some environment variables. for var in os.environ: env.insert(var, os.environ[var]) logger.info('Server process env variables: {0}'.format(env.keys())) # Setup server self.server.setProcessEnvironment(env) self.server.errorOccurred.connect(self.handle_process_errors) self.server.setWorkingDirectory(cwd) self.server.setProcessChannelMode(QProcess.MergedChannels) if self.server_log_file is not None: self.server.setStandardOutputFile(self.server_log_file) # Start server self.server.start(self.server_args[0], self.server_args[1:])
def get_versions(reporev=True): """Get version information for components used by Spyder""" import sys import platform import qtpy import qtpy.QtCore from spyder.utils.conda import is_conda_env from spyder.config.base import is_pynsist, running_in_mac_app revision = branch = None if reporev: if running_in_mac_app(): revision = os.environ.get('SPY_COMMIT', None) branch = os.environ.get('SPY_BRANCH', None) else: from spyder.utils import vcs revision, branch = vcs.get_git_revision( os.path.dirname(__current_directory__)) if is_pynsist() or running_in_mac_app(): installer = 'standalone' elif is_conda_env(pyexec=sys.executable): installer = 'conda' else: installer = 'pip' return { 'spyder': __version__, 'installer': installer, 'python': platform.python_version(), # "2.7.3" 'bitness': 64 if sys.maxsize > 2**32 else 32, 'qt': qtpy.QtCore.__version__, 'qt_api': qtpy.API_NAME, # PyQt5 'qt_api_ver': (qtpy.PYSIDE_VERSION if qtpy.API == "pyside2" else qtpy.PYQT_VERSION), 'system': platform.system(), # Linux, Windows, ... 'release': platform.release(), # XP, 10.6, 2.2.0, etc. 'revision': revision, # '9fdf926eccce', 'branch': branch, # '4.x' or master }
def createEditor(self, parent, option, index, object_explorer=False): """Overriding method createEditor""" val_type = index.sibling(index.row(), 1).data() self.sig_open_editor.emit() if index.column() < 3: return None if self.show_warning(index): answer = QMessageBox.warning( self.parent(), _("Warning"), _("Opening this variable can be slow\n\n" "Do you want to continue anyway?"), QMessageBox.Yes | QMessageBox.No) if answer == QMessageBox.No: return None try: value = self.get_value(index) if value is None: return None except ImportError as msg: self.sig_editor_shown.emit() module = str(msg).split("'")[1] if module in ['pandas', 'numpy']: if module == 'numpy': val_type = 'array' else: val_type = 'dataframe, series' message = _("Spyder is unable to show the {val_type} or object" " you're trying to view because <tt>{module}</tt>" " is not installed. ") if running_in_mac_app(): message += _("Please consider using the full version of " "the Spyder MacOS application.<br>") else: message += _("Please install this package in your Spyder " "environment.<br>") QMessageBox.critical( self.parent(), _("Error"), message.format(val_type=val_type, module=module)) return else: if running_in_mac_app() or is_pynsist(): message = _("Spyder is unable to show the variable you're" " trying to view because the module " "<tt>{module}</tt> is not supported in the " "Spyder Lite application.<br>") else: message = _("Spyder is unable to show the variable you're" " trying to view because the module " "<tt>{module}</tt> is not found in your " "Spyder environment. Please install this " "package in this environment.<br>") QMessageBox.critical(self.parent(), _("Error"), message.format(module=module)) return except Exception as msg: QMessageBox.critical( self.parent(), _("Error"), _("Spyder was unable to retrieve the value of " "this variable from the console.<br><br>" "The error message was:<br>" "%s") % to_text_string(msg)) return key = index.model().get_key(index) readonly = (isinstance(value, (tuple, set)) or self.parent().readonly or not is_known_type(value)) # CollectionsEditor for a list, tuple, dict, etc. if isinstance(value, (list, set, tuple, dict)) and not object_explorer: from spyder.widgets.collectionseditor import CollectionsEditor editor = CollectionsEditor(parent=parent) editor.setup(value, key, icon=self.parent().windowIcon(), readonly=readonly) self.create_dialog( editor, dict(model=index.model(), editor=editor, key=key, readonly=readonly)) return None # ArrayEditor for a Numpy array elif (isinstance(value, (ndarray, MaskedArray)) and ndarray is not FakeObject and not object_explorer): editor = ArrayEditor(parent=parent) if not editor.setup_and_check(value, title=key, readonly=readonly): return self.create_dialog( editor, dict(model=index.model(), editor=editor, key=key, readonly=readonly)) return None # ArrayEditor for an images elif (isinstance(value, Image) and ndarray is not FakeObject and Image is not FakeObject and not object_explorer): arr = array(value) editor = ArrayEditor(parent=parent) if not editor.setup_and_check(arr, title=key, readonly=readonly): return conv_func = lambda arr: Image.fromarray(arr, mode=value.mode) self.create_dialog( editor, dict(model=index.model(), editor=editor, key=key, readonly=readonly, conv=conv_func)) return None # DataFrameEditor for a pandas dataframe, series or index elif (isinstance(value, (DataFrame, Index, Series)) and DataFrame is not FakeObject and not object_explorer): editor = DataFrameEditor(parent=parent) if not editor.setup_and_check(value, title=key): return editor.dataModel.set_format(index.model().dataframe_format) editor.sig_option_changed.connect(self.change_option) self.create_dialog( editor, dict(model=index.model(), editor=editor, key=key, readonly=readonly)) return None # QDateEdit and QDateTimeEdit for a dates or datetime respectively elif isinstance(value, datetime.date) and not object_explorer: if readonly: self.sig_editor_shown.emit() return None else: if isinstance(value, datetime.datetime): editor = QDateTimeEdit(value, parent=parent) # Needed to handle NaT values # See spyder-ide/spyder#8329 try: value.time() except ValueError: self.sig_editor_shown.emit() return None else: editor = QDateEdit(value, parent=parent) editor.setCalendarPopup(True) editor.setFont(get_font(font_size_delta=DEFAULT_SMALL_DELTA)) self.sig_editor_shown.emit() return editor # TextEditor for a long string elif is_text_string(value) and len(value) > 40 and not object_explorer: te = TextEditor(None, parent=parent) if te.setup_and_check(value): editor = TextEditor(value, key, readonly=readonly, parent=parent) self.create_dialog( editor, dict(model=index.model(), editor=editor, key=key, readonly=readonly)) return None # QLineEdit for an individual value (int, float, short string, etc) elif is_editable_type(value) and not object_explorer: if readonly: self.sig_editor_shown.emit() return None else: editor = QLineEdit(parent=parent) editor.setFont(get_font(font_size_delta=DEFAULT_SMALL_DELTA)) editor.setAlignment(Qt.AlignLeft) # This is making Spyder crash because the QLineEdit that it's # been modified is removed and a new one is created after # evaluation. So the object on which this method is trying to # act doesn't exist anymore. # editor.returnPressed.connect(self.commitAndCloseEditor) self.sig_editor_shown.emit() return editor # ObjectExplorer for an arbitrary Python object else: show_callable_attributes = index.model().show_callable_attributes show_special_attributes = index.model().show_special_attributes dataframe_format = index.model().dataframe_format if show_callable_attributes is None: show_callable_attributes = False if show_special_attributes is None: show_special_attributes = False from spyder.plugins.variableexplorer.widgets.objectexplorer \ import ObjectExplorer editor = ObjectExplorer( value, name=key, parent=parent, show_callable_attributes=show_callable_attributes, show_special_attributes=show_special_attributes, dataframe_format=dataframe_format, readonly=readonly) editor.sig_option_changed.connect(self.change_option) self.create_dialog( editor, dict(model=index.model(), editor=editor, key=key, readonly=readonly)) return None
def env(self): """Env vars for kernels""" default_interpreter = self.get_conf('default', section='main_interpreter') env_vars = os.environ.copy() # Avoid IPython adding the virtualenv on which Spyder is running # to the kernel sys.path env_vars.pop('VIRTUAL_ENV', None) # Add spyder-kernels subrepo path to PYTHONPATH if (DEV or running_under_pytest()) and not running_in_ci(): repo_path = osp.normpath(osp.join(HERE, '..', '..', '..', '..')) subrepo_path = osp.join(repo_path, 'external-deps', 'spyder-kernels') env_vars.update({'PYTHONPATH': subrepo_path}) # List of paths declared by the user, plus project's path, to # add to PYTHONPATH pathlist = self.get_conf('spyder_pythonpath', default=[], section='main') pypath = os.pathsep.join(pathlist) # List of modules to exclude from our UMR umr_namelist = self.get_conf('umr/namelist', section='main_interpreter') # Environment variables that we need to pass to the kernel env_vars.update({ 'SPY_EXTERNAL_INTERPRETER': not default_interpreter, 'SPY_UMR_ENABLED': self.get_conf('umr/enabled', section='main_interpreter'), 'SPY_UMR_VERBOSE': self.get_conf('umr/verbose', section='main_interpreter'), 'SPY_UMR_NAMELIST': ','.join(umr_namelist), 'SPY_RUN_LINES_O': self.get_conf('startup/run_lines'), 'SPY_PYLAB_O': self.get_conf('pylab'), 'SPY_BACKEND_O': self.get_conf('pylab/backend'), 'SPY_AUTOLOAD_PYLAB_O': self.get_conf('pylab/autoload'), 'SPY_FORMAT_O': self.get_conf('pylab/inline/figure_format'), 'SPY_BBOX_INCHES_O': self.get_conf('pylab/inline/bbox_inches'), 'SPY_RESOLUTION_O': self.get_conf('pylab/inline/resolution'), 'SPY_WIDTH_O': self.get_conf('pylab/inline/width'), 'SPY_HEIGHT_O': self.get_conf('pylab/inline/height'), 'SPY_USE_FILE_O': self.get_conf('startup/use_run_file'), 'SPY_RUN_FILE_O': self.get_conf('startup/run_file'), 'SPY_AUTOCALL_O': self.get_conf('autocall'), 'SPY_GREEDY_O': self.get_conf('greedy_completer'), 'SPY_JEDI_O': self.get_conf('jedi_completer'), 'SPY_SYMPY_O': self.get_conf('symbolic_math'), 'SPY_TESTING': running_under_pytest() or get_safe_mode(), 'SPY_HIDE_CMD': self.get_conf('hide_cmd_windows'), 'SPY_PYTHONPATH': pypath }) if self.is_pylab is True: env_vars['SPY_AUTOLOAD_PYLAB_O'] = True env_vars['SPY_SYMPY_O'] = False env_vars['SPY_RUN_CYTHON'] = False if self.is_sympy is True: env_vars['SPY_AUTOLOAD_PYLAB_O'] = False env_vars['SPY_SYMPY_O'] = True env_vars['SPY_RUN_CYTHON'] = False if self.is_cython is True: env_vars['SPY_AUTOLOAD_PYLAB_O'] = False env_vars['SPY_SYMPY_O'] = False env_vars['SPY_RUN_CYTHON'] = True # App considerations if (running_in_mac_app() or is_pynsist()) and not default_interpreter: env_vars.pop('PYTHONHOME', None) env_vars.pop('PYTHONPATH', None) # Remove this variable because it prevents starting kernels for # external interpreters when present. # Fixes spyder-ide/spyder#13252 env_vars.pop('PYTHONEXECUTABLE', None) # Making all env_vars strings clean_env_vars = clean_env(env_vars) return clean_env_vars
PLUGIN = 'spyder plugins' # ============================================================================= # Versions # ============================================================================= # Hard dependencies APPLAUNCHSERVICES_REQVER = '>=0.1.7' ATOMICWRITES_REQVER = '>=1.2.0' CHARDET_REQVER = '>=2.0.0' CLOUDPICKLE_REQVER = '>=0.5.0' COOKIECUTTER_REQVER = '>=1.6.0' DIFF_MATCH_PATCH_REQVER = '>=20181111' # None for pynsist install for now # (check way to add dist.info/egg.info from packages without wheels available) INTERVALTREE_REQVER = None if is_pynsist() else '>=3.0.2' IPYTHON_REQVER = ">=7.6.0" JEDI_REQVER = '=0.17.2' JSONSCHEMA_REQVER = '>=3.2.0' KEYRING_REQVER = '>=17.0.0' NBCONVERT_REQVER = '>=4.0' NUMPYDOC_REQVER = '>=0.6.0' PARAMIKO_REQVER = '>=2.4.0' PARSO_REQVER = '=0.7.0' PEXPECT_REQVER = '>=4.4.0' PICKLESHARE_REQVER = '>=0.4' PSUTIL_REQVER = '>=5.3' PYGMENTS_REQVER = '>=2.0' PYLINT_REQVER = '>=1.0' PYLS_REQVER = '>=0.36.2;<1.0.0' PYLS_BLACK_REQVER = '>=0.4.6'
OPTIONAL = 'optional' PLUGIN = 'spyder plugins' # ============================================================================= # Versions # ============================================================================= # Hard dependencies APPLAUNCHSERVICES_REQVER = '>=0.1.7' ATOMICWRITES_REQVER = '>=1.2.0' CHARDET_REQVER = '>=2.0.0' CLOUDPICKLE_REQVER = '>=0.5.0' COOKIECUTTER_REQVER = '>=1.6.0' DIFF_MATCH_PATCH_REQVER = '>=20181111' # None for pynsist install for now # (check way to add dist.info/egg.info from packages without wheels available) INTERVALTREE_REQVER = None if is_pynsist() else '>=3.0.2' IPYTHON_REQVER = ">=4.0;<6.0" if PY2 else ">=4.0" JEDI_REQVER = '=0.17.2' JSONSCHEMA_REQVER = '>=3.2.0' KEYRING_REQVER = '>=17.0.0' NBCONVERT_REQVER = '>=4.0' NUMPYDOC_REQVER = '>=0.6.0' PARAMIKO_REQVER = '>=2.4.0' PARSO_REQVER = '=0.7.0' PEXPECT_REQVER = '>=4.4.0' PICKLESHARE_REQVER = '>=0.4' PSUTIL_REQVER = '>=5.3' PYGMENTS_REQVER = '>=2.0' PYLINT_REQVER = '>=1.0' PYLS_REQVER = '>=0.36.1;<1.0.0' PYLS_BLACK_REQVER = '>=0.4.6'
def __init__(self, parent=None, is_report=False): QDialog.__init__(self, parent) self.is_report = is_report # Set to true to run tests on the dialog. This is the default # in the test function at the end of this file. self._testing = False self.setWindowTitle(_("Issue reporter")) self._github_org = 'spyder-ide' self._github_repo = 'spyder' # To save the traceback sent to the internal console self.error_traceback = "" # Dialog main label if self.is_report: title = _("Please fill the following information") else: title = _("Spyder has encountered an internal problem!") self.main_label = QLabel( _("<h3>{title}</h3>" "Before reporting this problem, <i>please</i> consult our " "comprehensive " "<b><a href=\"{trouble_url}\">Troubleshooting Guide</a></b> " "which should help solve most issues, and search for " "<b><a href=\"{project_url}\">known bugs</a></b> " "matching your error message or problem description for a " "quicker solution.").format(title=title, trouble_url=__trouble_url__, project_url=__project_url__)) self.main_label.setOpenExternalLinks(True) self.main_label.setWordWrap(True) self.main_label.setAlignment(Qt.AlignJustify) self.main_label.setStyleSheet('font-size: 12px;') # Issue title self.title = QLineEdit() self.title.textChanged.connect(self._contents_changed) self.title_chars_label = QLabel( _("{} more characters " "to go...").format(TITLE_MIN_CHARS)) form_layout = QFormLayout() form_layout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow) red_asterisk = '<font color="Red">*</font>' title_label = QLabel(_("<b>Title</b>: {}").format(red_asterisk)) form_layout.setWidget(0, QFormLayout.LabelRole, title_label) form_layout.setWidget(0, QFormLayout.FieldRole, self.title) # Description steps_header = QLabel( _("<b>Steps to reproduce:</b> {}").format(red_asterisk)) self.steps_text = QLabel( _("Please enter a detailed step-by-step " "description (in English) of what led up to " "the problem below. Issue reports without a " "clear way to reproduce them will be closed.")) self.steps_text.setWordWrap(True) self.steps_text.setAlignment(Qt.AlignJustify) self.steps_text.setStyleSheet('font-size: 12px;') # Field to input the description of the problem self.input_description = DescriptionWidget(self) # Only allow to submit to Github if we have a long enough description self.input_description.textChanged.connect(self._contents_changed) # Widget to show errors self.details = ShowErrorWidget(self) self.details.set_pythonshell_font(get_font()) self.details.hide() self.description_minimum_length = DESC_MIN_CHARS self.require_minimum_length = True # Label to show missing chars self.initial_chars = len(self.input_description.toPlainText()) self.desc_chars_label = QLabel( _("{} more characters " "to go...").format(self.description_minimum_length)) # Checkbox to dismiss future errors self.dismiss_box = QCheckBox( _("Hide all future errors during this " "session")) # Checkbox to include IPython console environment self.include_env = QCheckBox(_("Include IPython console environment")) # Dialog buttons gh_icon = ima.icon('github') self.submit_btn = QPushButton(gh_icon, _('Submit to Github')) self.submit_btn.setEnabled(False) self.submit_btn.clicked.connect(self._submit_to_github) self.details_btn = QPushButton(_('Show details')) self.details_btn.clicked.connect(self._show_details) if self.is_report: self.details_btn.hide() self.close_btn = QPushButton(_('Close')) self.close_btn.clicked.connect(self.reject) # Buttons layout buttons_layout = QHBoxLayout() buttons_layout.addWidget(self.submit_btn) buttons_layout.addWidget(self.details_btn) buttons_layout.addWidget(self.close_btn) # Main layout layout = QVBoxLayout() layout.addWidget(self.main_label) layout.addSpacing(20) layout.addLayout(form_layout) layout.addWidget(self.title_chars_label) layout.addSpacing(12) layout.addWidget(steps_header) layout.addSpacing(-1) layout.addWidget(self.steps_text) layout.addSpacing(1) layout.addWidget(self.input_description) layout.addWidget(self.details) layout.addWidget(self.desc_chars_label) layout.addSpacing(15) if not self.is_report: layout.addWidget(self.dismiss_box) # Only provide checkbox if not an installer default interpreter if (not (is_pynsist() or running_in_mac_app()) or not self.get_conf('default', section='main_interpreter')): layout.addWidget(self.include_env) layout.addSpacing(15) layout.addLayout(buttons_layout) layout.setContentsMargins(25, 20, 25, 10) self.setLayout(layout) self.resize(570, 600) self.title.setFocus() # Set Tab key focus order self.setTabOrder(self.title, self.input_description)