def set_root_prefix(self, prefix=None): """ Set the prefix to the root environment (default is /opt/anaconda). This function should only be called once (right after importing conda_api). """ global ROOT_PREFIX if prefix: ROOT_PREFIX = prefix # find *some* conda instance, and then use info() to get 'root_prefix' else: pass # i = self.info(abspath=False) # self.ROOT_PREFIX = i['root_prefix'] ''' plat = 'posix' if sys.platform.lower().startswith('win'): listsep = ';' plat = 'win' else: listsep = ':' for p in os.environ['PATH'].split(listsep): if (os.path.exists(os.path.join(p, 'conda')) or os.path.exists(os.path.join(p, 'conda.exe')) or os.path.exists(os.path.join(p, 'conda.bat'))): # TEMPORARY: ROOT_PREFIX = os.path.dirname(p) # root prefix is 1 dir up i = info() # REAL: ROOT_PREFIX = i['root_prefix'] break else: # fall back to standard install location, which may be wrong if plat == 'win': ROOT_PREFIX = 'C:\Anaconda' else: ROOT_PREFIX = '/opt/anaconda' ''' # adapted code # ------------ if ROOT_PREFIX is None: qprocess = QProcess() cmd_list = ['conda', 'info', '--json'] qprocess.start(cmd_list[0], cmd_list[1:]) qprocess.waitForFinished() output = qprocess.readAllStandardOutput() output = handle_qbytearray(output, CondaProcess.ENCODING) info = json.loads(output) ROOT_PREFIX = info['root_prefix']
class ProfilerWidget(QWidget): """ Profiler widget """ DATAPATH = get_conf_path('profiler.results') VERSION = '0.0.1' def __init__(self, parent, max_entries=100): QWidget.__init__(self, parent) self.setWindowTitle("Profiler") self.output = None self.error_output = None self._last_wdir = None self._last_args = None self._last_pythonpath = None self.filecombo = PythonModulesComboBox(self) self.start_button = create_toolbutton(self, icon=get_icon('run.png'), text=_("Profile"), tip=_("Run profiler"), triggered=self.start, text_beside_icon=True) self.stop_button = create_toolbutton(self, icon=get_icon('terminate.png'), text=_("Stop"), tip=_( "Stop current profiling"), text_beside_icon=True) self.connect(self.filecombo, SIGNAL('valid(bool)'), self.start_button.setEnabled) #self.connect(self.filecombo, SIGNAL('valid(bool)'), self.show_data) # FIXME: The combobox emits this signal on almost any event # triggering show_data() too early, too often. browse_button = create_toolbutton(self, icon=get_icon('fileopen.png'), tip=_('Select Python script'), triggered=self.select_file) self.datelabel = QLabel() self.log_button = create_toolbutton(self, icon=get_icon('log.png'), text=_("Output"), text_beside_icon=True, tip=_("Show program's output"), triggered=self.show_log) self.datatree = ProfilerDataTree(self) self.collapse_button = create_toolbutton(self, icon=get_icon('collapse.png'), triggered=lambda dD=-1: self.datatree.change_view(dD), tip=_('Collapse one level up')) self.expand_button = create_toolbutton(self, icon=get_icon('expand.png'), triggered=lambda dD=1: self.datatree.change_view(dD), tip=_('Expand one level down')) hlayout1 = QHBoxLayout() hlayout1.addWidget(self.filecombo) hlayout1.addWidget(browse_button) hlayout1.addWidget(self.start_button) hlayout1.addWidget(self.stop_button) hlayout2 = QHBoxLayout() hlayout2.addWidget(self.collapse_button) hlayout2.addWidget(self.expand_button) hlayout2.addStretch() hlayout2.addWidget(self.datelabel) hlayout2.addStretch() hlayout2.addWidget(self.log_button) layout = QVBoxLayout() layout.addLayout(hlayout1) layout.addLayout(hlayout2) layout.addWidget(self.datatree) self.setLayout(layout) self.process = None self.set_running_state(False) self.start_button.setEnabled(False) if not is_profiler_installed(): # This should happen only on certain GNU/Linux distributions # or when this a home-made Python build because the Python # profilers are included in the Python standard library for widget in (self.datatree, self.filecombo, self.start_button, self.stop_button): widget.setDisabled(True) url = 'http://docs.python.org/library/profile.html' text = '%s <a href=%s>%s</a>' % (_('Please install'), url, _("the Python profiler modules")) self.datelabel.setText(text) else: pass # self.show_data() def analyze(self, filename, wdir=None, args=None, pythonpath=None): if not is_profiler_installed(): return self.kill_if_running() #index, _data = self.get_data(filename) index = None # FIXME: storing data is not implemented yet if index is None: self.filecombo.addItem(filename) self.filecombo.setCurrentIndex(self.filecombo.count()-1) else: self.filecombo.setCurrentIndex(self.filecombo.findText(filename)) self.filecombo.selected() if self.filecombo.is_valid(): if wdir is None: wdir = osp.dirname(filename) self.start(wdir, args, pythonpath) def select_file(self): self.emit(SIGNAL('redirect_stdio(bool)'), False) filename, _selfilter = getopenfilename(self, _("Select Python script"), getcwd(), _("Python scripts")+" (*.py ; *.pyw)") self.emit(SIGNAL('redirect_stdio(bool)'), False) if filename: self.analyze(filename) def show_log(self): if self.output: TextEditor(self.output, title=_("Profiler output"), readonly=True, size=(700, 500)).exec_() def show_errorlog(self): if self.error_output: TextEditor(self.error_output, title=_("Profiler output"), readonly=True, size=(700, 500)).exec_() def start(self, wdir=None, args=None, pythonpath=None): filename = to_text_string(self.filecombo.currentText()) if wdir is None: wdir = self._last_wdir if wdir is None: wdir = osp.basename(filename) if args is None: args = self._last_args if args is None: args = [] if pythonpath is None: pythonpath = self._last_pythonpath self._last_wdir = wdir self._last_args = args self._last_pythonpath = pythonpath self.datelabel.setText(_('Profiling, please wait...')) self.process = QProcess(self) self.process.setProcessChannelMode(QProcess.SeparateChannels) self.process.setWorkingDirectory(wdir) self.connect(self.process, SIGNAL("readyReadStandardOutput()"), self.read_output) self.connect(self.process, SIGNAL("readyReadStandardError()"), lambda: self.read_output(error=True)) self.connect(self.process, SIGNAL("finished(int,QProcess::ExitStatus)"), self.finished) self.connect(self.stop_button, SIGNAL("clicked()"), self.process.kill) if pythonpath is not None: env = [to_text_string(_pth) for _pth in self.process.systemEnvironment()] baseshell.add_pathlist_to_PYTHONPATH(env, pythonpath) self.process.setEnvironment(env) self.output = '' self.error_output = '' p_args = ['-m', 'cProfile', '-o', self.DATAPATH] if os.name == 'nt': # On Windows, one has to replace backslashes by slashes to avoid # confusion with escape characters (otherwise, for example, '\t' # will be interpreted as a tabulation): p_args.append(osp.normpath(filename).replace(os.sep, '/')) else: p_args.append(filename) if args: p_args.extend(shell_split(args)) executable = sys.executable if executable.endswith("spyder.exe"): # py2exe distribution executable = "python.exe" self.process.start(executable, p_args) running = self.process.waitForStarted() self.set_running_state(running) if not running: QMessageBox.critical(self, _("Error"), _("Process failed to start")) def set_running_state(self, state=True): self.start_button.setEnabled(not state) self.stop_button.setEnabled(state) def read_output(self, error=False): if error: self.process.setReadChannel(QProcess.StandardError) else: self.process.setReadChannel(QProcess.StandardOutput) qba = QByteArray() while self.process.bytesAvailable(): if error: qba += self.process.readAllStandardError() else: qba += self.process.readAllStandardOutput() text = to_text_string( locale_codec.toUnicode(qba.data()) ) if error: self.error_output += text else: self.output += text def finished(self): self.set_running_state(False) self.show_errorlog() # If errors occurred, show them. self.output = self.error_output + self.output # FIXME: figure out if show_data should be called here or # as a signal from the combobox self.show_data(justanalyzed=True) def kill_if_running(self): if self.process is not None: if self.process.state() == QProcess.Running: self.process.kill() self.process.waitForFinished() def show_data(self, justanalyzed=False): if not justanalyzed: self.output = None self.log_button.setEnabled(self.output is not None \ and len(self.output) > 0) self.kill_if_running() filename = to_text_string(self.filecombo.currentText()) if not filename: return self.datatree.load_data(self.DATAPATH) self.datelabel.setText(_('Sorting data, please wait...')) QApplication.processEvents() self.datatree.show_tree() text_style = "<span style=\'color: #444444\'><b>%s </b></span>" date_text = text_style % time.strftime("%d %b %Y %H:%M", time.localtime()) self.datelabel.setText(date_text)
class PylintWidget(QWidget): """ Pylint widget """ DATAPATH = get_conf_path('pylint.results') VERSION = '1.1.0' redirect_stdio = Signal(bool) def __init__(self, parent, max_entries=100): QWidget.__init__(self, parent) self.setWindowTitle("Pylint") self.output = None self.error_output = None self.max_entries = max_entries self.rdata = [] if osp.isfile(self.DATAPATH): try: data = pickle.loads(open(self.DATAPATH, 'rb').read()) if data[0] == self.VERSION: self.rdata = data[1:] except (EOFError, ImportError): pass self.filecombo = PythonModulesComboBox(self) if self.rdata: self.remove_obsolete_items() self.filecombo.addItems(self.get_filenames()) self.start_button = create_toolbutton(self, icon=ima.icon('run'), text=_("Analyze"), tip=_("Run analysis"), triggered=self.start, text_beside_icon=True) self.stop_button = create_toolbutton(self, icon=ima.icon('stop'), text=_("Stop"), tip=_("Stop current analysis"), text_beside_icon=True) self.filecombo.valid.connect(self.start_button.setEnabled) self.filecombo.valid.connect(self.show_data) browse_button = create_toolbutton(self, icon=ima.icon('fileopen'), tip=_('Select Python file'), triggered=self.select_file) self.ratelabel = QLabel() self.datelabel = QLabel() self.log_button = create_toolbutton(self, icon=ima.icon('log'), text=_("Output"), text_beside_icon=True, tip=_("Complete output"), triggered=self.show_log) self.treewidget = ResultsTree(self) hlayout1 = QHBoxLayout() hlayout1.addWidget(self.filecombo) hlayout1.addWidget(browse_button) hlayout1.addWidget(self.start_button) hlayout1.addWidget(self.stop_button) hlayout2 = QHBoxLayout() hlayout2.addWidget(self.ratelabel) hlayout2.addStretch() hlayout2.addWidget(self.datelabel) hlayout2.addStretch() hlayout2.addWidget(self.log_button) layout = QVBoxLayout() layout.addLayout(hlayout1) layout.addLayout(hlayout2) layout.addWidget(self.treewidget) self.setLayout(layout) self.process = None self.set_running_state(False) if PYLINT_PATH is None: for widget in (self.treewidget, self.filecombo, self.start_button, self.stop_button): widget.setDisabled(True) if os.name == 'nt' \ and programs.is_module_installed("pylint"): # Pylint is installed but pylint script is not in PATH # (AFAIK, could happen only on Windows) text = _('Pylint script was not found. Please add "%s" to PATH.') text = to_text_string(text) % osp.join(sys.prefix, "Scripts") else: text = _('Please install <b>pylint</b>:') url = 'http://www.logilab.fr' text += ' <a href=%s>%s</a>' % (url, url) self.ratelabel.setText(text) else: self.show_data() def analyze(self, filename): if PYLINT_PATH is None: return filename = to_text_string(filename) # filename is a QString instance self.kill_if_running() index, _data = self.get_data(filename) if index is None: self.filecombo.addItem(filename) self.filecombo.setCurrentIndex(self.filecombo.count()-1) else: self.filecombo.setCurrentIndex(self.filecombo.findText(filename)) self.filecombo.selected() if self.filecombo.is_valid(): self.start() def select_file(self): self.redirect_stdio.emit(False) filename, _selfilter = getopenfilename(self, _("Select Python file"), getcwd(), _("Python files")+" (*.py ; *.pyw)") self.redirect_stdio.emit(True) if filename: self.analyze(filename) def remove_obsolete_items(self): """Removing obsolete items""" self.rdata = [(filename, data) for filename, data in self.rdata if is_module_or_package(filename)] def get_filenames(self): return [filename for filename, _data in self.rdata] def get_data(self, filename): filename = osp.abspath(filename) for index, (fname, data) in enumerate(self.rdata): if fname == filename: return index, data else: return None, None def set_data(self, filename, data): filename = osp.abspath(filename) index, _data = self.get_data(filename) if index is not None: self.rdata.pop(index) self.rdata.insert(0, (filename, data)) self.save() def save(self): while len(self.rdata) > self.max_entries: self.rdata.pop(-1) pickle.dump([self.VERSION]+self.rdata, open(self.DATAPATH, 'wb'), 2) def show_log(self): if self.output: TextEditor(self.output, title=_("Pylint output"), readonly=True, size=(700, 500)).exec_() def start(self): filename = to_text_string(self.filecombo.currentText()) self.process = QProcess(self) self.process.setProcessChannelMode(QProcess.SeparateChannels) self.process.setWorkingDirectory(osp.dirname(filename)) self.process.readyReadStandardOutput.connect(self.read_output) self.process.readyReadStandardError.connect( lambda: self.read_output(error=True)) self.process.finished.connect(lambda ec, es=QProcess.ExitStatus: self.finished(ec, es)) self.stop_button.clicked.connect(self.process.kill) self.output = '' self.error_output = '' plver = PYLINT_VER if plver is not None: if plver.split('.')[0] == '0': p_args = ['-i', 'yes'] else: # Option '-i' (alias for '--include-ids') was removed in pylint # 1.0 p_args = ["--msg-template='{msg_id}:{line:3d},"\ "{column}: {obj}: {msg}"] p_args += [osp.basename(filename)] else: p_args = [osp.basename(filename)] self.process.start(PYLINT_PATH, p_args) running = self.process.waitForStarted() self.set_running_state(running) if not running: QMessageBox.critical(self, _("Error"), _("Process failed to start")) def set_running_state(self, state=True): self.start_button.setEnabled(not state) self.stop_button.setEnabled(state) def read_output(self, error=False): if error: self.process.setReadChannel(QProcess.StandardError) else: self.process.setReadChannel(QProcess.StandardOutput) qba = QByteArray() while self.process.bytesAvailable(): if error: qba += self.process.readAllStandardError() else: qba += self.process.readAllStandardOutput() text = to_text_string( locale_codec.toUnicode(qba.data()) ) if error: self.error_output += text else: self.output += text def finished(self, exit_code, exit_status): self.set_running_state(False) if not self.output: if self.error_output: QMessageBox.critical(self, _("Error"), self.error_output) print("pylint error:\n\n" + self.error_output, file=sys.stderr) return # Convention, Refactor, Warning, Error results = {'C:': [], 'R:': [], 'W:': [], 'E:': []} txt_module = '************* Module ' module = '' # Should not be needed - just in case something goes wrong for line in self.output.splitlines(): if line.startswith(txt_module): # New module module = line[len(txt_module):] continue # Supporting option include-ids: ('R3873:' instead of 'R:') if not re.match('^[CRWE]+([0-9]{4})?:', line): continue i1 = line.find(':') if i1 == -1: continue msg_id = line[:i1] i2 = line.find(':', i1+1) if i2 == -1: continue line_nb = line[i1+1:i2].strip() if not line_nb: continue line_nb = int(line_nb.split(',')[0]) message = line[i2+1:] item = (module, line_nb, message, msg_id) results[line[0]+':'].append(item) # Rate rate = None txt_rate = 'Your code has been rated at ' i_rate = self.output.find(txt_rate) if i_rate > 0: i_rate_end = self.output.find('/10', i_rate) if i_rate_end > 0: rate = self.output[i_rate+len(txt_rate):i_rate_end] # Previous run previous = '' if rate is not None: txt_prun = 'previous run: ' i_prun = self.output.find(txt_prun, i_rate_end) if i_prun > 0: i_prun_end = self.output.find('/10', i_prun) previous = self.output[i_prun+len(txt_prun):i_prun_end] filename = to_text_string(self.filecombo.currentText()) self.set_data(filename, (time.localtime(), rate, previous, results)) self.output = self.error_output + self.output self.show_data(justanalyzed=True) def kill_if_running(self): if self.process is not None: if self.process.state() == QProcess.Running: self.process.kill() self.process.waitForFinished() def show_data(self, justanalyzed=False): if not justanalyzed: self.output = None self.log_button.setEnabled(self.output is not None \ and len(self.output) > 0) self.kill_if_running() filename = to_text_string(self.filecombo.currentText()) if not filename: return _index, data = self.get_data(filename) if data is None: text = _('Source code has not been rated yet.') self.treewidget.clear_results() date_text = '' else: datetime, rate, previous_rate, results = data if rate is None: text = _('Analysis did not succeed ' '(see output for more details).') self.treewidget.clear_results() date_text = '' else: text_style = "<span style=\'color: #444444\'><b>%s </b></span>" rate_style = "<span style=\'color: %s\'><b>%s</b></span>" prevrate_style = "<span style=\'color: #666666\'>%s</span>" color = "#FF0000" if float(rate) > 5.: color = "#22AA22" elif float(rate) > 3.: color = "#EE5500" text = _('Global evaluation:') text = (text_style % text)+(rate_style % (color, ('%s/10' % rate))) if previous_rate: text_prun = _('previous run:') text_prun = ' (%s %s/10)' % (text_prun, previous_rate) text += prevrate_style % text_prun self.treewidget.set_results(filename, results) date = to_text_string(time.strftime("%d %b %Y %H:%M", datetime), encoding='utf8') date_text = text_style % date self.ratelabel.setText(text) self.datelabel.setText(date_text)
class PluginClient(QObject): """ A class which handles a connection to a plugin through a QProcess. """ # Emitted when the plugin has initialized. initialized = Signal() # Emitted when the plugin has failed to load. errored = Signal() # Emitted when a request response is received. request_handled = Signal(object) def __init__(self, plugin_name, executable=None): super(PluginClient, self).__init__() self.plugin_name = plugin_name self.executable = executable or sys.executable self.start() def start(self): """Start a new connection with the plugin. """ self._initialized = False plugin_name = self.plugin_name self.sock, server_port = connect_to_port() self.sock.listen(2) QApplication.instance().aboutToQuit.connect(self.close) self.process = QProcess(self) self.process.setWorkingDirectory(os.path.dirname(__file__)) processEnvironment = QProcessEnvironment() env = self.process.systemEnvironment() python_path = imp.find_module('spyderlib')[1] # Use the current version of the plugin provider if possible. try: provider_path = imp.find_module(self.plugin_name)[1] python_path = os.sep.join([python_path, provider_path]) except ImportError: pass env.append("PYTHONPATH=%s" % python_path) for envItem in env: envName, separator, envValue = envItem.partition('=') processEnvironment.insert(envName, envValue) self.process.setProcessEnvironment(processEnvironment) p_args = ['-u', 'plugin_server.py', str(server_port), plugin_name] self.listener = PluginListener(self.sock) self.listener.request_handled.connect(self.request_handled.emit) self.listener.initialized.connect(self._on_initialized) self.listener.start() self.process.start(self.executable, p_args) self.process.finished.connect(self._on_finished) running = self.process.waitForStarted() if not running: raise IOError('Could not start plugin %s' % plugin_name) def send(self, request): """Send a request to the plugin. """ if not self._initialized: return sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(("127.0.0.1", self.client_port)) request['plugin_name'] = self.plugin_name write_packet(sock, request) sock.close() def close(self): self.process.kill() self.process.waitForFinished(200) self.sock.close() def _on_initialized(self, port): debug_print('Initialized %s' % self.plugin_name) self._initialized = True self.client_port = port self.initialized.emit() def _on_finished(self): debug_print('finished %s %s' % (self.plugin_name, self._initialized)) if self._initialized: self.start() else: self._initialized = False debug_print(self.process.readAllStandardOutput()) debug_print(self.process.readAllStandardError()) self.errored.emit()
class ProfilerWidget(QWidget): """ Profiler widget """ DATAPATH = get_conf_path('profiler.results') VERSION = '0.0.1' redirect_stdio = Signal(bool) def __init__(self, parent, max_entries=100): QWidget.__init__(self, parent) self.setWindowTitle("Profiler") self.output = None self.error_output = None self._last_wdir = None self._last_args = None self._last_pythonpath = None self.filecombo = PythonModulesComboBox(self) self.start_button = create_toolbutton(self, icon=get_icon('run.png'), text=_("Profile"), tip=_("Run profiler"), triggered=lambda: self.start(), text_beside_icon=True) self.stop_button = create_toolbutton(self, icon=get_icon('stop.png'), text=_("Stop"), tip=_("Stop current profiling"), text_beside_icon=True) self.filecombo.valid.connect(self.start_button.setEnabled) #self.connect(self.filecombo, SIGNAL('valid(bool)'), self.show_data) # FIXME: The combobox emits this signal on almost any event # triggering show_data() too early, too often. browse_button = create_toolbutton(self, icon=get_icon('fileopen.png'), tip=_('Select Python script'), triggered=self.select_file) self.datelabel = QLabel() self.log_button = create_toolbutton(self, icon=get_icon('log.png'), text=_("Output"), text_beside_icon=True, tip=_("Show program's output"), triggered=self.show_log) self.datatree = ProfilerDataTree(self) self.collapse_button = create_toolbutton( self, icon=get_icon('collapse.png'), triggered=lambda dD=-1: self.datatree.change_view(dD), tip=_('Collapse one level up')) self.expand_button = create_toolbutton( self, icon=get_icon('expand.png'), triggered=lambda dD=1: self.datatree.change_view(dD), tip=_('Expand one level down')) self.save_button = create_toolbutton(self, text_beside_icon=True, text=_("Save data"), icon=get_icon('filesave.png'), triggered=self.save_data, tip=_('Save profiling data')) self.load_button = create_toolbutton( self, text_beside_icon=True, text=_("Load data"), icon=get_icon('fileimport.png'), triggered=self.compare, tip=_('Load profiling data for comparison')) self.clear_button = create_toolbutton(self, text_beside_icon=True, text=_("Clear comparison"), icon=get_icon('eraser.png'), triggered=self.clear) hlayout1 = QHBoxLayout() hlayout1.addWidget(self.filecombo) hlayout1.addWidget(browse_button) hlayout1.addWidget(self.start_button) hlayout1.addWidget(self.stop_button) hlayout2 = QHBoxLayout() hlayout2.addWidget(self.collapse_button) hlayout2.addWidget(self.expand_button) hlayout2.addStretch() hlayout2.addWidget(self.datelabel) hlayout2.addStretch() hlayout2.addWidget(self.log_button) hlayout2.addWidget(self.save_button) hlayout2.addWidget(self.load_button) hlayout2.addWidget(self.clear_button) layout = QVBoxLayout() layout.addLayout(hlayout1) layout.addLayout(hlayout2) layout.addWidget(self.datatree) self.setLayout(layout) self.process = None self.set_running_state(False) self.start_button.setEnabled(False) self.clear_button.setEnabled(False) if not is_profiler_installed(): # This should happen only on certain GNU/Linux distributions # or when this a home-made Python build because the Python # profilers are included in the Python standard library for widget in (self.datatree, self.filecombo, self.start_button, self.stop_button): widget.setDisabled(True) url = 'http://docs.python.org/library/profile.html' text = '%s <a href=%s>%s</a>' % (_('Please install'), url, _("the Python profiler modules")) self.datelabel.setText(text) else: pass # self.show_data() def save_data(self): """Save data""" title = _("Save profiler result") filename, _selfilter = getsavefilename( self, title, getcwd(), _("Profiler result") + " (*.Result)") if filename: self.datatree.save_data(filename) def compare(self): filename, _selfilter = getopenfilename( self, _("Select script to compare"), getcwd(), _("Profiler result") + " (*.Result)") if filename: self.datatree.compare(filename) self.show_data() self.clear_button.setEnabled(True) def clear(self): self.datatree.compare(None) self.datatree.hide_diff_cols(True) self.show_data() self.clear_button.setEnabled(False) def analyze(self, filename, wdir=None, args=None, pythonpath=None): if not is_profiler_installed(): return self.kill_if_running() #index, _data = self.get_data(filename) index = None # FIXME: storing data is not implemented yet if index is None: self.filecombo.addItem(filename) self.filecombo.setCurrentIndex(self.filecombo.count() - 1) else: self.filecombo.setCurrentIndex(self.filecombo.findText(filename)) self.filecombo.selected() if self.filecombo.is_valid(): if wdir is None: wdir = osp.dirname(filename) self.start(wdir, args, pythonpath) def select_file(self): self.redirect_stdio.emit(False) filename, _selfilter = getopenfilename( self, _("Select Python script"), getcwd(), _("Python scripts") + " (*.py ; *.pyw)") self.redirect_stdio.emit(True) if filename: self.analyze(filename) def show_log(self): if self.output: TextEditor(self.output, title=_("Profiler output"), readonly=True, size=(700, 500)).exec_() def show_errorlog(self): if self.error_output: TextEditor(self.error_output, title=_("Profiler output"), readonly=True, size=(700, 500)).exec_() def start(self, wdir=None, args=None, pythonpath=None): filename = to_text_string(self.filecombo.currentText()) if wdir is None: wdir = self._last_wdir if wdir is None: wdir = osp.basename(filename) if args is None: args = self._last_args if args is None: args = [] if pythonpath is None: pythonpath = self._last_pythonpath self._last_wdir = wdir self._last_args = args self._last_pythonpath = pythonpath self.datelabel.setText(_('Profiling, please wait...')) self.process = QProcess(self) self.process.setProcessChannelMode(QProcess.SeparateChannels) self.process.setWorkingDirectory(wdir) self.process.readyReadStandardOutput.connect(self.read_output) self.process.readyReadStandardError.connect( lambda: self.read_output(error=True)) self.process.finished.connect( lambda ec, es=QProcess.ExitStatus: self.finished(ec, es)) self.stop_button.clicked.connect(self.process.kill) if pythonpath is not None: env = [ to_text_string(_pth) for _pth in self.process.systemEnvironment() ] baseshell.add_pathlist_to_PYTHONPATH(env, pythonpath) processEnvironment = QProcessEnvironment() for envItem in env: envName, separator, envValue = envItem.partition('=') processEnvironment.insert(envName, envValue) self.process.setProcessEnvironment(processEnvironment) self.output = '' self.error_output = '' p_args = ['-m', 'cProfile', '-o', self.DATAPATH] if os.name == 'nt': # On Windows, one has to replace backslashes by slashes to avoid # confusion with escape characters (otherwise, for example, '\t' # will be interpreted as a tabulation): p_args.append(osp.normpath(filename).replace(os.sep, '/')) else: p_args.append(filename) if args: p_args.extend(shell_split(args)) executable = sys.executable if executable.endswith("spyder.exe"): # py2exe distribution executable = "python.exe" self.process.start(executable, p_args) running = self.process.waitForStarted() self.set_running_state(running) if not running: QMessageBox.critical(self, _("Error"), _("Process failed to start")) def set_running_state(self, state=True): self.start_button.setEnabled(not state) self.stop_button.setEnabled(state) def read_output(self, error=False): if error: self.process.setReadChannel(QProcess.StandardError) else: self.process.setReadChannel(QProcess.StandardOutput) qba = QByteArray() while self.process.bytesAvailable(): if error: qba += self.process.readAllStandardError() else: qba += self.process.readAllStandardOutput() text = to_text_string(locale_codec.toUnicode(qba.data())) if error: self.error_output += text else: self.output += text def finished(self, exit_code, exit_status): self.set_running_state(False) self.show_errorlog() # If errors occurred, show them. self.output = self.error_output + self.output # FIXME: figure out if show_data should be called here or # as a signal from the combobox self.show_data(justanalyzed=True) def kill_if_running(self): if self.process is not None: if self.process.state() == QProcess.Running: self.process.kill() self.process.waitForFinished() def show_data(self, justanalyzed=False): if not justanalyzed: self.output = None self.log_button.setEnabled(self.output is not None \ and len(self.output) > 0) self.kill_if_running() filename = to_text_string(self.filecombo.currentText()) if not filename: return self.datelabel.setText(_('Sorting data, please wait...')) QApplication.processEvents() self.datatree.load_data(self.DATAPATH) self.datatree.show_tree() text_style = "<span style=\'color: #444444\'><b>%s </b></span>" date_text = text_style % time.strftime("%d %b %Y %H:%M", time.localtime()) self.datelabel.setText(date_text)
class PylintWidget(QWidget): """ Pylint widget """ DATAPATH = get_conf_path('pylint.results') VERSION = '1.1.0' def __init__(self, parent, max_entries=100): QWidget.__init__(self, parent) self.setWindowTitle("Pylint") self.output = None self.error_output = None self.max_entries = max_entries self.rdata = [] if osp.isfile(self.DATAPATH): try: data = pickle.loads(open(self.DATAPATH, 'rb').read()) if data[0] == self.VERSION: self.rdata = data[1:] except (EOFError, ImportError): pass self.filecombo = PythonModulesComboBox(self) if self.rdata: self.remove_obsolete_items() self.filecombo.addItems(self.get_filenames()) self.start_button = create_toolbutton(self, icon=get_icon('run.png'), text=_("Analyze"), tip=_("Run analysis"), triggered=self.start, text_beside_icon=True) self.stop_button = create_toolbutton(self, icon=get_icon('stop.png'), text=_("Stop"), tip=_("Stop current analysis"), text_beside_icon=True) self.connect(self.filecombo, SIGNAL('valid(bool)'), self.start_button.setEnabled) self.connect(self.filecombo, SIGNAL('valid(bool)'), self.show_data) browse_button = create_toolbutton(self, icon=get_icon('fileopen.png'), tip=_('Select Python file'), triggered=self.select_file) self.ratelabel = QLabel() self.datelabel = QLabel() self.log_button = create_toolbutton(self, icon=get_icon('log.png'), text=_("Output"), text_beside_icon=True, tip=_("Complete output"), triggered=self.show_log) self.treewidget = ResultsTree(self) hlayout1 = QHBoxLayout() hlayout1.addWidget(self.filecombo) hlayout1.addWidget(browse_button) hlayout1.addWidget(self.start_button) hlayout1.addWidget(self.stop_button) hlayout2 = QHBoxLayout() hlayout2.addWidget(self.ratelabel) hlayout2.addStretch() hlayout2.addWidget(self.datelabel) hlayout2.addStretch() hlayout2.addWidget(self.log_button) layout = QVBoxLayout() layout.addLayout(hlayout1) layout.addLayout(hlayout2) layout.addWidget(self.treewidget) self.setLayout(layout) self.process = None self.set_running_state(False) if PYLINT_PATH is None: for widget in (self.treewidget, self.filecombo, self.start_button, self.stop_button): widget.setDisabled(True) if os.name == 'nt' \ and programs.is_module_installed("pylint"): # Pylint is installed but pylint script is not in PATH # (AFAIK, could happen only on Windows) text = _('Pylint script was not found. Please add "%s" to PATH.') text = to_text_string(text) % osp.join(sys.prefix, "Scripts") else: text = _('Please install <b>pylint</b>:') url = 'http://www.logilab.fr' text += ' <a href=%s>%s</a>' % (url, url) self.ratelabel.setText(text) else: self.show_data() def analyze(self, filename): if PYLINT_PATH is None: return filename = to_text_string(filename) # filename is a QString instance self.kill_if_running() index, _data = self.get_data(filename) if index is None: self.filecombo.addItem(filename) self.filecombo.setCurrentIndex(self.filecombo.count()-1) else: self.filecombo.setCurrentIndex(self.filecombo.findText(filename)) self.filecombo.selected() if self.filecombo.is_valid(): self.start() def select_file(self): self.emit(SIGNAL('redirect_stdio(bool)'), False) filename, _selfilter = getopenfilename(self, _("Select Python file"), getcwd(), _("Python files")+" (*.py ; *.pyw)") self.emit(SIGNAL('redirect_stdio(bool)'), False) if filename: self.analyze(filename) def remove_obsolete_items(self): """Removing obsolete items""" self.rdata = [(filename, data) for filename, data in self.rdata if is_module_or_package(filename)] def get_filenames(self): return [filename for filename, _data in self.rdata] def get_data(self, filename): filename = osp.abspath(filename) for index, (fname, data) in enumerate(self.rdata): if fname == filename: return index, data else: return None, None def set_data(self, filename, data): filename = osp.abspath(filename) index, _data = self.get_data(filename) if index is not None: self.rdata.pop(index) self.rdata.insert(0, (filename, data)) self.save() def save(self): while len(self.rdata) > self.max_entries: self.rdata.pop(-1) pickle.dump([self.VERSION]+self.rdata, open(self.DATAPATH, 'wb'), 2) def show_log(self): if self.output: TextEditor(self.output, title=_("Pylint output"), readonly=True, size=(700, 500)).exec_() def start(self): filename = to_text_string(self.filecombo.currentText()) self.process = QProcess(self) self.process.setProcessChannelMode(QProcess.SeparateChannels) self.process.setWorkingDirectory(osp.dirname(filename)) self.connect(self.process, SIGNAL("readyReadStandardOutput()"), self.read_output) self.connect(self.process, SIGNAL("readyReadStandardError()"), lambda: self.read_output(error=True)) self.connect(self.process, SIGNAL("finished(int,QProcess::ExitStatus)"), self.finished) self.connect(self.stop_button, SIGNAL("clicked()"), self.process.kill) self.output = '' self.error_output = '' plver = PYLINT_VER if plver is not None: if plver.split('.')[0] == '0': p_args = ['-i', 'yes'] else: # Option '-i' (alias for '--include-ids') was removed in pylint # 1.0 p_args = ["--msg-template='{msg_id}:{line:3d},"\ "{column}: {obj}: {msg}"] p_args += [osp.basename(filename)] else: p_args = [osp.basename(filename)] self.process.start(PYLINT_PATH, p_args) running = self.process.waitForStarted() self.set_running_state(running) if not running: QMessageBox.critical(self, _("Error"), _("Process failed to start")) def set_running_state(self, state=True): self.start_button.setEnabled(not state) self.stop_button.setEnabled(state) def read_output(self, error=False): if error: self.process.setReadChannel(QProcess.StandardError) else: self.process.setReadChannel(QProcess.StandardOutput) qba = QByteArray() while self.process.bytesAvailable(): if error: qba += self.process.readAllStandardError() else: qba += self.process.readAllStandardOutput() text = to_text_string( locale_codec.toUnicode(qba.data()) ) if error: self.error_output += text else: self.output += text def finished(self): self.set_running_state(False) if not self.output: if self.error_output: QMessageBox.critical(self, _("Error"), self.error_output) print("pylint error:\n\n" + self.error_output, file=sys.stderr) return # Convention, Refactor, Warning, Error results = {'C:': [], 'R:': [], 'W:': [], 'E:': []} txt_module = '************* Module ' module = '' # Should not be needed - just in case something goes wrong for line in self.output.splitlines(): if line.startswith(txt_module): # New module module = line[len(txt_module):] continue # Supporting option include-ids: ('R3873:' instead of 'R:') if not re.match('^[CRWE]+([0-9]{4})?:', line): continue i1 = line.find(':') if i1 == -1: continue msg_id = line[:i1] i2 = line.find(':', i1+1) if i2 == -1: continue line_nb = line[i1+1:i2].strip() if not line_nb: continue line_nb = int(line_nb.split(',')[0]) message = line[i2+1:] item = (module, line_nb, message, msg_id) results[line[0]+':'].append(item) # Rate rate = None txt_rate = 'Your code has been rated at ' i_rate = self.output.find(txt_rate) if i_rate > 0: i_rate_end = self.output.find('/10', i_rate) if i_rate_end > 0: rate = self.output[i_rate+len(txt_rate):i_rate_end] # Previous run previous = '' if rate is not None: txt_prun = 'previous run: ' i_prun = self.output.find(txt_prun, i_rate_end) if i_prun > 0: i_prun_end = self.output.find('/10', i_prun) previous = self.output[i_prun+len(txt_prun):i_prun_end] filename = to_text_string(self.filecombo.currentText()) self.set_data(filename, (time.localtime(), rate, previous, results)) self.output = self.error_output + self.output self.show_data(justanalyzed=True) def kill_if_running(self): if self.process is not None: if self.process.state() == QProcess.Running: self.process.kill() self.process.waitForFinished() def show_data(self, justanalyzed=False): if not justanalyzed: self.output = None self.log_button.setEnabled(self.output is not None \ and len(self.output) > 0) self.kill_if_running() filename = to_text_string(self.filecombo.currentText()) if not filename: return _index, data = self.get_data(filename) if data is None: text = _('Source code has not been rated yet.') self.treewidget.clear_results() date_text = '' else: datetime, rate, previous_rate, results = data if rate is None: text = _('Analysis did not succeed ' '(see output for more details).') self.treewidget.clear_results() date_text = '' else: text_style = "<span style=\'color: #444444\'><b>%s </b></span>" rate_style = "<span style=\'color: %s\'><b>%s</b></span>" prevrate_style = "<span style=\'color: #666666\'>%s</span>" color = "#FF0000" if float(rate) > 5.: color = "#22AA22" elif float(rate) > 3.: color = "#EE5500" text = _('Global evaluation:') text = (text_style % text)+(rate_style % (color, ('%s/10' % rate))) if previous_rate: text_prun = _('previous run:') text_prun = ' (%s %s/10)' % (text_prun, previous_rate) text += prevrate_style % text_prun self.treewidget.set_results(filename, results) date = to_text_string(time.strftime("%d %b %Y %H:%M", datetime), encoding='utf8') date_text = text_style % date self.ratelabel.setText(text) self.datelabel.setText(date_text)
class CondaProcess(QObject): """conda-api modified to work with QProcess""" ENCODING = 'ascii' def __init__(self, parent, on_finished=None, on_partial=None): QObject.__init__(self, parent) self._parent = parent self.output = None self.partial = None self.stdout = None self.error = None self._parse = False self._function_called = '' self._name = None self._process = QProcess() self._on_finished = on_finished self._process.finished.connect(self._call_conda_ready) self._process.readyReadStandardOutput.connect(self._call_conda_partial) if on_finished is not None: self._process.finished.connect(on_finished) if on_partial is not None: self._process.readyReadStandardOutput.connect(on_partial) self.set_root_prefix() def _call_conda_partial(self): """ """ stdout = self._process.readAllStandardOutput() stdout = handle_qbytearray(stdout, CondaProcess.ENCODING) stderr = self._process.readAllStandardError() stderr = handle_qbytearray(stderr, CondaProcess.ENCODING) if self._parse: self.output = json.loads(stdout) else: self.output = stdout self.partial = self.output self.stdout = self.output self.error = stderr # print(self.partial) # print(self.error) def _call_conda_ready(self): """function called when QProcess in _call_conda finishes task""" function = self._function_called if self.stdout is None: stdout = to_text_string(self._process.readAllStandardOutput(), encoding=CondaProcess.ENCODING) else: stdout = self.stdout if self.error is None: stderr = to_text_string(self._process.readAllStandardError(), encoding=CondaProcess.ENCODING) else: stderr = self.error if function == 'get_conda_version': pat = re.compile(r'conda:?\s+(\d+\.\d\S+|unknown)') m = pat.match(stderr.strip()) if m is None: m = pat.match(stdout.strip()) if m is None: raise Exception('output did not match: %r' % stderr) self.output = m.group(1) elif function == 'get_envs': info = self.output self.output = info['envs'] elif function == 'get_prefix_envname': name = self._name envs = self.output self.output = self._get_prefix_envname_helper(name, envs) self._name = None elif function == 'config_path': result = self.output self.output = result['rc_path'] elif function == 'config_get': result = self.output self.output = result['get'] elif (function == 'config_delete' or function == 'config_add' or function == 'config_set' or function == 'config_remove'): result = self.output self.output = result.get('warnings', []) elif function == 'pip': result = [] lines = self.output.split('\n') for line in lines: if '<pip>' in line: temp = line.split()[:-1] + ['pip'] result.append('-'.join(temp)) self.output = result if stderr.strip(): self.error = stderr # raise Exception('conda %r:\nSTDERR:\n%s\nEND' % (extra_args, # stderr.decode())) self._parse = False def _get_prefix_envname_helper(self, name, envs): """ """ global ROOTPREFIX if name == 'root': return ROOT_PREFIX for prefix in envs: if basename(prefix) == name: return prefix return None def _call_conda(self, extra_args, abspath=True): """ """ # call conda with the list of extra arguments, and return the tuple # stdout, stderr global ROOT_PREFIX if abspath: if sys.platform == 'win32': python = join(ROOT_PREFIX, 'python.exe') conda = join(ROOT_PREFIX, 'Scripts', 'conda-script.py') else: python = join(ROOT_PREFIX, 'bin/python') conda = join(ROOT_PREFIX, 'bin/conda') cmd_list = [python, conda] else: # just use whatever conda is on the path cmd_list = ['conda'] cmd_list.extend(extra_args) # try: # p = Popen(cmd_list, stdout=PIPE, stderr=PIPE) # except OSError: # raise Exception("could not invoke %r\n" % args) # return p.communicate() # adapted code # ------------ self.error, self.output = None, None self._process.start(cmd_list[0], cmd_list[1:]) def _call_and_parse(self, extra_args, abspath=True): """ """ # stdout, stderr = _call_conda(extra_args, abspath=abspath) # if stderr.decode().strip(): # raise Exception('conda %r:\nSTDERR:\n%s\nEND' % (extra_args, # stderr.decode())) # return json.loads(stdout.decode()) # adapted code # ------------ self._parse = True self._call_conda(extra_args, abspath=abspath) def _setup_install_commands_from_kwargs(self, kwargs, keys=tuple()): """ """ cmd_list = [] if kwargs.get('override_channels', False) and 'channel' not in kwargs: raise TypeError('conda search: override_channels requires channel') if 'env' in kwargs: cmd_list.extend(['--name', kwargs.pop('env')]) if 'prefix' in kwargs: cmd_list.extend(['--prefix', kwargs.pop('prefix')]) if 'channel' in kwargs: channel = kwargs.pop('channel') if isinstance(channel, str): cmd_list.extend(['--channel', channel]) else: cmd_list.append('--channel') cmd_list.extend(channel) for key in keys: if key in kwargs and kwargs[key]: cmd_list.append('--' + key.replace('_', '-')) return cmd_list def set_root_prefix(self, prefix=None): """ Set the prefix to the root environment (default is /opt/anaconda). This function should only be called once (right after importing conda_api). """ global ROOT_PREFIX if prefix: ROOT_PREFIX = prefix # find *some* conda instance, and then use info() to get 'root_prefix' else: pass # i = self.info(abspath=False) # self.ROOT_PREFIX = i['root_prefix'] ''' plat = 'posix' if sys.platform.lower().startswith('win'): listsep = ';' plat = 'win' else: listsep = ':' for p in os.environ['PATH'].split(listsep): if (os.path.exists(os.path.join(p, 'conda')) or os.path.exists(os.path.join(p, 'conda.exe')) or os.path.exists(os.path.join(p, 'conda.bat'))): # TEMPORARY: ROOT_PREFIX = os.path.dirname(p) # root prefix is 1 dir up i = info() # REAL: ROOT_PREFIX = i['root_prefix'] break else: # fall back to standard install location, which may be wrong if plat == 'win': ROOT_PREFIX = 'C:\Anaconda' else: ROOT_PREFIX = '/opt/anaconda' ''' # adapted code # ------------ if ROOT_PREFIX is None: qprocess = QProcess() cmd_list = ['conda', 'info', '--json'] qprocess.start(cmd_list[0], cmd_list[1:]) qprocess.waitForFinished() output = qprocess.readAllStandardOutput() output = handle_qbytearray(output, CondaProcess.ENCODING) info = json.loads(output) ROOT_PREFIX = info['root_prefix'] def get_conda_version(self): """ return the version of conda being used (invoked) as a string """ # pat = re.compile(r'conda:?\s+(\d+\.\d\S+|unknown)') # stdout, stderr = self._call_conda(['--version']) # # argparse outputs version to stderr in Python < 3.4. # # http://bugs.python.org/issue18920 # m = pat.match(stderr.decode().strip()) # if m is None: # m = pat.match(stdout.decode().strip()) # # if m is None: # raise Exception('output did not match: %r' % stderr) # return m.group(1) # adapted code # ------------ if self._process.state() == QProcess.NotRunning: self._function_called = 'get_conda_version' self._call_conda(['--version']) def get_envs(self): """ Return all of the (named) environment (this does not include the root environment), as a list of absolute path to their prefixes. """ # info = self._call_and_parse(['info', '--json']) # return info['envs'] # adapted code # ------------ if self._process.state() == QProcess.NotRunning: self._function_called = 'get_envs' self._call_and_parse(['info', '--json']) def get_prefix_envname(self, name): """ Given the name of an environment return its full prefix path, or None if it cannot be found. """ # if name == 'root': # return self.ROOT_PREFIX # for prefix in self.get_envs(): # if basename(prefix) == name: # return prefix # return None # adapted code # ------------ if self._process.state() == QProcess.NotRunning: self._name = name self._function_called = 'get_prefix_envname' self._call_and_parse(['info', '--json']) def info(self, abspath=True): """ Return a dictionary with configuration information. No guarantee is made about which keys exist. Therefore this function should only be used for testing and debugging. """ # return self._call_and_parse(['info', '--json'], abspath=abspath) # adapted code # ------------ if self._process.state() == QProcess.NotRunning: self._function_called = 'info' self._call_and_parse(['info', '--json'], abspath=abspath) def package_info(self, package, abspath=True): """ Return a dictionary with package information. Structure is { 'package_name': [{ 'depends': list, 'version': str, 'name': str, 'license': str, 'fn': ..., ... }] } """ # return self._call_and_parse(['info', package, '--json'], # abspath=abspath) # adapted code # ------------ if self._process.state() == QProcess.NotRunning: self._function_called = 'package_info' self._call_and_parse(['info', package, '--json'], abspath=abspath) def search(self, regex=None, spec=None, **kwargs): """ Search for packages. """ cmd_list = ['search', '--json'] if regex and spec: raise TypeError('conda search: only one of regex or spec allowed') if regex: cmd_list.append(regex) if spec: cmd_list.extend(['--spec', spec]) if 'platform' in kwargs: platform = kwargs.pop('platform') platforms = ('win-32', 'win-64', 'osx-64', 'linux-32', 'linux-64') if platform not in platforms: raise TypeError('conda search: platform must be one of ' + ', '.join(platforms)) cmd_list.extend(['--platform', platform]) cmd_list.extend( self._setup_install_commands_from_kwargs( kwargs, ('canonical', 'unknown', 'use_index_cache', 'outdated', 'override_channels'))) # return self._call_and_parse(cmd_list, # abspath=kwargs.get('abspath', True)) # adapted code # ------------ if self._process.state() == QProcess.NotRunning: self._function_called = 'search' self._call_and_parse(cmd_list, abspath=kwargs.get('abspath', True)) def share(self, prefix): """ Create a "share package" of the environment located in `prefix`, and return a dictionary with (at least) the following keys: - 'path': the full path to the created package - 'warnings': a list of warnings This file is created in a temp directory, and it is the callers responsibility to remove this directory (after the file has been handled in some way). """ # return self._call_and_parse(['share', '--json', '--prefix', prefix]) # adapted code # ------------ if self._process.state() == QProcess.NotRunning: self._function_called = 'share' self._call_and_parse(['share', '--json', '--prefix', prefix]) def create(self, name=None, path=None, pkgs=None): """ Create an environment either by name or path with a specified set of packages """ if not pkgs or not isinstance(pkgs, (list, tuple)): raise TypeError('must specify a list of one or more packages to ' 'install into new environment') cmd_list = ['create', '--yes', '--quiet'] if name: ref = name search = [os.path.join(d, name) for d in self.info()['envs_dirs']] cmd_list = ['create', '--yes', '--quiet', '--name', name] elif path: ref = path search = [path] cmd_list = ['create', '--yes', '--quiet', '--prefix', path] else: raise TypeError('must specify either an environment name or a path' ' for new environment') if any(os.path.exists(path) for path in search): raise CondaEnvExistsError('Conda environment [%s] already exists' % ref) cmd_list.extend(pkgs) # (out, err) = self._call_conda(cmd_list) # if err.decode().strip(): # raise CondaError('conda %s: %s' % (" ".join(cmd_list), # err.decode())) # return out # adapted code # ------------ if self._process.state() == QProcess.NotRunning: self._function_called = 'create' self._call_conda(cmd_list) def install(self, name=None, path=None, pkgs=None, dep=True): """ Install packages into an environment either by name or path with a specified set of packages """ if not pkgs or not isinstance(pkgs, (list, tuple)): raise TypeError('must specify a list of one or more packages to ' 'install into existing environment') cmd_list = ['install', '--yes', '--json', '--force-pscheck'] # cmd_list = ['install', '--yes', '--quiet'] if name: cmd_list.extend(['--name', name]) elif path: cmd_list.extend(['--prefix', path]) else: # just install into the current environment, whatever that is pass cmd_list.extend(pkgs) # (out, err) = self._call_conda(cmd_list) # if err.decode().strip(): # raise CondaError('conda %s: %s' % (" ".join(cmd_list), # err.decode())) # return out # adapted code # ------------ if not dep: cmd_list.extend(['--no-deps']) if self._process.state() == QProcess.NotRunning: self._function_called = 'install' self._call_conda(cmd_list) def update(self, *pkgs, **kwargs): """ Update package(s) (in an environment) by name. """ cmd_list = ['update', '--json', '--quiet', '--yes'] if not pkgs and not kwargs.get('all'): raise TypeError("Must specify at least one package to update, \ or all=True.") cmd_list.extend( self._setup_install_commands_from_kwargs( kwargs, ('dry_run', 'no_deps', 'override_channels', 'no_pin', 'force', 'all', 'use_index_cache', 'use_local', 'alt_hint'))) cmd_list.extend(pkgs) # result = self._call_and_parse(cmd_list, # abspath=kwargs.get('abspath', True)) # # if 'error' in result: # raise CondaError('conda %s: %s' % (" ".join(cmd_list), # result['error'])) # # return result # adapted code # ------------ if self._process.state() == QProcess.NotRunning: self._function_called = 'update' self._call_and_parse(cmd_list, abspath=kwargs.get('abspath', True)) def remove(self, *pkgs, **kwargs): """ Remove a package (from an environment) by name. Returns { success: bool, (this is always true), (other information) } """ cmd_list = ['remove', '--json', '--quiet', '--yes', '--force-pscheck'] # cmd_list = ['remove', '--json', '--quiet', '--yes'] if not pkgs and not kwargs.get('all'): raise TypeError("Must specify at least one package to remove, \ or all=True.") if kwargs.get('name') and kwargs.get('path'): raise TypeError('conda remove: At most one of name, path allowed') if kwargs.get('name'): cmd_list.extend(['--name', kwargs.pop('name')]) if kwargs.get('path'): cmd_list.extend(['--prefix', kwargs.pop('path')]) cmd_list.extend( self._setup_install_commands_from_kwargs( kwargs, ('dry_run', 'features', 'override_channels', 'no_pin', 'force', 'all'))) cmd_list.extend(pkgs) # result = self._call_and_parse(cmd_list, # abspath=kwargs.get('abspath', True)) # # if 'error' in result: # raise CondaError('conda %s: %s' % (" ".join(cmd_list), # result['error'])) # # return result # adapted code # ------------ if self._process.state() == QProcess.NotRunning: self._function_called = 'remove' self._call_and_parse(cmd_list, abspath=kwargs.get('abspath', True)) def remove_environment(self, name=None, path=None, **kwargs): """ Remove an environment entirely. See ``remove``. """ # return self.remove(name=name, path=path, all=True, **kwargs) # adapted code # ------------ if self._process.state() == QProcess.NotRunning: self._function_called = 'remove_environment' self.remove(name=name, path=path, all=True, **kwargs) def clone_environment(self, clone, name=None, path=None, **kwargs): """ Clone the environment ``clone`` into ``name`` or ``path``. """ cmd_list = ['create', '--json', '--quiet'] if (name and path) or not (name or path): raise TypeError("conda clone_environment: exactly one of name or \ path required") if name: cmd_list.extend(['--name', name]) if path: cmd_list.extend(['--prefix', path]) cmd_list.extend(['--clone', clone]) cmd_list.extend( self._setup_install_commands_from_kwargs( kwargs, ('dry_run', 'unknown', 'use_index_cache', 'use_local', 'no_pin', 'force', 'all', 'channel', 'override_channels', 'no_default_packages'))) # result = self._call_and_parse(cmd_list, # abspath=kwargs.get('abspath', True)) # # if 'error' in result: # raise CondaError('conda %s: %s' % (" ".join(cmd_list), # result['error'])) # # return result # adapted code # ------------ if self._process.state() == QProcess.NotRunning: self._function_called = 'clone_environment' self._call_and_parse(cmd_list, abspath=kwargs.get('abspath', True)) # def process(self, name=None, path=None, cmd=None, args=None, stdin=None, # stdout=None, stderr=None, timeout=None): # """ # Create a Popen process for cmd using the specified args but in the # conda environment specified by name or path. # # The returned object will need to be invoked with p.communicate() or # similar. # # :param name: name of conda environment # :param path: path to conda environment (if no name specified) # :param cmd: command to invoke # :param args: argument # :param stdin: stdin # :param stdout: stdout # :param stderr: stderr # :return: Popen object # """ # # if bool(name) == bool(path): # raise TypeError('exactly one of name or path must be specified') # # if not cmd: # raise TypeError('cmd to execute must be specified') # # if not args: # args = [] # # if name: # path = self.get_prefix_envname(name) # # plat = 'posix' # if sys.platform.lower().startswith('win'): # listsep = ';' # plat = 'win' # else: # listsep = ':' # # conda_env = dict(os.environ) # # if plat == 'posix': # conda_env['PATH'] = path + os.path.sep + 'bin' + listsep + \ # conda_env['PATH'] # else: # win # conda_env['PATH'] = path + os.path.sep + 'Scripts' + listsep + \ # conda_env['PATH'] # # conda_env['PATH'] = path + listsep + conda_env['PATH'] # # cmd_list = [cmd] # cmd_list.extend(args) # # try: # p = Popen(cmd_list, env=conda_env, stdin=stdin, stdout=stdout, # stderr=stderr) # except OSError: # raise Exception("could not invoke %r\n" % cmd_list) # return p def clone(self, path, prefix): """ Clone a "share package" (located at `path`) by creating a new environment at `prefix`, and return a dictionary with (at least) the following keys: - 'warnings': a list of warnings The directory `path` is located in, should be some temp directory or some other directory OUTSIDE /opt/anaconda. After calling this function, the original file (at `path`) may be removed (by the caller of this function). The return object is a list of warnings. """ # return self._call_and_parse(['clone', '--json', '--prefix', prefix, # path]) # adapted code # ------------ if self._process.state() == QProcess.NotRunning: self._function_called = 'clone' self._call_and_parse(['clone', '--json', '--prefix', prefix, path]) def _setup_config_from_kwargs(kwargs): cmd_list = ['--json', '--force'] if 'file' in kwargs: cmd_list.extend(['--file', kwargs['file']]) if 'system' in kwargs: cmd_list.append('--system') return cmd_list def config_path(self, **kwargs): """ Get the path to the config file. """ cmd_list = ['config', '--get'] cmd_list.extend(self._setup_config_from_kwargs(kwargs)) # result = self._call_and_parse(cmd_list, # abspath=kwargs.get('abspath', True)) # # if 'error' in result: # raise CondaError('conda %s: %s' % (" ".join(cmd_list), # result['error'])) # return result['rc_path'] # adapted code # ------------ if self._process.state() == QProcess.NotRunning: self._function_called = 'config_path' self._call_and_parse(cmd_list, abspath=kwargs.get('abspath', True)) def config_get(self, *keys, **kwargs): """ Get the values of configuration keys. Returns a dictionary of values. Note, the key may not be in the dictionary if the key wasn't set in the configuration file. """ cmd_list = ['config', '--get'] cmd_list.extend(keys) cmd_list.extend(self._setup_config_from_kwargs(kwargs)) # result = self._call_and_parse(cmd_list, # abspath=kwargs.get('abspath', True)) # # if 'error' in result: # raise CondaError('conda %s: %s' % (" ".join(cmd_list), # result['error'])) # return result['get'] # adapted code # ------------ if self._process.state() == QProcess.NotRunning: self._function_called = 'config_get' self._call_and_parse(cmd_list, abspath=kwargs.get('abspath', True)) def config_set(self, key, value, **kwargs): """ Set a key to a (bool) value. Returns a list of warnings Conda may have emitted. """ cmd_list = ['config', '--set', key, str(value)] cmd_list.extend(self._setup_config_from_kwargs(kwargs)) # result = self._call_and_parse(cmd_list, # abspath=kwargs.get('abspath', True)) # # if 'error' in result: # raise CondaError('conda %s: %s' % (" ".join(cmd_list), # result['error'])) # return result.get('warnings', []) # adapted code # ------------ if self._process.state() == QProcess.NotRunning: self._function_called = 'config_set' self._call_and_parse(cmd_list, abspath=kwargs.get('abspath', True)) def config_add(self, key, value, **kwargs): """ Add a value to a key. Returns a list of warnings Conda may have emitted. """ cmd_list = ['config', '--add', key, value] cmd_list.extend(self._setup_config_from_kwargs(kwargs)) # result = self._call_and_parse(cmd_list, # abspath=kwargs.get('abspath', True)) # # if 'error' in result: # raise CondaError('conda %s: %s' % (" ".join(cmd_list), # result['error'])) # return result.get('warnings', []) # adapted code # ------------ if self._process.state() == QProcess.NotRunning: self._function_called = 'config_add' self._call_and_parse(cmd_list, abspath=kwargs.get('abspath', True)) def config_remove(self, key, value, **kwargs): """ Remove a value from a key. Returns a list of warnings Conda may have emitted. """ cmd_list = ['config', '--remove', key, value] cmd_list.extend(self._setup_config_from_kwargs(kwargs)) # result = self._call_and_parse(cmd_list, # abspath=kwargs.get('abspath', True)) # # if 'error' in result: # raise CondaError('conda %s: %s' % (" ".join(cmd_list), # result['error'])) # return result.get('warnings', []) # adapted code # ------------ if self._process.state() == QProcess.NotRunning: self._function_called = 'config_remove' self._call_and_parse(cmd_list, abspath=kwargs.get('abspath', True)) def config_delete(self, key, **kwargs): """ Remove a key entirely. Returns a list of warnings Conda may have emitted. """ cmd_list = ['config', '--remove-key', key] cmd_list.extend(self._setup_config_from_kwargs(kwargs)) # result = self._call_and_parse(cmd_list, # abspath=kwargs.get('abspath', True)) # # if 'error' in result: # raise CondaError('conda %s: %s' % (" ".join(cmd_list), # result['error'])) # return result.get('warnings', []) # adapted code # ------------ if self._process.state() == QProcess.NotRunning: self._function_called = 'config_delete' self._call_and_parse(cmd_list, abspath=kwargs.get('abspath', True)) def run(self, command, abspath=True): """ Launch the specified app by name or full package name. Returns a dictionary containing the key "fn", whose value is the full package (ending in ``.tar.bz2``) of the app. """ cmd_list = ['run', '--json', command] # result = self._call_and_parse(cmd_list, abspath=abspath) # # if 'error' in result: # raise CondaError('conda %s: %s' % (" ".join(cmd_list), # result['error'])) # return result # adapted code # ------------ if self._process.state() == QProcess.NotRunning: self._function_called = 'run' self._call_and_parse(cmd_list, abspath=abspath) # def test(): # """ # Self-test function, which prints useful debug information. # This function returns None on success, and will crash the interpreter # on failure. # """ # print('sys.version: %r' % sys.version) # print('sys.prefix : %r' % sys.prefix) # print('conda_api.__version__: %r' % __version__) # print('conda_api.ROOT_PREFIX: %r' % ROOT_PREFIX) # if isdir(ROOT_PREFIX): # conda_version = get_conda_version() # print('conda version: %r' % conda_version) # print('conda info:') # d = info() # for kv in d.items(): # print('\t%s=%r' % kv) # assert d['conda_version'] == conda_version # else: # print('Warning: no such directory: %r' % ROOT_PREFIX) # print('OK') # ---- Additional methods not in conda-api def pip(self, name): """ """ cmd_list = ['list', '-n', name] if self._process.state() == QProcess.NotRunning: self._function_called = 'pip' self._call_conda(cmd_list) def dependencies(self, name=None, path=None, pkgs=None, dep=True): """ Install packages into an environment either by name or path with a specified set of packages """ if not pkgs or not isinstance(pkgs, (list, tuple)): raise TypeError('must specify a list of one or more packages to ' 'install into existing environment') cmd_list = ['install', '--dry-run', '--json'] cmd_list = ['install', '--dry-run', '--json', '--force-pscheck'] if not dep: cmd_list.extend(['--no-deps']) if name: cmd_list.extend(['--name', name]) elif path: cmd_list.extend(['--prefix', path]) else: # just install into the current environment, whatever that is pass cmd_list.extend(pkgs) if self._process.state() == QProcess.NotRunning: self._function_called = 'install_dry' self._call_and_parse(cmd_list)
class PluginClient(QObject): """ A class which handles a connection to a plugin through a QProcess. """ # Emitted when the plugin has initialized. initialized = Signal() # Emitted when the plugin has failed to load. errored = Signal() # Emitted when a request response is received. request_handled = Signal(object) def __init__(self, plugin_name, executable=None): super(PluginClient, self).__init__() self.plugin_name = plugin_name self.executable = executable or sys.executable self.start() def start(self): """Start a new connection with the plugin. """ self._initialized = False plugin_name = self.plugin_name self.sock, server_port = connect_to_port() self.sock.listen(2) QApplication.instance().aboutToQuit.connect(self.close) self.process = QProcess(self) self.process.setWorkingDirectory(os.path.dirname(__file__)) processEnvironment = QProcessEnvironment() env = self.process.systemEnvironment() python_path = osp.dirname(get_module_path('spyderlib')) # Use the current version of the plugin provider if possible. try: provider_path = osp.dirname(imp.find_module(self.plugin_name)[1]) python_path = osp.pathsep.join([python_path, provider_path]) except ImportError: pass env.append("PYTHONPATH=%s" % python_path) for envItem in env: envName, separator, envValue = envItem.partition('=') processEnvironment.insert(envName, envValue) self.process.setProcessEnvironment(processEnvironment) p_args = ['-u', 'plugin_server.py', str(server_port), plugin_name] self.listener = PluginListener(self.sock) self.listener.request_handled.connect(self.request_handled.emit) self.listener.initialized.connect(self._on_initialized) self.listener.start() self.process.start(self.executable, p_args) self.process.finished.connect(self._on_finished) running = self.process.waitForStarted() if not running: raise IOError('Could not start plugin %s' % plugin_name) def send(self, request): """Send a request to the plugin. """ if not self._initialized: return sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(("127.0.0.1", self.client_port)) request['plugin_name'] = self.plugin_name write_packet(sock, request) sock.close() def close(self): self.process.kill() self.process.waitForFinished(200) self.sock.close() def _on_initialized(self, port): debug_print('Initialized %s' % self.plugin_name) self._initialized = True self.client_port = port self.initialized.emit() def _on_finished(self): debug_print('finished %s %s' % (self.plugin_name, self._initialized)) if self._initialized: self.start() else: self._initialized = False debug_print(self.process.readAllStandardOutput()) debug_print(self.process.readAllStandardError()) self.errored.emit()
class AsyncClient(QObject): """ A class which handles a connection to a client through a QProcess. """ # Emitted when the client has initialized. initialized = Signal() # Emitted when the client errors. errored = Signal() # Emitted when a request response is received. received = Signal(object) def __init__(self, target, executable=None, name=None, extra_args=None, libs=None, cwd=None, env=None): super(AsyncClient, self).__init__() self.executable = executable or sys.executable self.extra_args = extra_args self.target = target self.name = name or self self.libs = libs self.cwd = cwd self.env = env self.is_initialized = False self.closing = False self.context = zmq.Context() QApplication.instance().aboutToQuit.connect(self.close) # Set up the heartbeat timer. self.timer = QTimer(self) self.timer.timeout.connect(self._heartbeat) self.timer.start(HEARTBEAT) def run(self): """Handle the connection with the server. """ # Set up the zmq port. self.socket = self.context.socket(zmq.PAIR) self.port = self.socket.bind_to_random_port('tcp://*') # Set up the process. self.process = QProcess(self) if self.cwd: self.process.setWorkingDirectory(self.cwd) p_args = ['-u', self.target, str(self.port)] if self.extra_args is not None: p_args += self.extra_args # Set up environment variables. processEnvironment = QProcessEnvironment() env = self.process.systemEnvironment() if (self.env and 'PYTHONPATH' not in self.env) or DEV: python_path = osp.dirname(get_module_path('spyderlib')) # Add the libs to the python path. for lib in self.libs: try: path = osp.dirname(imp.find_module(lib)[1]) python_path = osp.pathsep.join([python_path, path]) except ImportError: pass env.append("PYTHONPATH=%s" % python_path) if self.env: env.update(self.env) for envItem in env: envName, separator, envValue = envItem.partition('=') processEnvironment.insert(envName, envValue) self.process.setProcessEnvironment(processEnvironment) # Start the process and wait for started. self.process.start(self.executable, p_args) self.process.finished.connect(self._on_finished) running = self.process.waitForStarted() if not running: raise IOError('Could not start %s' % self) # Set up the socket notifer. fid = self.socket.getsockopt(zmq.FD) self.notifier = QSocketNotifier(fid, QSocketNotifier.Read, self) self.notifier.activated.connect(self._on_msg_received) def request(self, func_name, *args, **kwargs): """Send a request to the server. The response will be a dictionary the 'request_id' and the 'func_name' as well as a 'result' field with the object returned by the function call or or an 'error' field with a traceback. """ if not self.is_initialized: return request_id = uuid.uuid4().hex request = dict(func_name=func_name, args=args, kwargs=kwargs, request_id=request_id) try: self.socket.send_pyobj(request) except zmq.ZMQError: pass return request_id def close(self): """Cleanly close the connection to the server. """ self.closing = True self.timer.stop() self.request('server_quit') self.process.waitForFinished(1000) self.context.destroy(0) def _on_finished(self): """Handle a finished signal from the process. """ if self.closing: return if self.is_initialized: debug_print('Restarting %s' % self.name) debug_print(self.process.readAllStandardOutput()) debug_print(self.process.readAllStandardError()) self.is_initialized = False self.notifier.setEnabled(False) self.run() else: debug_print('Errored %s' % self.name) debug_print(self.process.readAllStandardOutput()) debug_print(self.process.readAllStandardError()) self.errored.emit() def _on_msg_received(self): """Handle a message trigger from the socket. """ self.notifier.setEnabled(False) while 1: try: resp = self.socket.recv_pyobj(flags=zmq.NOBLOCK) except zmq.ZMQError: self.notifier.setEnabled(True) return if not self.is_initialized: self.is_initialized = True debug_print('Initialized %s' % self.name) self.initialized.emit() continue resp['name'] = self.name self.received.emit(resp) def _heartbeat(self): """Send a heartbeat to keep the server alive. """ if not self.is_initialized: return self.socket.send_pyobj(dict(func_name='server_heartbeat'))
class LineProfilerWidget(QWidget): """ Line profiler widget """ DATAPATH = get_conf_path('lineprofiler.results') VERSION = '0.0.1' def __init__(self, parent): QWidget.__init__(self, parent) self.setWindowTitle("Line profiler") self.output = None self.error_output = None self.use_colors = True self._last_wdir = None self._last_args = None self._last_pythonpath = None self.filecombo = PythonModulesComboBox(self) self.start_button = create_toolbutton(self, icon=get_icon('run.png'), text=_("Profile by line"), tip=_("Run line profiler"), triggered=self.start, text_beside_icon=True) self.stop_button = create_toolbutton(self, icon=get_icon('terminate.png'), text=_("Stop"), tip=_("Stop current profiling"), text_beside_icon=True) self.connect(self.filecombo, SIGNAL('valid(bool)'), self.start_button.setEnabled) #self.connect(self.filecombo, SIGNAL('valid(bool)'), self.show_data) # FIXME: The combobox emits this signal on almost any event # triggering show_data() too early, too often. browse_button = create_toolbutton(self, icon=get_icon('fileopen.png'), tip=_('Select Python script'), triggered=self.select_file) self.datelabel = QLabel() self.log_button = create_toolbutton(self, icon=get_icon('log.png'), text=_("Output"), text_beside_icon=True, tip=_("Show program's output"), triggered=self.show_log) self.datatree = LineProfilerDataTree(self) self.collapse_button = create_toolbutton( self, icon=get_icon('collapse.png'), triggered=lambda dD=-1: self.datatree.collapseAll(), tip=_('Collapse all')) self.expand_button = create_toolbutton( self, icon=get_icon('expand.png'), triggered=lambda dD=1: self.datatree.expandAll(), tip=_('Expand all')) hlayout1 = QHBoxLayout() hlayout1.addWidget(self.filecombo) hlayout1.addWidget(browse_button) hlayout1.addWidget(self.start_button) hlayout1.addWidget(self.stop_button) hlayout2 = QHBoxLayout() hlayout2.addWidget(self.collapse_button) hlayout2.addWidget(self.expand_button) hlayout2.addStretch() hlayout2.addWidget(self.datelabel) hlayout2.addStretch() hlayout2.addWidget(self.log_button) layout = QVBoxLayout() layout.addLayout(hlayout1) layout.addLayout(hlayout2) layout.addWidget(self.datatree) self.setLayout(layout) self.process = None self.set_running_state(False) self.start_button.setEnabled(False) if not is_lineprofiler_installed(): for widget in (self.datatree, self.filecombo, self.log_button, self.start_button, self.stop_button, browse_button, self.collapse_button, self.expand_button): widget.setDisabled(True) text = _( '<b>Please install the <a href="%s">line_profiler module</a></b>' ) % WEBSITE_URL self.datelabel.setText(text) self.datelabel.setOpenExternalLinks(True) else: pass # self.show_data() def analyze(self, filename, wdir=None, args=None, pythonpath=None, use_colors=True): self.use_colors = use_colors if not is_lineprofiler_installed(): return self.kill_if_running() #index, _data = self.get_data(filename) index = None # FIXME: storing data is not implemented yet if index is None: self.filecombo.addItem(filename) self.filecombo.setCurrentIndex(self.filecombo.count() - 1) else: self.filecombo.setCurrentIndex(self.filecombo.findText(filename)) self.filecombo.selected() if self.filecombo.is_valid(): if wdir is None: wdir = osp.dirname(filename) self.start(wdir, args, pythonpath) def select_file(self): self.emit(SIGNAL('redirect_stdio(bool)'), False) filename, _selfilter = getopenfilename( self, _("Select Python script"), getcwd(), _("Python scripts") + " (*.py ; *.pyw)") self.emit(SIGNAL('redirect_stdio(bool)'), False) if filename: self.analyze(filename) def show_log(self): if self.output: TextEditor(self.output, title=_("Line profiler output"), readonly=True, size=(700, 500)).exec_() def show_errorlog(self): if self.error_output: TextEditor(self.error_output, title=_("Line profiler output"), readonly=True, size=(700, 500)).exec_() def start(self, wdir=None, args=None, pythonpath=None): filename = to_text_string(self.filecombo.currentText()) if wdir is None: wdir = self._last_wdir if wdir is None: wdir = osp.basename(filename) if args is None: args = self._last_args if args is None: args = [] if pythonpath is None: pythonpath = self._last_pythonpath self._last_wdir = wdir self._last_args = args self._last_pythonpath = pythonpath self.datelabel.setText(_('Profiling, please wait...')) self.process = QProcess(self) self.process.setProcessChannelMode(QProcess.SeparateChannels) self.process.setWorkingDirectory(wdir) self.connect(self.process, SIGNAL("readyReadStandardOutput()"), self.read_output) self.connect(self.process, SIGNAL("readyReadStandardError()"), lambda: self.read_output(error=True)) self.connect(self.process, SIGNAL("finished(int,QProcess::ExitStatus)"), self.finished) self.connect(self.stop_button, SIGNAL("clicked()"), self.process.kill) if pythonpath is not None: env = [ to_text_string(_pth) for _pth in self.process.systemEnvironment() ] baseshell.add_pathlist_to_PYTHONPATH(env, pythonpath) self.process.setEnvironment(env) self.output = '' self.error_output = '' p_args = ['-lvb', '-o', self.DATAPATH, filename] if args: p_args.extend(programs.shell_split(args)) if os.name == 'nt': # On Windows, one has to replace backslashes by slashes to avoid # confusion with escape characters (otherwise, for example, '\t' # will be interpreted as a tabulation): filename = osp.normpath(filename).replace(os.sep, '/') script_path = programs.find_program('kernprof.py') executable = '{0} {1}'.format(sys.executable, script_path) executable += ' ' + ' '.join(p_args) executable = executable.replace(os.sep, '/') self.process.start(executable) else: executable = 'kernprof.py' self.process.start(executable, p_args) running = self.process.waitForStarted() self.set_running_state(running) if not running: QMessageBox.critical(self, _("Error"), _("Process failed to start")) def set_running_state(self, state=True): self.start_button.setEnabled(not state) self.stop_button.setEnabled(state) def read_output(self, error=False): if error: self.process.setReadChannel(QProcess.StandardError) else: self.process.setReadChannel(QProcess.StandardOutput) qba = QByteArray() while self.process.bytesAvailable(): if error: qba += self.process.readAllStandardError() else: qba += self.process.readAllStandardOutput() text = to_text_string(locale_codec.toUnicode(qba.data())) if error: self.error_output += text else: self.output += text def finished(self): self.set_running_state(False) self.show_errorlog() # If errors occurred, show them. self.output = self.error_output + self.output # FIXME: figure out if show_data should be called here or # as a signal from the combobox self.show_data(justanalyzed=True) def kill_if_running(self): if self.process is not None: if self.process.state() == QProcess.Running: self.process.kill() self.process.waitForFinished() def show_data(self, justanalyzed=False): if not justanalyzed: self.output = None self.log_button.setEnabled(self.output is not None and len(self.output) > 0) self.kill_if_running() filename = to_text_string(self.filecombo.currentText()) if not filename: return self.datatree.load_data(self.DATAPATH) self.datelabel.setText(_('Sorting data, please wait...')) QApplication.processEvents() self.datatree.show_tree() text_style = "<span style=\'color: #444444\'><b>%s </b></span>" date_text = text_style % time.strftime("%d %b %Y %H:%M", time.localtime()) self.datelabel.setText(date_text)