spyder_memory_profiler-0.1.2/0000755000175000017500000000000013200667364016577 5ustar jitsejitse00000000000000spyder_memory_profiler-0.1.2/spyder_memory_profiler.egg-info/0000755000175000017500000000000013200667364025071 5ustar jitsejitse00000000000000spyder_memory_profiler-0.1.2/spyder_memory_profiler.egg-info/top_level.txt0000644000175000017500000000002713200667364027622 0ustar jitsejitse00000000000000spyder_memory_profiler spyder_memory_profiler-0.1.2/spyder_memory_profiler.egg-info/PKG-INFO0000644000175000017500000000310413200667364026164 0ustar jitsejitse00000000000000Metadata-Version: 1.1 Name: spyder-memory-profiler Version: 0.1.2 Summary: Plugin for the Spyder IDE that integrates the Python memory profiler Home-page: https://github.com/spyder-ide/spyder-memory-profiler Author: Spyder Project Contributors Author-email: UNKNOWN License: MIT Description: This is a plugin for the Spyder IDE that integrates the Python memory profiler. It allows you to see the memory usage in every line. Usage ----- Add a ``@profile`` decorator to the functions that you wish to profile then press Ctrl+Shift+F10 to run the profiler on the current script, or go to ``Run > Profile memory line by line``. The results will be shown in a dockwidget, grouped by function. Lines with a stronger color have the largest increments in memory usage. Keywords: Qt PyQt4 PyQt5 PySide spyder plugins spyplugins 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_memory_profiler-0.1.2/spyder_memory_profiler.egg-info/dependency_links.txt0000644000175000017500000000000113200667364031137 0ustar jitsejitse00000000000000 spyder_memory_profiler-0.1.2/spyder_memory_profiler.egg-info/SOURCES.txt0000644000175000017500000000163413200667364026761 0ustar jitsejitse00000000000000CHANGELOG.md LICENSE.txt MANIFEST.in README.rst setup.py spyder_memory_profiler/__init__.py spyder_memory_profiler/memoryprofiler.py spyder_memory_profiler.egg-info/PKG-INFO spyder_memory_profiler.egg-info/SOURCES.txt spyder_memory_profiler.egg-info/dependency_links.txt spyder_memory_profiler.egg-info/requires.txt spyder_memory_profiler.egg-info/top_level.txt spyder_memory_profiler/data/__init__.py spyder_memory_profiler/data/images/__init__.py spyder_memory_profiler/data/images/spyder.memory_profiler.png spyder_memory_profiler/example/__init__.py spyder_memory_profiler/example/profiling_test_script.py spyder_memory_profiler/example/subdir/__init__.py spyder_memory_profiler/example/subdir/profiling_test_script2.py spyder_memory_profiler/widgets/__init__.py spyder_memory_profiler/widgets/memoryprofiler.py spyder_memory_profiler/widgets/tests/__init__.py spyder_memory_profiler/widgets/tests/test_memoryprofiler.pyspyder_memory_profiler-0.1.2/spyder_memory_profiler.egg-info/requires.txt0000644000175000017500000000003213200667364027464 0ustar jitsejitse00000000000000memory_profiler spyder>=3 spyder_memory_profiler-0.1.2/LICENSE.txt0000644000175000017500000000210513160543202020405 0ustar jitsejitse00000000000000The MIT License (MIT) Copyright © 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_memory_profiler-0.1.2/setup.py0000644000175000017500000000562013160543202020301 0ustar jitsejitse00000000000000# -*- coding: utf-8 -*- # # Copyright © 2013 Spyder Project Contributors # Licensed under the terms of the MIT License # (see LICENSE.txt for details) 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_memory_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 = ['memory_profiler', 'spyder>=3'] EXTLIST = ['.jpg', '.png', '.json', '.mo', '.ini'] LIBNAME = 'spyder_memory_profiler' LONG_DESCRIPTION = """ This is a plugin for the Spyder IDE that integrates the Python memory profiler. It allows you to see the memory usage in every line. Usage ----- Add a ``@profile`` decorator to the functions that you wish to profile then press Ctrl+Shift+F10 to run the profiler on the current script, or go to ``Run > Profile memory line by line``. The results will be shown in a dockwidget, grouped by function. Lines with a stronger color have the largest increments in memory usage. """ 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 profiler"], install_requires=REQUIREMENTS, url='https://github.com/spyder-ide/spyder-memory-profiler', license='MIT', author='Spyder Project Contributors', description='Plugin for the Spyder IDE that integrates the Python' ' memory 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_memory_profiler-0.1.2/setup.cfg0000644000175000017500000000007313200667364020420 0ustar jitsejitse00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 spyder_memory_profiler-0.1.2/README.rst0000644000175000017500000000523013177153421020263 0ustar jitsejitse00000000000000spyder-memory-profiler ====================== Project information ------------------- .. image:: https://img.shields.io/pypi/l/spyder-memory-profiler.svg :target: https://github.com/spyder-ide/spyder-memory-profiler/blob/master/LICENSE.txt .. image:: https://img.shields.io/pypi/v/spyder-memory-profiler.svg :target: https://pypi.python.org/pypi/spyder-memory-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-memory-profiler.svg?branch=master :target: https://travis-ci.org/spyder-ide/spyder-memory-profiler .. image:: https://ci.appveyor.com/api/projects/status/gd88722qallyheoe/branch/master?svg=true :target: https://ci.appveyor.com/project/spyder-ide/spyder-memory-profiler .. image:: https://circleci.com/gh/spyder-ide/spyder-memory-profiler/tree/master.svg?style=shield :target: https://circleci.com/gh/spyder-ide/spyder-memory-profiler/tree/master .. image:: https://coveralls.io/repos/github/spyder-ide/spyder-memory-profiler/badge.svg?branch=master :target: https://coveralls.io/github/spyder-ide/spyder-memory-profiler?branch=master .. image:: https://www.quantifiedcode.com/api/v1/project/4a08fcbf42db40589ec02efd38597e8a/badge.svg :target: https://www.quantifiedcode.com/app/project/4a08fcbf42db40589ec02efd38597e8a .. image:: https://scrutinizer-ci.com/g/spyder-ide/spyder-memory-profiler/badges/quality-score.png?b=master :target: https://scrutinizer-ci.com/g/spyder-ide/spyder-memory-profiler/?branch=master) Description ----------- This is a plugin to run the python `memory_profiler `_ from within the python IDE `spyder `_. The code is an adaptation of the profiler plugin integrated in `spyder `_. Install instructions -------------------- The memory-profiler plugin is available in the ``spyder-ide`` channel in Anaconda and in PyPI, so it can be installed with the following commands: * Using Anaconda: ``conda install -c spyder-ide spyder-memory-profiler`` * Using pip: ``pip install spyder-memory-profiler`` All dependencies will be automatically installed. You have to restart Spyder before you can use the plugin. Usage ----- Add a ``@profile`` decorator to the functions that you wish to profile then Ctrl+Shift+F10 to run the profiler on the current script, or go to ``Run > Profile memory line by line``. The results will be shown in a dockwidget, grouped by function. Lines with a stronger color have the largest increments in memory usage (memory profiler). spyder_memory_profiler-0.1.2/spyder_memory_profiler/0000755000175000017500000000000013200667364023377 5ustar jitsejitse00000000000000spyder_memory_profiler-0.1.2/spyder_memory_profiler/example/0000755000175000017500000000000013200667364025032 5ustar jitsejitse00000000000000spyder_memory_profiler-0.1.2/spyder_memory_profiler/example/subdir/0000755000175000017500000000000013200667364026322 5ustar jitsejitse00000000000000spyder_memory_profiler-0.1.2/spyder_memory_profiler/example/subdir/__init__.py0000644000175000017500000000000013057072503030414 0ustar jitsejitse00000000000000spyder_memory_profiler-0.1.2/spyder_memory_profiler/example/subdir/profiling_test_script2.py0000644000175000017500000000077413057072503033375 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 xrange(2, n + 1): result *= i * 2 return result @profile def sum2(n): result = 0 for i in xrange(1, n + 1): result += i * 2 return result if __name__ == "__main__": print(fact2(120)) print(sum2(120)) spyder_memory_profiler-0.1.2/spyder_memory_profiler/example/__init__.py0000644000175000017500000000000013057072503027124 0ustar jitsejitse00000000000000spyder_memory_profiler-0.1.2/spyder_memory_profiler/example/profiling_test_script.py0000644000175000017500000000175513057072503032023 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 xrange(2, n // 4): result *= i result = 1 # This is a comment for i in xrange(2, n // 16): result *= i result = 1 if False: # This won't be run raise RuntimeError("What are you doing here ???") for i in xrange(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 xrange(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_memory_profiler-0.1.2/spyder_memory_profiler/widgets/0000755000175000017500000000000013200667364025045 5ustar jitsejitse00000000000000spyder_memory_profiler-0.1.2/spyder_memory_profiler/widgets/tests/0000755000175000017500000000000013200667364026207 5ustar jitsejitse00000000000000spyder_memory_profiler-0.1.2/spyder_memory_profiler/widgets/tests/__init__.py0000644000175000017500000000000013160543202030273 0ustar jitsejitse00000000000000spyder_memory_profiler-0.1.2/spyder_memory_profiler/widgets/tests/test_memoryprofiler.py0000644000175000017500000000445113177153450032676 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 memoryprofiler.py.""" from __future__ import division # Standard library imports import os import sys # Third party imports import pytest from pytestqt import qtbot from qtpy.QtCore import Qt from spyder.utils.qthelpers import qapplication MAIN_APP = qapplication() # Local imports from spyder_memory_profiler.widgets.memoryprofiler import MemoryProfilerWidget try: from unittest.mock import Mock except ImportError: from mock import Mock # Python 2 TEST_SCRIPT = \ """@profile def foo(): a = [1] * (10 ** 6) b = [2] * (2 * 10 ** 7) del b return a foo()""" @pytest.mark.qt_log_level_fail('WARNING') def test_profile_and_display_results(qtbot, tmpdir, monkeypatch): """ Run profiler on simple script and check that results are okay. This is a fairly simple integration test which checks that the plugin works on a basic level. """ 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_memory_profiler.widgets.memoryprofiler.QMessageBox', MockQMessageBox) widget = MemoryProfilerWidget(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 + 1 # line no # Column 2 has increment (in MiB); displayed as 'xxx MiB' so need to strip # last 4 characters. To make the test robust, we only check the sign assert float(top.child(2).data(2, Qt.DisplayRole)[:-4]) > 0 assert float(top.child(3).data(2, Qt.DisplayRole)[:-4]) > 0 assert float(top.child(4).data(2, Qt.DisplayRole)[:-4]) < 0 assert float(top.child(5).data(2, Qt.DisplayRole)[:-4]) == 0 spyder_memory_profiler-0.1.2/spyder_memory_profiler/widgets/__init__.py0000644000175000017500000000000213047154231027137 0ustar jitsejitse00000000000000 spyder_memory_profiler-0.1.2/spyder_memory_profiler/widgets/memoryprofiler.py0000644000175000017500000005437613177153421030506 0ustar jitsejitse00000000000000# -*- coding: utf-8 -*- # # Copyright © 2011 Spyder Project Contributors # Licensed under the terms of the MIT License # (see LICENSE.txt for details) """ Memory Profiler widget See the official documentation of memory_profiler: https://pypi.python.org/pypi/memory_profiler/ """ # Standar library imports from __future__ import with_statement import hashlib import inspect import linecache import os import os.path as osp import sys import time # 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) 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.utils.misc import add_pathlist_to_PYTHONPATH, get_python_executable from spyder.widgets.comboboxes import PythonModulesComboBox from spyder.widgets.variableexplorer.texteditor import TextEditor try: from spyder.py3compat import to_text_string, getcwd except ImportError: # python2 to_text_string = unicode getcwd = os.getcwdu locale_codec = QTextCodec.codecForLocale() _ = get_translation("memory_profiler", dirname="spyder_memory_profiler") COL_NO = 0 COL_USAGE = 1 COL_INCREMENT = 2 COL_LINE = 3 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 = 'https://pypi.python.org/pypi/memory_profiler/' def is_memoryprofiler_installed(): """ Checks if the library for memory_profiler is installed. """ return programs.is_module_installed('memory_profiler') class MemoryProfilerWidget(QWidget): """ Memory profiler widget. """ DATAPATH = get_conf_path('memoryprofiler.results') VERSION = '0.0.1' redirect_stdio = Signal(bool) sig_finished = Signal() def __init__(self, parent): QWidget.__init__(self, parent) self.setWindowTitle("Memory 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 memory usage"), tip=_("Run memory 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.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 = MemoryProfilerDataTree(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_memoryprofiler_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 memory_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_memoryprofiler_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=_("Memory profiler output"), readonly=True, size=(700, 500)).exec_() def show_errorlog(self): if self.error_output: TextEditor(self.error_output, title=_("Memory 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 = '' # remove previous results, since memory_profiler appends to output file # instead of replacing if osp.isfile(self.DATAPATH): os.remove(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): filename = osp.normpath(filename).replace(os.sep, '/') p_args = ['-m', 'memory_profiler', '-o', '"' + self.DATAPATH + '"', '"' + filename + '"'] if args: p_args.extend(programs.shell_split(args)) executable = get_python_executable() executable += ' ' + ' '.join(p_args) executable = executable.replace(os.sep, '/') self.process.start(executable) else: p_args = ['-m', 'memory_profiler', '-o', self.DATAPATH, filename] if args: p_args.extend(programs.shell_split(args)) executable = get_python_executable() 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 MemoryProfilerDataTree(QTreeWidget): """ Convenience tree widget (with built-in model) to store and view memory profiler data. """ def __init__(self, parent=None): QTreeWidget.__init__(self, parent) self.header_list = [ _('Line #'), _('Memory usage'), _('Increment'), _('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 memory 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) self.collapseAll() self.setSortingEnabled(True) self.sortItems(COL_POS, Qt.AscendingOrder) def load_data(self, profdatafile): """Load memory profiler data saved by memory_profiler module""" # NOTE: Description of lstats below is for line_profiler. Here we # create a mock Lstats class to emulate this behaviour, so we can reuse # the spyder_line_profiler code. The structure of lstats is the same, # but the entries (line_no, hits, total_time) are replaced # by (line_no, usage, increment) # 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, 'r') as fid: reslines = fid.readlines() # get the results into an "lstats"-like format so that the code below # (originally for line_profiler) can be used without much modification class Lstats(object): def __init__(self): self.timings = {} lstats = Lstats() # find lines in results where new function starts newFuncAtLine = [] for i, line in enumerate(reslines): if line.startswith('Filename: '): newFuncAtLine.append(i) # parse results from each function for i in newFuncAtLine: # filename filename = reslines[i].rstrip()[10:] # line number l = reslines[i+4].lstrip() line_no = int(l[:l.find(' ')]) # function name l = reslines[i+5] function_name = l[l.find('def')+4:l.find('(')] # initiate structure lstats.timings[(filename, line_no, function_name)] = [] # parse lines, add code lines and memory usage of current function for l in reslines[i+4:]: l = l.lstrip() # break on empty line (we have ended the current function results) if l == '': break # split string (discard empty strings using filter) stuff = list(filter(None, l.split(' '))) # get line number, mem usage, and mem increment lineno = int(stuff[0]) if len(stuff) >= 5 and stuff[2] == 'MiB': usage = float(stuff[1]) else: usage = None if len(stuff) >= 5 and stuff[4] == 'MiB': increment = float(stuff[3]) else: increment = None # append lstats.timings[(filename, line_no, function_name)].append( (lineno, usage, increment)) # 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_initial_usage = stats[0][1] func_peak_usage = 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 usage, increment = None, None else: # Compute line stats usage, increment = stats[next_stat_line][1:] if usage is not None: func_peak_usage = max(func_peak_usage, usage-func_initial_usage) next_stat_line += 1 func_stats.append( [line_no, code_line, usage, increment]) # Fill dict self.stats[func_info] = [func_stats, func_peak_usage] def fill_item(self, item, filename, line_no, code, usage, increment): 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 usage is None: usage = '' else: usage = '%.3f MiB' % (usage) item.setData(COL_USAGE, Qt.DisplayRole, usage) item.setTextAlignment(COL_USAGE, Qt.AlignCenter) if increment is None: increment = '' else: increment = '%.3f MiB' % (increment) item.setData(COL_INCREMENT, Qt.DisplayRole, increment) item.setTextAlignment(COL_INCREMENT, 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_peak_usage = func_data func_item = QTreeWidgetItem(self) func_item.setData( 0, Qt.DisplayRole, _('{func_name} (peak {peak_usage:.3f} MiB) in file "{filename}", ' 'line {line_no}').format( filename=filename, line_no=start_line_no, func_name=func_name, peak_usage=func_peak_usage)) 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_USAGE, Qt.DisplayRole, func_peak_usage) func_item.setData(COL_INCREMENT, Qt.DisplayRole, func_peak_usage) 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) # get max increment max_increment = 0 for line_info in func_stats: (line_no, code_line, usage, increment) = line_info if increment is not None: max_increment = max(max_increment, increment) # Lines of code for line_info in func_stats: line_item = QTreeWidgetItem(func_item) (line_no, code_line, usage, increment) = line_info self.fill_item( line_item, filename, line_no, code_line, usage, increment) # Color background if increment is not None: if increment > 0 and max_increment > 0: alpha = increment / max_increment else: alpha = 0 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 = MemoryProfilerWidget(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_memory_profiler-0.1.2/spyder_memory_profiler/data/0000755000175000017500000000000013200667364024310 5ustar jitsejitse00000000000000spyder_memory_profiler-0.1.2/spyder_memory_profiler/data/__init__.py0000644000175000017500000000000213047154231026402 0ustar jitsejitse00000000000000 spyder_memory_profiler-0.1.2/spyder_memory_profiler/data/images/0000755000175000017500000000000013200667364025555 5ustar jitsejitse00000000000000spyder_memory_profiler-0.1.2/spyder_memory_profiler/data/images/__init__.py0000644000175000017500000000000213047154231027647 0ustar jitsejitse00000000000000 spyder_memory_profiler-0.1.2/spyder_memory_profiler/data/images/spyder.memory_profiler.png0000644000175000017500000000276713047154231033007 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_memory_profiler-0.1.2/spyder_memory_profiler/__init__.py0000644000175000017500000000074513200667223025510 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.2' # ============================================================================= # The following statements are required to register this 3rd party plugin: # ============================================================================= from .memoryprofiler import MemoryProfiler PLUGIN_CLASS = MemoryProfiler spyder_memory_profiler-0.1.2/spyder_memory_profiler/memoryprofiler.py0000644000175000017500000001336213160543202027016 0ustar jitsejitse00000000000000# -*- coding: utf-8 -*- # # Copyright © 2013 Spyder Project Contributors # Licensed under the terms of the MIT License # (see LICENSE.txt for details) """Memory profiler Plugin.""" # Third party imports from qtpy.QtCore import Qt, Signal from qtpy.QtWidgets import QVBoxLayout, QGroupBox, QLabel # 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.qthelpers import get_icon, create_action # Local imports from .widgets.memoryprofiler import (MemoryProfilerWidget, is_memoryprofiler_installed) _ = get_translation("memory_profiler", dirname="spyder_memory_profiler") class MemoryProfilerConfigPage(PluginConfigPage): """ Widget with configuration options for memory 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(_("Memory profiler plugin results " "(the output of memory_profiler)\n" "is 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(MemoryProfilerWidget.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 MemoryProfiler(SpyderPluginWidget): """Memory profiler.""" CONF_SECTION = 'memoryprofiler' CONFIGWIDGET_CLASS = MemoryProfilerConfigPage 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 = MemoryProfilerWidget(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 _("Memory profiler") def get_plugin_icon(self): """Return widget icon.""" return get_icon('profiler.png') 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) memoryprofiler_act = create_action(self, _("Profile memory line by line"), icon=self.get_plugin_icon(), shortcut="Ctrl+Shift+F10", triggered=self.run_memoryprofiler) memoryprofiler_act.setEnabled(is_memoryprofiler_installed()) self.main.run_menu_actions += [memoryprofiler_act] self.main.editor.pythonfile_dependent_actions += [memoryprofiler_act] def refresh_plugin(self): """Refresh memory 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_memoryprofiler(self): """Run memory 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_memory_profiler-0.1.2/PKG-INFO0000644000175000017500000000310413200667364017672 0ustar jitsejitse00000000000000Metadata-Version: 1.1 Name: spyder_memory_profiler Version: 0.1.2 Summary: Plugin for the Spyder IDE that integrates the Python memory profiler Home-page: https://github.com/spyder-ide/spyder-memory-profiler Author: Spyder Project Contributors Author-email: UNKNOWN License: MIT Description: This is a plugin for the Spyder IDE that integrates the Python memory profiler. It allows you to see the memory usage in every line. Usage ----- Add a ``@profile`` decorator to the functions that you wish to profile then press Ctrl+Shift+F10 to run the profiler on the current script, or go to ``Run > Profile memory line by line``. The results will be shown in a dockwidget, grouped by function. Lines with a stronger color have the largest increments in memory usage. Keywords: Qt PyQt4 PyQt5 PySide spyder plugins spyplugins 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_memory_profiler-0.1.2/MANIFEST.in0000644000175000017500000000014013200665643020326 0ustar jitsejitse00000000000000include CHANGELOG.md LICENSE.txt README.rst recursive-include spyder_memory_profiler *.py *.png spyder_memory_profiler-0.1.2/CHANGELOG.md0000644000175000017500000000260713200667046020412 0ustar jitsejitse00000000000000# History of changes ## Version 0.1.2 (2017/11/08) This release fixes a packaging mistake in the 0.1.1 release. ### Pull Requests Merged * [PR 19](https://github.com/spyder-ide/spyder-memory-profiler/pull/19) - Restrict recursive inclusion filter In this release 1 pull request was closed. ## Version 0.1.1 (2017/11/03) This release fixes some minor bugs. ### Issues Closed * [Issue 16](https://github.com/spyder-ide/spyder-memory-profiler/issues/16) - QColor::setAlphaF called with negative value ([PR 17](https://github.com/spyder-ide/spyder-memory-profiler/pull/17)) * [Issue 14](https://github.com/spyder-ide/spyder-memory-profiler/issues/14) - Tests fail on 32-bit platforms ([PR 15](https://github.com/spyder-ide/spyder-memory-profiler/pull/15)) In this release 2 issues were closed. ### Pull Requests Merged * [PR 18](https://github.com/spyder-ide/spyder-memory-profiler/pull/18) - Make test more robust * [PR 17](https://github.com/spyder-ide/spyder-memory-profiler/pull/17) - Make sure that alpha value is not negative ([16](https://github.com/spyder-ide/spyder-memory-profiler/issues/16)) * [PR 15](https://github.com/spyder-ide/spyder-memory-profiler/pull/15) - Use sys.getsizeof() to get expected memory consumption in tests ([14](https://github.com/spyder-ide/spyder-memory-profiler/issues/14)) In this release 3 pull requests were closed. ## Version 0.1.0 (2017/03/26) Initial release.