spyder_line_profiler-0.1.1/0000755000175000017500000000000013066014063016205 5ustar jitsejitse00000000000000spyder_line_profiler-0.1.1/LICENSE.txt0000644000175000017500000000210613064043412020025 0ustar jitsejitse00000000000000The MIT License (MIT) Copyright (c) 2013 Spyder Project Contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. spyder_line_profiler-0.1.1/setup.py0000644000175000017500000000603713066013275017731 0ustar jitsejitse00000000000000# -*- coding: utf-8 -*- # # Copyright © 2013 Spyder Project Contributors # Licensed under the terms of the MIT License # (see LICENSE.txt for details) """ Setup script for spyder_line_profiler """ from setuptools import setup, find_packages import os import os.path as osp def get_version(): """Get version from source file""" import codecs with codecs.open("spyder_line_profiler/__init__.py", encoding="utf-8") as f: lines = f.read().splitlines() for l in lines: if "__version__" in l: version = l.split("=")[1].strip() version = version.replace("'", '').replace('"', '') return version def get_package_data(name, extlist): """Return data files for package *name* with extensions in *extlist*""" flist = [] # Workaround to replace os.path.relpath (not available until Python 2.6): offset = len(name) + len(os.pathsep) for dirpath, _dirnames, filenames in os.walk(name): for fname in filenames: if not fname.startswith('.') and osp.splitext(fname)[1] in extlist: flist.append(osp.join(dirpath, fname)[offset:]) return flist # Requirements REQUIREMENTS = ['line_profiler', 'spyder>=3'] EXTLIST = ['.jpg', '.png', '.json', '.mo', '.ini'] LIBNAME = 'spyder_line_profiler' LONG_DESCRIPTION = """ This is a plugin for the Spyder IDE that integrates the Python line profiler. It allows you to see the time spent in every line. Usage ----- Add a ``@profile`` decorator to the functions that you wish to profile then press Shift+F10 (line profiler default) to run the profiler on the current script, or go to ``Run > Profile line by line``. The results will be shown in a dockwidget, grouped by function. Lines with a stronger color take more time to run. .. image: https://raw.githubusercontent.com/spyder-ide/spyder-line-profiler/master/img_src/screenshot_profler.png """ setup( name=LIBNAME, version=get_version(), packages=find_packages(), package_data={LIBNAME: get_package_data(LIBNAME, EXTLIST)}, keywords=["Qt PyQt4 PyQt5 PySide spyder plugins spyplugins line_profiler profiler"], install_requires=REQUIREMENTS, url='https://github.com/spyder-ide/spyder-line-profiler', license='MIT', author="Spyder Project Contributors", description='Plugin for the Spyder IDE that integrates the Python line profiler.', long_description=LONG_DESCRIPTION, classifiers=[ 'Development Status :: 4 - Beta', 'Environment :: X11 Applications :: Qt', 'Environment :: Win32 (MS Windows)', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Topic :: Software Development', 'Topic :: Text Editors :: Integrated Development Environments (IDE)']) spyder_line_profiler-0.1.1/setup.cfg0000644000175000017500000000007313066014063020026 0ustar jitsejitse00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 spyder_line_profiler-0.1.1/README.rst0000644000175000017500000000531013064043412017671 0ustar jitsejitse00000000000000spyder_line_profiler =========================== Project information ------------------- .. image:: https://img.shields.io/pypi/l/spyder-line-profiler.svg :target: https://github.com/spyder-ide/spyder-line-profiler/blob/master/LICENSE.txt .. image:: https://img.shields.io/pypi/v/spyder-line-profiler.svg :target: https://pypi.python.org/pypi/spyder-line-profiler .. image:: https://badges.gitter.im/spyder-ide/spyder.svg :target: https://gitter.im/spyder-ide/public Build information ----------------- .. image:: https://travis-ci.org/spyder-ide/spyder-line-profiler.svg?branch=master :target: https://travis-ci.org/spyder-ide/spyder-line-profiler .. image:: https://ci.appveyor.com/api/projects/status/u8m20qgel4j155pn/branch/master?svg=true :target: https://ci.appveyor.com/project/spyder-ide/spyder-line-profiler .. image:: https://circleci.com/gh/spyder-ide/spyder-line-profiler/tree/master.svg?style=shield :target: https://circleci.com/gh/spyder-ide/spyder-line-profiler/tree/master .. image:: https://coveralls.io/repos/github/spyder-ide/spyder-line-profiler/badge.svg?branch=master :target: https://coveralls.io/github/spyder-ide/spyder-line-profiler?branch=master .. image:: https://www.quantifiedcode.com/api/v1/project/54e5f210a2dd4d979438fd8574650822/badge.svg :target: https://www.quantifiedcode.com/api/project/54e5f210a2dd4d979438fd8574650822 .. image:: https://scrutinizer-ci.com/g/spyder-ide/spyder-line-profiler/badges/quality-score.png?b=master :target: https://scrutinizer-ci.com/g/spyder-ide/spyder-line-profiler/?branch=master) Description ----------- This is a plugin to run the python `line profiler `_ from within the python IDE `spyder `_. The code is an adaptation of the profiler plugin integrated in spyder. Install instructions -------------------- See https://github.com/spyder-ide/spyder/wiki/User-plugins but in short, the following command installs the development version of the line-profiler plugin: :: pip install git+git://github.com/spyder-ide/spyder-line-profiler.git The plugin is not yet included in PyPI. You need to also install `Spyder `_ (version 3.0 or higher) and the `line profiler `_ . Usage ----- Add a ``@profile`` decorator to the functions that you wish to profile then press Shift+F10 (line profiler default) to run the profiler on the current script, or go to ``Run > Profile line by line``. The results will be shown in a dockwidget, grouped by function. Lines with a stronger color take more time to run. Screenshot ---------- Line profiler: .. image:: img_src/screenshot_profler.png spyder_line_profiler-0.1.1/spyder_line_profiler.egg-info/0000755000175000017500000000000013066014063024116 5ustar jitsejitse00000000000000spyder_line_profiler-0.1.1/spyder_line_profiler.egg-info/top_level.txt0000644000175000017500000000002513066014063026645 0ustar jitsejitse00000000000000spyder_line_profiler spyder_line_profiler-0.1.1/spyder_line_profiler.egg-info/PKG-INFO0000644000175000017500000000330213066014063025211 0ustar jitsejitse00000000000000Metadata-Version: 1.1 Name: spyder-line-profiler Version: 0.1.1 Summary: Plugin for the Spyder IDE that integrates the Python line profiler. Home-page: https://github.com/spyder-ide/spyder-line-profiler Author: Spyder Project Contributors Author-email: UNKNOWN License: MIT Description: This is a plugin for the Spyder IDE that integrates the Python line profiler. It allows you to see the time spent in every line. Usage ----- Add a ``@profile`` decorator to the functions that you wish to profile then press Shift+F10 (line profiler default) to run the profiler on the current script, or go to ``Run > Profile line by line``. The results will be shown in a dockwidget, grouped by function. Lines with a stronger color take more time to run. .. image: https://raw.githubusercontent.com/spyder-ide/spyder-line-profiler/master/img_src/screenshot_profler.png Keywords: Qt PyQt4 PyQt5 PySide spyder plugins spyplugins line_profiler profiler Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Environment :: X11 Applications :: Qt Classifier: Environment :: Win32 (MS Windows) Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Topic :: Software Development Classifier: Topic :: Text Editors :: Integrated Development Environments (IDE) spyder_line_profiler-0.1.1/spyder_line_profiler.egg-info/dependency_links.txt0000644000175000017500000000000113066014063030164 0ustar jitsejitse00000000000000 spyder_line_profiler-0.1.1/spyder_line_profiler.egg-info/SOURCES.txt0000644000175000017500000000156013066014063026004 0ustar jitsejitse00000000000000CHANGELOG.md LICENSE.txt MANIFEST.in README.rst setup.py spyder_line_profiler/__init__.py spyder_line_profiler/lineprofiler.py spyder_line_profiler.egg-info/PKG-INFO spyder_line_profiler.egg-info/SOURCES.txt spyder_line_profiler.egg-info/dependency_links.txt spyder_line_profiler.egg-info/requires.txt spyder_line_profiler.egg-info/top_level.txt spyder_line_profiler/data/__init__.py spyder_line_profiler/data/images/__init__.py spyder_line_profiler/data/images/spyder.line_profiler.png spyder_line_profiler/example/__init__.py spyder_line_profiler/example/profiling_test_script.py spyder_line_profiler/example/subdir/__init__.py spyder_line_profiler/example/subdir/profiling_test_script2.py spyder_line_profiler/widgets/__init__.py spyder_line_profiler/widgets/lineprofiler.py spyder_line_profiler/widgets/tests/__init__.py spyder_line_profiler/widgets/tests/test_lineprofiler.pyspyder_line_profiler-0.1.1/spyder_line_profiler.egg-info/requires.txt0000644000175000017500000000003013066014063026507 0ustar jitsejitse00000000000000line_profiler spyder>=3 spyder_line_profiler-0.1.1/spyder_line_profiler/0000755000175000017500000000000013066014063022424 5ustar jitsejitse00000000000000spyder_line_profiler-0.1.1/spyder_line_profiler/example/0000755000175000017500000000000013066014063024057 5ustar jitsejitse00000000000000spyder_line_profiler-0.1.1/spyder_line_profiler/example/subdir/0000755000175000017500000000000013066014063025347 5ustar jitsejitse00000000000000spyder_line_profiler-0.1.1/spyder_line_profiler/example/subdir/__init__.py0000644000175000017500000000000013052142265027447 0ustar jitsejitse00000000000000spyder_line_profiler-0.1.1/spyder_line_profiler/example/subdir/profiling_test_script2.py0000644000175000017500000000076113052142265032424 0ustar jitsejitse00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- u""" :author: Joseph Martinot-Lagarde Created on Sat Jan 19 14:57:57 2013 """ from __future__ import ( print_function, division, unicode_literals, absolute_import) @profile def fact2(n): result = 1 for i in range(2, n + 1): result *= i * 2 return result def sum2(n): result = 0 for i in range(1, n + 1): result += i * 2 return result if __name__ == "__main__": print(fact2(120)) print(sum2(120)) spyder_line_profiler-0.1.1/spyder_line_profiler/example/__init__.py0000644000175000017500000000000013052142265026157 0ustar jitsejitse00000000000000spyder_line_profiler-0.1.1/spyder_line_profiler/example/profiling_test_script.py0000644000175000017500000000175113052142265031052 0ustar jitsejitse00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- u""" :author: Joseph Martinot-Lagarde Created on Sat Jan 19 14:57:57 2013 """ from __future__ import ( print_function, division, unicode_literals, absolute_import) import subdir.profiling_test_script2 as script2 @profile def fact(n): result = 1 for i in range(2, n // 4): result *= i result = 1 # This is a comment for i in range(2, n // 16): result *= i result = 1 if False: # This won't be run raise RuntimeError("What are you doing here ???") for i in range(2, n + 1): result *= i return result # This is after the end of the function. if False: # This won't be run raise RuntimeError("It's getting bad.") @profile def sum_(n): result = 0 for i in range(1, n + 1): result += i return result if __name__ == "__main__": print(fact(120)) print(sum_(120)) print(script2.fact2(120)) print(script2.sum2(120)) spyder_line_profiler-0.1.1/spyder_line_profiler/widgets/0000755000175000017500000000000013066014063024072 5ustar jitsejitse00000000000000spyder_line_profiler-0.1.1/spyder_line_profiler/widgets/lineprofiler.py0000644000175000017500000005077413064043412027151 0ustar jitsejitse00000000000000# -*- coding: utf-8 -*- # # Copyright © 2011 Spyder Project Contributors # Licensed under the terms of the MIT License # (see LICENSE.txt for details) """ Line Profiler widget See the official documentation of line_profiler: http://pythonhosted.org/line_profiler/ """ # Standard library imports from __future__ import with_statement import hashlib import inspect import linecache import os import os.path as osp import time import sys # Third party imports from qtpy.compat import getopenfilename from qtpy.QtCore import (QByteArray, QProcess, Qt, QTextCodec, QProcessEnvironment, Signal) from qtpy.QtGui import QBrush, QColor, QFont from qtpy.QtWidgets import (QHBoxLayout, QWidget, QMessageBox, QVBoxLayout, QLabel, QTreeWidget, QTreeWidgetItem, QApplication) # Local imports from spyder.config.base import get_conf_path, get_translation from spyder.utils import programs from spyder.utils.qthelpers import create_toolbutton, get_icon from spyder.widgets.comboboxes import PythonModulesComboBox from spyder.utils.misc import add_pathlist_to_PYTHONPATH from spyder.widgets.variableexplorer.texteditor import TextEditor try: from spyder.py3compat import to_text_string, getcwd, pickle except ImportError: # python2 to_text_string = unicode getcwd = os.getcwdu import cPickle as pickle # This is needed for testing this module as a stand alone script try: _ = get_translation("line_profiler", dirname="spyder_line_profiler") except KeyError as error: import gettext _ = gettext.gettext locale_codec = QTextCodec.codecForLocale() COL_NO = 0 COL_HITS = 1 COL_TIME = 2 COL_PERHIT = 3 COL_PERCENT = 4 COL_LINE = 5 COL_POS = 0 # Position is not displayed but set as Qt.UserRole CODE_NOT_RUN_COLOR = QBrush(QColor.fromRgb(128, 128, 128, 200)) WEBSITE_URL = 'http://pythonhosted.org/line_profiler/' def is_lineprofiler_installed(): """ Checks if the program and the library for line_profiler is installed. """ return (programs.is_module_installed('line_profiler') and programs.find_program('kernprof') is not None) class LineProfilerWidget(QWidget): """ Line profiler widget. """ DATAPATH = get_conf_path('lineprofiler.results') VERSION = '0.0.1' redirect_stdio = Signal(bool) sig_finished = Signal() 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.filecombo.valid.connect(self.start_button.setEnabled) #self.filecombo.valid.connect(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 = _( 'Please install the line_profiler module' ) % 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.redirect_stdio.emit(False) filename, _selfilter = getopenfilename( self, _("Select Python script"), getcwd(), _("Python scripts")+" (*.py ; *.pyw)") self.redirect_stdio.emit(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.process.readyReadStandardOutput.connect(self.read_output) self.process.readyReadStandardError.connect( lambda: self.read_output(error=True)) self.process.finished.connect(self.finished) self.stop_button.clicked.connect(self.process.kill) if pythonpath is not None: env = [to_text_string(_pth) for _pth in self.process.systemEnvironment()] 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 = '' 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, '/') p_args = ['-lvb', '-o', '"' + self.DATAPATH + '"', '"' + filename + '"'] if args: p_args.extend(programs.shell_split(args)) executable = '"' + programs.find_program('kernprof') + '"' executable += ' ' + ' '.join(p_args) executable = executable.replace(os.sep, '/') self.process.start(executable) else: p_args = ['-lvb', '-o', self.DATAPATH, filename] if args: p_args.extend(programs.shell_split(args)) executable = 'kernprof' 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) self.sig_finished.emit() 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 = "%s " date_text = text_style % time.strftime("%d %b %Y %H:%M", time.localtime()) self.datelabel.setText(date_text) class LineProfilerDataTree(QTreeWidget): """ Convenience tree widget (with built-in model) to store and view line profiler data. """ def __init__(self, parent=None): QTreeWidget.__init__(self, parent) self.header_list = [ _('Line #'), _('Hits'), _('Time (ms)'), _('Per hit (ms)'), _('% Time'), _('Line contents')] self.stats = None # To be filled by self.load_data() self.max_time = 0 # To be filled by self.load_data() self.header().setDefaultAlignment(Qt.AlignCenter) self.setColumnCount(len(self.header_list)) self.setHeaderLabels(self.header_list) self.clear() self.itemActivated.connect(self.item_activated) def show_tree(self): """Populate the tree with line profiler data and display it.""" self.clear() # Clear before re-populating self.setItemsExpandable(True) self.setSortingEnabled(False) self.populate_tree() self.expandAll() for col in range(self.columnCount()-1): self.resizeColumnToContents(col) if self.topLevelItemCount() > 1: self.collapseAll() self.setSortingEnabled(True) self.sortItems(COL_POS, Qt.AscendingOrder) def load_data(self, profdatafile): """Load line profiler data saved by kernprof module""" # lstats has the following layout : # lstats.timings = # {(filename1, line_no1, function_name1): # [(line_no1, hits1, total_time1), # (line_no2, hits2, total_time2)], # (filename2, line_no2, function_name2): # [(line_no1, hits1, total_time1), # (line_no2, hits2, total_time2), # (line_no3, hits3, total_time3)]} # lstats.unit = time_factor with open(profdatafile, 'rb') as fid: lstats = pickle.load(fid) # First pass to group by filename self.stats = dict() linecache.checkcache() for func_info, stats in lstats.timings.items(): # func_info is a tuple containing (filename, line, function anme) filename, start_line_no = func_info[:2] # Read code start_line_no -= 1 # include the @profile decorator all_lines = linecache.getlines(filename) block_lines = inspect.getblock(all_lines[start_line_no:]) # Loop on each line of code func_stats = [] func_total_time = 0.0 next_stat_line = 0 for line_no, code_line in enumerate(block_lines): line_no += start_line_no + 1 # Lines start at 1 code_line = code_line.rstrip('\n') if (next_stat_line >= len(stats) or line_no != stats[next_stat_line][0]): # Line didn't run hits, line_total_time, time_per_hit = None, None, None else: # Compute line times hits, line_total_time = stats[next_stat_line][1:] line_total_time *= lstats.unit time_per_hit = line_total_time / hits func_total_time += line_total_time next_stat_line += 1 func_stats.append( [line_no, code_line, line_total_time, time_per_hit, hits]) # Compute percent time for line in func_stats: line_total_time = line[2] if line_total_time is None: line.append(None) else: line.append(line_total_time / func_total_time) # Fill dict self.stats[func_info] = [func_stats, func_total_time] def fill_item(self, item, filename, line_no, code, time, percent, perhit, hits): item.setData(COL_POS, Qt.UserRole, (osp.normpath(filename), line_no)) item.setData(COL_NO, Qt.DisplayRole, line_no) item.setData(COL_LINE, Qt.DisplayRole, code) if percent is None: percent = '' else: percent = '%.1f' % (100 * percent) item.setData(COL_PERCENT, Qt.DisplayRole, percent) item.setTextAlignment(COL_PERCENT, Qt.AlignCenter) if time is None: time = '' else: time = '%.3f' % (time * 1e3) item.setData(COL_TIME, Qt.DisplayRole, time) item.setTextAlignment(COL_TIME, Qt.AlignCenter) if perhit is None: perhit = '' else: perhit = '%.3f' % (perhit * 1e3) item.setData(COL_PERHIT, Qt.DisplayRole, perhit) item.setTextAlignment(COL_PERHIT, Qt.AlignCenter) if hits is None: hits = '' else: hits = '%d' % hits item.setData(COL_HITS, Qt.DisplayRole, hits) item.setTextAlignment(COL_HITS, Qt.AlignCenter) def populate_tree(self): """Create each item (and associated data) in the tree""" if not self.stats: warn_item = QTreeWidgetItem(self) warn_item.setData( 0, Qt.DisplayRole, _('No timings to display. ' 'Did you forget to add @profile decorators ?') .format(url=WEBSITE_URL)) warn_item.setFirstColumnSpanned(True) warn_item.setTextAlignment(0, Qt.AlignCenter) font = warn_item.font(0) font.setStyle(QFont.StyleItalic) warn_item.setFont(0, font) return try: monospace_font = self.window().editor.get_plugin_font() except AttributeError: # If run standalone for testing monospace_font = QFont("Courier New") monospace_font.setPointSize(10) for func_info, func_data in self.stats.items(): # Function name and position filename, start_line_no, func_name = func_info func_stats, func_total_time = func_data func_item = QTreeWidgetItem(self) func_item.setData( 0, Qt.DisplayRole, _('{func_name} ({time_ms:.3f}ms) in file "{filename}", ' 'line {line_no}').format( filename=filename, line_no=start_line_no, func_name=func_name, time_ms=func_total_time * 1e3)) func_item.setFirstColumnSpanned(True) func_item.setData(COL_POS, Qt.UserRole, (osp.normpath(filename), start_line_no)) # For sorting by time func_item.setData(COL_TIME, Qt.DisplayRole, func_total_time * 1e3) func_item.setData(COL_PERCENT, Qt.DisplayRole, func_total_time * 1e3) if self.parent().use_colors: # Choose deteministic unique color for the function md5 = hashlib.md5((filename + func_name).encode("utf8")).hexdigest() hue = (int(md5[:2], 16) - 68) % 360 # avoid blue (unreadable) func_color = QColor.fromHsv(hue, 200, 255) else: # Red color only func_color = QColor.fromRgb(255, 0, 0) # Lines of code for line_info in func_stats: line_item = QTreeWidgetItem(func_item) (line_no, code_line, line_total_time, time_per_hit, hits, percent) = line_info self.fill_item( line_item, filename, line_no, code_line, line_total_time, percent, time_per_hit, hits) # Color background if line_total_time is not None: alpha = percent color = QColor(func_color) color.setAlphaF(alpha) # Returns None color = QBrush(color) for col in range(self.columnCount()): line_item.setBackground(col, color) else: for col in range(self.columnCount()): line_item.setForeground(col, CODE_NOT_RUN_COLOR) # Monospace font for code line_item.setFont(COL_LINE, monospace_font) def item_activated(self, item): filename, line_no = item.data(COL_POS, Qt.UserRole) self.parent().edit_goto.emit(filename, line_no, '') def test(): """Run widget test""" from spyder.utils.qthelpers import qapplication app = qapplication() widget = LineProfilerWidget(None) widget.resize(800, 600) widget.show() widget.analyze(osp.normpath(osp.join(osp.dirname(__file__), os.pardir, 'tests/profiling_test_script.py')), use_colors=True) sys.exit(app.exec_()) if __name__ == '__main__': test() spyder_line_profiler-0.1.1/spyder_line_profiler/widgets/tests/0000755000175000017500000000000013066014063025234 5ustar jitsejitse00000000000000spyder_line_profiler-0.1.1/spyder_line_profiler/widgets/tests/test_lineprofiler.py0000644000175000017500000000435713064043412031346 0ustar jitsejitse00000000000000# -*- coding: utf-8 -*- # # Copyright © 2017 Spyder Project Contributors # Licensed under the terms of the MIT License # (see LICENSE.txt for details) """Tests for lineprofiler.py.""" # Standard library imports import os # Third party imports from pytestqt import qtbot from qtpy.QtCore import Qt from spyder.utils.qthelpers import qapplication MAIN_APP = qapplication() # Local imports from spyder_line_profiler.widgets.lineprofiler import LineProfilerWidget try: from unittest.mock import Mock except ImportError: from mock import Mock # Python 2 TEST_SCRIPT = \ """import time @profile def foo(): time.sleep(1) xs = [] for k in range(100): xs = xs + ['x'] foo()""" def test_profile_and_display_results(qtbot, tmpdir, monkeypatch): """Run profiler on simple script and check that results are okay.""" os.chdir(tmpdir.strpath) testfilename = tmpdir.join('test_foo.py').strpath with open(testfilename, 'w') as f: f.write(TEST_SCRIPT) MockQMessageBox = Mock() monkeypatch.setattr('spyder_line_profiler.widgets.lineprofiler.QMessageBox', MockQMessageBox) widget = LineProfilerWidget(None) qtbot.addWidget(widget) with qtbot.waitSignal(widget.sig_finished, timeout=10000, raising=True): widget.analyze(testfilename) MockQMessageBox.assert_not_called() dt = widget.datatree assert dt.topLevelItemCount() == 1 # number of functions profiled top = dt.topLevelItem(0) assert top.data(0, Qt.DisplayRole).startswith('foo ') assert top.childCount() == 6 for i in range(6): assert top.child(i).data(0, Qt.DisplayRole) == i + 2 # line no assert top.child(2).data(1, Qt.DisplayRole) == '1' # hits assert top.child(3).data(1, Qt.DisplayRole) == '1' assert top.child(4).data(1, Qt.DisplayRole) == '101' assert top.child(5).data(1, Qt.DisplayRole) == '100' assert float(top.child(2).data(2, Qt.DisplayRole)) >= 900 # time (ms) assert float(top.child(2).data(2, Qt.DisplayRole)) <= 1200 assert float(top.child(3).data(2, Qt.DisplayRole)) <= 100 assert float(top.child(4).data(2, Qt.DisplayRole)) <= 100 assert float(top.child(5).data(2, Qt.DisplayRole)) <= 100 spyder_line_profiler-0.1.1/spyder_line_profiler/widgets/tests/__init__.py0000644000175000017500000000000013066013275027337 0ustar jitsejitse00000000000000spyder_line_profiler-0.1.1/spyder_line_profiler/widgets/__init__.py0000644000175000017500000000000213045706742026204 0ustar jitsejitse00000000000000 spyder_line_profiler-0.1.1/spyder_line_profiler/data/0000755000175000017500000000000013066014063023335 5ustar jitsejitse00000000000000spyder_line_profiler-0.1.1/spyder_line_profiler/data/__init__.py0000644000175000017500000000000213045706742025447 0ustar jitsejitse00000000000000 spyder_line_profiler-0.1.1/spyder_line_profiler/data/images/0000755000175000017500000000000013066014063024602 5ustar jitsejitse00000000000000spyder_line_profiler-0.1.1/spyder_line_profiler/data/images/__init__.py0000644000175000017500000000000213045706742026714 0ustar jitsejitse00000000000000 spyder_line_profiler-0.1.1/spyder_line_profiler/data/images/spyder.line_profiler.png0000644000175000017500000000276713045706742031473 0ustar jitsejitse00000000000000PNG  IHDRw=sRGBgAMA a cHRMz&u0`:pQ<tEXtSoftwarePaint.NET v3.5.4>vPIDATHKUOg͘?mYdɲ;%K6r\ܧX~r`AG8@r80 gO|O>yzg{B90E8OxyyY+ɰ4Sfv?[:<@[y,?}62"BD,3߳jP]PfLMJ7E1[KrYF a5SWo?~]TGa [V_$-z `cS\w$;^IENDB`spyder_line_profiler-0.1.1/spyder_line_profiler/lineprofiler.py0000644000175000017500000001335613066013275025504 0ustar jitsejitse00000000000000# -*- coding: utf-8 -*- # # Copyright © 2013 Spyder Project Contributors # Licensed under the terms of the MIT License # (see LICENSE.txt for details) """Line profiler Plugin.""" # Third party imports from qtpy.QtCore import Qt, Signal from qtpy.QtWidgets import QGroupBox, QLabel, QVBoxLayout # Need running QApplication before importing runconfig from spyder.utils.qthelpers import qapplication MAIN_APP = qapplication() from spyder.config.base import get_translation from spyder.plugins import SpyderPluginWidget, runconfig from spyder.plugins.configdialog import PluginConfigPage from spyder.utils import icon_manager as ima from spyder.utils.qthelpers import create_action # Local imports from .data import images from .widgets.lineprofiler import LineProfilerWidget, is_lineprofiler_installed _ = get_translation("line_profiler", dirname="spyder_line_profiler") class LineProfilerConfigPage(PluginConfigPage): """ Widget with configuration options for line profiler. """ def setup_page(self): settings_group = QGroupBox(_("Settings")) use_color_box = self.create_checkbox( _("Use deterministic colors to differentiate functions"), 'use_colors', default=True) results_group = QGroupBox(_("Results")) results_label1 = QLabel(_("Line profiler plugin results " "(the output of kernprof.py)\n" "are stored here:")) results_label1.setWordWrap(True) # Warning: do not try to regroup the following QLabel contents with # widgets above -- this string was isolated here in a single QLabel # on purpose: to fix Issue 863 of Profiler plugon results_label2 = QLabel(LineProfilerWidget.DATAPATH) results_label2.setTextInteractionFlags(Qt.TextSelectableByMouse) results_label2.setWordWrap(True) settings_layout = QVBoxLayout() settings_layout.addWidget(use_color_box) settings_group.setLayout(settings_layout) results_layout = QVBoxLayout() results_layout.addWidget(results_label1) results_layout.addWidget(results_label2) results_group.setLayout(results_layout) vlayout = QVBoxLayout() vlayout.addWidget(settings_group) vlayout.addWidget(results_group) vlayout.addStretch(1) self.setLayout(vlayout) class LineProfiler(SpyderPluginWidget): """ Line profiler. """ CONF_SECTION = 'lineprofiler' CONFIGWIDGET_CLASS = LineProfilerConfigPage edit_goto = Signal(str, int, str) def __init__(self, parent=None): SpyderPluginWidget.__init__(self, parent) self.main = parent # Spyder 3 compatibility # Create widget and add to dockwindow self.widget = LineProfilerWidget(self.main) layout = QVBoxLayout() layout.addWidget(self.widget) self.setLayout(layout) # Initialize plugin self.initialize_plugin() # --- SpyderPluginWidget API ---------------------------------------------- def get_plugin_title(self): """Return widget title.""" return _("Line profiler") def get_plugin_icon(self): """Return widget icon.""" path = images.__path__[0] return ima.icon('spyder.line_profiler', icon_path=path) def get_focus_widget(self): """ Return the widget to give focus to when this plugin's dockwidget is raised on top-level. """ return self.widget.datatree def get_plugin_actions(self): """Return a list of actions related to plugin.""" return [] def on_first_registration(self): """Action to be performed on first plugin registration.""" self.main.tabify_plugins(self.main.help, self) self.dockwidget.hide() def register_plugin(self): """Register plugin in Spyder's main window.""" self.edit_goto.connect(self.main.editor.load) self.widget.redirect_stdio.connect(self.main.redirect_internalshell_stdio) self.main.add_dockwidget(self) lineprofiler_act = create_action(self, _("Profile line by line"), icon=self.get_plugin_icon(), shortcut="Shift+F10", triggered=self.run_lineprofiler) lineprofiler_act.setEnabled(is_lineprofiler_installed()) self.main.run_menu_actions += [lineprofiler_act] self.main.editor.pythonfile_dependent_actions += [lineprofiler_act] def refresh_plugin(self): """Refresh line profiler widget.""" pass def closing_plugin(self, cancelable=False): """Perform actions before parent main window is closed.""" return True def apply_plugin_settings(self, options): """Apply configuration file's plugin settings.""" pass # --- Public API ---------------------------------------------------------- def run_lineprofiler(self): """Run line profiler.""" self.analyze(self.main.editor.get_current_filename()) def analyze(self, filename): """Reimplement analyze method.""" if self.dockwidget and not self.ismaximized: self.dockwidget.setVisible(True) self.dockwidget.setFocus() self.dockwidget.raise_() pythonpath = self.main.get_spyder_pythonpath() runconf = runconfig.get_run_configuration(filename) wdir, args = None, None if runconf is not None: if runconf.wdir_enabled: wdir = runconf.wdir if runconf.args_enabled: args = runconf.args self.widget.analyze( filename, wdir=wdir, args=args, pythonpath=pythonpath, use_colors=self.get_option('use_colors', True)) spyder_line_profiler-0.1.1/spyder_line_profiler/__init__.py0000644000175000017500000000073713066013646024552 0ustar jitsejitse00000000000000# -*- coding: utf-8 -*- # # Copyright © 2013 Spyder Project Contributors # Licensed under the terms of the MIT License # (see LICENSE.txt for details) __version__ = '0.1.1' # ============================================================================= # The following statements are required to register this 3rd party plugin: # ============================================================================= from .lineprofiler import LineProfiler PLUGIN_CLASS = LineProfiler spyder_line_profiler-0.1.1/PKG-INFO0000644000175000017500000000330213066014063017300 0ustar jitsejitse00000000000000Metadata-Version: 1.1 Name: spyder_line_profiler Version: 0.1.1 Summary: Plugin for the Spyder IDE that integrates the Python line profiler. Home-page: https://github.com/spyder-ide/spyder-line-profiler Author: Spyder Project Contributors Author-email: UNKNOWN License: MIT Description: This is a plugin for the Spyder IDE that integrates the Python line profiler. It allows you to see the time spent in every line. Usage ----- Add a ``@profile`` decorator to the functions that you wish to profile then press Shift+F10 (line profiler default) to run the profiler on the current script, or go to ``Run > Profile line by line``. The results will be shown in a dockwidget, grouped by function. Lines with a stronger color take more time to run. .. image: https://raw.githubusercontent.com/spyder-ide/spyder-line-profiler/master/img_src/screenshot_profler.png Keywords: Qt PyQt4 PyQt5 PySide spyder plugins spyplugins line_profiler profiler Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Environment :: X11 Applications :: Qt Classifier: Environment :: Win32 (MS Windows) Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Topic :: Software Development Classifier: Topic :: Text Editors :: Integrated Development Environments (IDE) spyder_line_profiler-0.1.1/MANIFEST.in0000644000175000017500000000012513066013631017741 0ustar jitsejitse00000000000000include CHANGELOG.md LICENSE.txt README.rst recursive-include spyder_line_profiler * spyder_line_profiler-0.1.1/CHANGELOG.md0000644000175000017500000000053613066013617020026 0ustar jitsejitse00000000000000# History of changes ## Version 0.1.1 (2017/03/26) This version improves the packaging. The code itself was not changed. ### Pull Requests Merged * [PR 22](https://github.com/spyder-ide/spyder-line-profiler/pull/22) - Install tests alongside package In this release 1 pull request was closed. ## Version 0.1.0 (2017/03/22) Initial release.