Hello community,
here is the log from the commit of package python-memory_profiler for openSUSE:Factory checked in at 2019-04-08 20:53:24
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-memory_profiler (Old)
and /work/SRC/openSUSE:Factory/.python-memory_profiler.new.3908 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-memory_profiler"
Mon Apr 8 20:53:24 2019 rev:4 rq:691807 version:0.55.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-memory_profiler/python-memory_profiler.changes 2018-12-24 11:39:40.145543834 +0100
+++ /work/SRC/openSUSE:Factory/.python-memory_profiler.new.3908/python-memory_profiler.changes 2019-04-08 20:53:26.822564434 +0200
@@ -1,0 +2,6 @@
+Fri Apr 5 12:32:09 UTC 2019 - Tomáš Chvátal
+
+- Update to 0.55.0:
+ * no upstream changelog available
+
+-------------------------------------------------------------------
Old:
----
memory_profiler-0.52.0.tar.gz
New:
----
memory_profiler-0.55.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-memory_profiler.spec ++++++
--- /var/tmp/diff_new_pack.PcVrUS/_old 2019-04-08 20:53:27.506564936 +0200
+++ /var/tmp/diff_new_pack.PcVrUS/_new 2019-04-08 20:53:27.510564939 +0200
@@ -1,7 +1,7 @@
#
# spec file for package python-memory_profiler
#
-# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -18,21 +18,21 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
Name: python-memory_profiler
-Version: 0.52.0
+Version: 0.55.0
Release: 0
Summary: A module for monitoring memory usage of a python program
License: BSD-3-Clause
Group: Development/Languages/Python
-Url: http://pypi.python.org/pypi/memory_profiler
+URL: https://github.com/pythonprofilers/memory_profiler
Source: https://files.pythonhosted.org/packages/source/m/memory_profiler/memory_profiler-%{version}.tar.gz
BuildRequires: %{python_module setuptools}
+BuildRequires: fdupes
BuildRequires: python-rpm-macros
+Requires: python-psutil
+BuildArch: noarch
# SECTION tests
BuildRequires: %{python_module psutil}
# /SECTION
-Requires: python-psutil
-BuildArch: noarch
-
%python_subpackages
%description
@@ -49,18 +49,19 @@
%install
%python_install
-%{python_expand chmod -x %{buildroot}%{$python_sitelib}/memory_profiler-%version-py%{$python_bin_suffix}.egg-info/*}
+%python_expand %fdupes %{buildroot}%{$python_sitelib}
+%python_expand chmod -x %{buildroot}%{$python_sitelib}/memory_profiler-%{version}-py%{$python_bin_suffix}.egg-info/*
%check
-# adapted command from the Makefile which is not included in the tarball :(
+export LANG="en_US.UTF8"
%python_exec -m memory_profiler test/test_func.py
%python_exec -m memory_profiler test/test_loop.py
%python_exec -m memory_profiler test/test_as.py
%python_exec -m memory_profiler test/test_global.py
%python_exec -m memory_profiler test/test_precision_command_line.py
%python_exec -m memory_profiler test/test_gen.py
-# fails no matter what I set as LANG
-%{python_expand $python -m memory_profiler test/test_unicode.py || :}
+# unicode handling only proper in python3
+python3 -m memory_profiler test/test_unicode.py
%python_exec -m memory_profiler test/test_tracemalloc.py
%python_exec -m memory_profiler test/test_import.py
%python_exec -m memory_profiler test/test_memory_usage.py
++++++ memory_profiler-0.52.0.tar.gz -> memory_profiler-0.55.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/memory_profiler-0.52.0/PKG-INFO new/memory_profiler-0.55.0/PKG-INFO
--- old/memory_profiler-0.52.0/PKG-INFO 2018-02-16 01:44:00.000000000 +0100
+++ new/memory_profiler-0.55.0/PKG-INFO 2018-12-14 16:21:33.000000000 +0100
@@ -1,12 +1,11 @@
Metadata-Version: 1.1
Name: memory_profiler
-Version: 0.52.0
+Version: 0.55.0
Summary: A module for monitoring memory usage of a python program
Home-page: http://pypi.python.org/pypi/memory_profiler
Author: Fabian Pedregosa
Author-email: f@bianp.net
License: BSD
-Description-Content-Type: UNKNOWN
Description: .. image:: https://travis-ci.org/pythonprofilers/memory_profiler.svg?branch=master
:target: https://travis-ci.org/pythonprofilers/memory_profiler
@@ -23,9 +22,12 @@
==============
Installation
==============
- To install through easy_install or pip::
+ Install via pip::
- $ easy_install -U memory_profiler # pip install -U memory_profiler
+ $ pip install -U memory_profiler
+
+ The package is also available on `conda-forge
+ https://github.com/conda-forge/memory_profiler-feedstock`_.
To install from source, download the package, extract and type::
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/memory_profiler-0.52.0/README.rst new/memory_profiler-0.55.0/README.rst
--- old/memory_profiler-0.52.0/README.rst 2018-01-19 22:09:23.000000000 +0100
+++ new/memory_profiler-0.55.0/README.rst 2018-12-13 16:49:08.000000000 +0100
@@ -14,9 +14,12 @@
==============
Installation
==============
-To install through easy_install or pip::
+Install via pip::
- $ easy_install -U memory_profiler # pip install -U memory_profiler
+ $ pip install -U memory_profiler
+
+The package is also available on `conda-forge
+https://github.com/conda-forge/memory_profiler-feedstock`_.
To install from source, download the package, extract and type::
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/memory_profiler-0.52.0/memory_profiler.egg-info/PKG-INFO new/memory_profiler-0.55.0/memory_profiler.egg-info/PKG-INFO
--- old/memory_profiler-0.52.0/memory_profiler.egg-info/PKG-INFO 2018-02-16 01:44:00.000000000 +0100
+++ new/memory_profiler-0.55.0/memory_profiler.egg-info/PKG-INFO 2018-12-14 16:21:33.000000000 +0100
@@ -1,12 +1,11 @@
Metadata-Version: 1.1
Name: memory-profiler
-Version: 0.52.0
+Version: 0.55.0
Summary: A module for monitoring memory usage of a python program
Home-page: http://pypi.python.org/pypi/memory_profiler
Author: Fabian Pedregosa
Author-email: f@bianp.net
License: BSD
-Description-Content-Type: UNKNOWN
Description: .. image:: https://travis-ci.org/pythonprofilers/memory_profiler.svg?branch=master
:target: https://travis-ci.org/pythonprofilers/memory_profiler
@@ -23,9 +22,12 @@
==============
Installation
==============
- To install through easy_install or pip::
+ Install via pip::
- $ easy_install -U memory_profiler # pip install -U memory_profiler
+ $ pip install -U memory_profiler
+
+ The package is also available on `conda-forge
+ https://github.com/conda-forge/memory_profiler-feedstock`_.
To install from source, download the package, extract and type::
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/memory_profiler-0.52.0/memory_profiler.egg-info/SOURCES.txt new/memory_profiler-0.55.0/memory_profiler.egg-info/SOURCES.txt
--- old/memory_profiler-0.52.0/memory_profiler.egg-info/SOURCES.txt 2018-02-16 01:44:00.000000000 +0100
+++ new/memory_profiler-0.55.0/memory_profiler.egg-info/SOURCES.txt 2018-12-14 16:21:33.000000000 +0100
@@ -2,16 +2,17 @@
MANIFEST.in
README.rst
memory_profiler.py
-mprof
-mprof.bat
+mprof.py
setup.py
memory_profiler.egg-info/PKG-INFO
memory_profiler.egg-info/SOURCES.txt
memory_profiler.egg-info/dependency_links.txt
+memory_profiler.egg-info/entry_points.txt
memory_profiler.egg-info/requires.txt
memory_profiler.egg-info/top_level.txt
test/test_as.py
test/test_exception.py
+test/test_exit_code.py
test/test_func.py
test/test_gen.py
test/test_global.py
@@ -19,6 +20,7 @@
test/test_loop.py
test/test_loop_decorated.py
test/test_memory_usage.py
+test/test_mprof.py
test/test_mprofile.py
test/test_nested.py
test/test_precision_command_line.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/memory_profiler-0.52.0/memory_profiler.egg-info/entry_points.txt new/memory_profiler-0.55.0/memory_profiler.egg-info/entry_points.txt
--- old/memory_profiler-0.52.0/memory_profiler.egg-info/entry_points.txt 1970-01-01 01:00:00.000000000 +0100
+++ new/memory_profiler-0.55.0/memory_profiler.egg-info/entry_points.txt 2018-12-14 16:21:33.000000000 +0100
@@ -0,0 +1,3 @@
+[console_scripts]
+mprof = mprof:main
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/memory_profiler-0.52.0/memory_profiler.egg-info/top_level.txt new/memory_profiler-0.55.0/memory_profiler.egg-info/top_level.txt
--- old/memory_profiler-0.52.0/memory_profiler.egg-info/top_level.txt 2018-02-16 01:44:00.000000000 +0100
+++ new/memory_profiler-0.55.0/memory_profiler.egg-info/top_level.txt 2018-12-14 16:21:33.000000000 +0100
@@ -1 +1,2 @@
memory_profiler
+mprof
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/memory_profiler-0.52.0/memory_profiler.py new/memory_profiler-0.55.0/memory_profiler.py
--- old/memory_profiler-0.52.0/memory_profiler.py 2018-02-16 01:43:37.000000000 +0100
+++ new/memory_profiler-0.55.0/memory_profiler.py 2018-12-14 16:14:18.000000000 +0100
@@ -3,20 +3,22 @@
# .. we'll use this to pass it to the child script ..
_CLEAN_GLOBALS = globals().copy()
-__version__ = '0.52.0'
+__version__ = '0.55.0'
_CMD_USAGE = "python -m memory_profiler script_file.py"
-import time
-import sys
+from functools import wraps
+import inspect
+import linecache
+import logging
import os
import pdb
-import warnings
-import linecache
-import inspect
import subprocess
-import logging
+import sys
+import time
import traceback
+import warnings
+
if sys.platform == "win32":
# any value except signal.CTRL_C_EVENT and signal.CTRL_BREAK_EVENT
# can be used to kill a process unconditionally in Windows
@@ -46,6 +48,7 @@
if PY2:
import __builtin__ as builtins
+ from future_builtins import filter
else:
import builtins
@@ -405,13 +408,13 @@
# Write children to the stream file
if multiprocess:
- for idx, chldmem in enumerate(_get_child_memory(proc.pid)):
+ for idx, chldmem in enumerate(_get_child_memory(proc)):
stream.write("CHLD {0} {1:.6f} {2:.4f}\n".format(idx, chldmem, time.time()))
else:
# Create a nested list with the child memory
if multiprocess:
mem_usage = [mem_usage]
- for chldmem in _get_child_memory(proc.pid):
+ for chldmem in _get_child_memory(proc):
mem_usage.append(chldmem)
# Append the memory usage to the return value
@@ -594,9 +597,7 @@
prev_line_value = self[code].get(prev_lineno, None) if prev_lineno else None
prev_line_memory = prev_line_value[1] if prev_line_value else 0
- #inc = (memory-prev_line_memory)
- #print('trace lineno=%(lineno)s prev_lineno=%(prev_lineno)s mem=%(memory)s prev_inc=%(previous_inc)s inc=%(inc)s' % locals())
- self[code][lineno] = (previous_inc + (memory-prev_line_memory), max(memory, previous_memory))
+ self[code][lineno] = (max(previous_inc, memory-prev_line_memory), max(memory, previous_memory))
def items(self):
"""Iterate on the toplevel code blocks."""
@@ -1067,6 +1068,7 @@
if not tracemalloc.is_tracing():
tracemalloc.start()
if func is not None:
+ @wraps(func)
def wrapper(*args, **kwargs):
prof = LineProfiler(backend=backend)
val = prof(func)(*args, **kwargs)
@@ -1119,17 +1121,14 @@
ns = dict(_CLEAN_GLOBALS, profile=profiler)
_backend = choose_backend(backend)
sys.argv = [filename] + passed_args
- if PY2:
- execfile(filename, ns, ns)
- else:
+ try:
if _backend == 'tracemalloc' and has_tracemalloc:
tracemalloc.start()
- try:
- with open(filename) as f:
- exec(compile(f.read(), filename, 'exec'), ns, ns)
- finally:
- if has_tracemalloc and tracemalloc.is_tracing():
- tracemalloc.stop()
+ with open(filename) as f:
+ exec(compile(f.read(), filename, 'exec'), ns, ns)
+ finally:
+ if has_tracemalloc and tracemalloc.is_tracing():
+ tracemalloc.stop()
def run_module_with_profiler(module, profiler, backend, passed_args=[]):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/memory_profiler-0.52.0/mprof new/memory_profiler-0.55.0/mprof
--- old/memory_profiler-0.52.0/mprof 2018-02-16 01:41:27.000000000 +0100
+++ new/memory_profiler-0.55.0/mprof 1970-01-01 01:00:00.000000000 +0100
@@ -1,558 +0,0 @@
-#!/usr/bin/env python
-
-import glob
-import os
-import os.path as osp
-import sys
-import re
-import copy
-import time
-import math
-
-from collections import defaultdict
-from argparse import ArgumentParser, ArgumentError, REMAINDER, RawTextHelpFormatter
-
-import memory_profiler as mp
-
-ALL_ACTIONS = ("run", "rm", "clean", "list", "plot")
-help_msg = """
-Available commands:
-
- run run a given command or python file
- rm remove a given file generated by mprof
- clean clean the current directory from files created by mprof
- list display existing profiles, with indices
- plot plot memory consumption generated by mprof run
-
-Type mprof <command> --help for usage help on a specific command.
-For example, mprof plot --help will list all plotting options.
-"""
-
-
-def print_usage():
- print("Usage: %s <command> <options> <arguments>"
- % osp.basename(sys.argv[0]))
- print(help_msg)
-
-
-def get_action():
- """Pop first argument, check it is a valid action."""
- if len(sys.argv) <= 1:
- print_usage()
- sys.exit(1)
- if not sys.argv[1] in ALL_ACTIONS:
- print_usage()
- sys.exit(1)
-
- return sys.argv.pop(1)
-
-
-def get_profile_filenames(args):
- """Return list of profile filenames.
-
- Parameters
- ==========
- args (list)
- list of filename or integer. An integer is the index of the
- profile in the list of existing profiles. 0 is the oldest,
- -1 in the more recent.
- Non-existing files cause a ValueError exception to be thrown.
-
- Returns
- =======
- filenames (list)
- list of existing memory profile filenames. It is guaranteed
- that an given file name will not appear twice in this list.
- """
- profiles = glob.glob("mprofile_??????????????.dat")
- profiles.sort()
-
- if args is "all":
- filenames = copy.copy(profiles)
- else:
- filenames = []
- for arg in args:
- if arg == "--": # workaround
- continue
- try:
- index = int(arg)
- except ValueError:
- index = None
- if index is not None:
- try:
- filename = profiles[index]
- except IndexError:
- raise ValueError("Invalid index (non-existing file): %s" % arg)
-
- if filename not in filenames:
- filenames.append(filename)
- else:
- if osp.isfile(arg):
- if arg not in filenames:
- filenames.append(arg)
- elif osp.isdir(arg):
- raise ValueError("Path %s is a directory" % arg)
- else:
- raise ValueError("File %s not found" % arg)
-
- # Add timestamp files, if any
- for filename in reversed(filenames):
- parts = osp.splitext(filename)
- timestamp_file = parts[0] + "_ts" + parts[1]
- if osp.isfile(timestamp_file) and timestamp_file not in filenames:
- filenames.append(timestamp_file)
-
- return filenames
-
-
-def list_action():
- """Display existing profiles, with indices."""
- parser = ArgumentParser(
- usage='mprof list\nThis command takes no argument.')
- parser.add_argument('--version', action='version', version=mp.__version__)
- args = parser.parse_args()
-
- filenames = get_profile_filenames("all")
- for n, filename in enumerate(filenames):
- ts = osp.splitext(filename)[0].split('_')[-1]
- print("{index} {filename} {hour}:{min}:{sec} {day}/{month}/{year}"
- .format(index=n, filename=filename,
- year=ts[:4], month=ts[4:6], day=ts[6:8],
- hour=ts[8:10], min=ts[10:12], sec=ts[12:14]))
-
-
-def rm_action():
- """TODO: merge with clean_action (@pgervais)"""
- parser = ArgumentParser(usage='mprof rm [options] numbers_or_filenames')
- parser.add_argument('--version', action='version', version=mp.__version__)
- parser.add_argument("--dry-run", dest="dry_run", default=False,
- action="store_true",
- help="""Show what will be done, without actually doing it.""")
- parser.add_argument("numbers_or_filenames", nargs='*',
- help="""numbers or filenames removed""")
- args = parser.parse_args()
-
- if len(args.numbers_or_filenames) == 0:
- print("A profile to remove must be provided (number or filename)")
- sys.exit(1)
-
- filenames = get_profile_filenames(args.numbers_or_filenames)
- if args.dry_run:
- print("Files to be removed: ")
- for filename in filenames:
- print(filename)
- else:
- for filename in filenames:
- os.remove(filename)
-
-
-def clean_action():
- """Remove every profile file in current directory."""
- parser = ArgumentParser(
- usage='mprof clean\nThis command takes no argument.')
- parser.add_argument('--version', action='version', version=mp.__version__)
- parser.add_argument("--dry-run", dest="dry_run", default=False,
- action="store_true",
- help="""Show what will be done, without actually doing it.""")
- args = parser.parse_args()
-
- filenames = get_profile_filenames("all")
- if args.dry_run:
- print("Files to be removed: ")
- for filename in filenames:
- print(filename)
- else:
- for filename in filenames:
- os.remove(filename)
-
-
-def get_cmd_line(args):
- """Given a set or arguments, compute command-line."""
- blanks = set(' \t')
- args = [s if blanks.isdisjoint(s) else "'" + s + "'" for s in args]
- return ' '.join(args)
-
-
-def run_action():
- import time, subprocess
- parser = ArgumentParser(usage="mprof run [options] program", formatter_class=RawTextHelpFormatter)
- parser.add_argument('--version', action='version', version=mp.__version__)
- parser.add_argument("--python", dest="python", action="store_true",
- help="""Activates extra features when the profiling executable is a Python program (currently: function timestamping.)""")
- parser.add_argument("--nopython", dest="nopython", action="store_true",
- help="""Disables extra features when the profiled executable is a Python program (currently: function timestamping.)""")
- parser.add_argument("--interval", "-T", dest="interval", default="0.1", type=float, action="store",
- help="Sampling period (in seconds), defaults to 0.1")
- parser.add_argument("--include-children", "-C", dest="include_children", action="store_true",
- help="""Monitors forked processes as well (sum up all process memory)""")
- parser.add_argument("--multiprocess", "-M", dest="multiprocess", action="store_true",
- help="""Monitors forked processes creating individual plots for each child (disables --python features)""")
- parser.add_argument("program", nargs=REMAINDER,
- help='Option 1: "<EXECUTABLE> <ARG1> <ARG2>..." - profile executable\n'
- 'Option 2: " <ARG1> <ARG2>..." - profile python script\n'
- 'Option 3: (--python flag present) " <ARG1> <ARG2>..." - profile python script with specified interpreter\n'
- 'Option 4: (--python flag present) " <ARG1> <ARG2>..." - profile python module\n'
- )
- args = parser.parse_args()
-
- if len(args.program) == 0:
- print("A program to run must be provided. Use -h for help")
- sys.exit(1)
-
- print("{1}: Sampling memory every {0}s".format(
- args.interval, osp.basename(sys.argv[0])))
-
- ## Output results in a file called "mprofile_<YYYYMMDDhhmmss>.dat" (where
- ## <YYYYMMDDhhmmss> is the date-time of the program start) in the current
- ## directory. This file contains the process memory consumption, in Mb (one
- ## value per line). Memory is sampled twice each second."""
-
- suffix = time.strftime("%Y%m%d%H%M%S", time.localtime())
- mprofile_output = "mprofile_%s.dat" % suffix
-
- # .. TODO: more than one script as argument ? ..
- program = args.program
- if program[0].endswith('.py') and not args.nopython:
- if args.multiprocess:
- # in multiprocessing mode you want to spawn a separate
- # python process
- if not program[0].startswith("python"):
- program.insert(0, sys.executable)
- args.python = False
- else:
- args.python = True
- if args.python:
- print("running as a Python program...")
- if not program[0].startswith("python"):
- program.insert(0, sys.executable)
- cmd_line = get_cmd_line(program)
- program[1:1] = ("-m", "memory_profiler", "--timestamp",
- "-o", mprofile_output)
- p = subprocess.Popen(program)
- else:
- cmd_line = get_cmd_line(program)
- p = subprocess.Popen(program)
-
- with open(mprofile_output, "a") as f:
- f.write("CMDLINE {0}\n".format(cmd_line))
- mp.memory_usage(proc=p, interval=args.interval, timestamps=True,
- include_children=args.include_children,
- multiprocess=args.multiprocess, stream=f)
-
-
-def add_brackets(xloc, yloc, xshift=0, color="r", label=None, options=None):
- """Add two brackets on the memory line plot.
-
- This function uses the current figure.
-
- Parameters
- ==========
- xloc: tuple with 2 values
- brackets location (on horizontal axis).
- yloc: tuple with 2 values
- brackets location (on vertical axis)
- xshift: float
- value to subtract to xloc.
- """
- try:
- import pylab as pl
- except ImportError as e:
- print("matplotlib is needed for plotting.")
- print(e)
- sys.exit(1)
- height_ratio = 20.
- vsize = (pl.ylim()[1] - pl.ylim()[0]) / height_ratio
- hsize = (pl.xlim()[1] - pl.xlim()[0]) / (3. * height_ratio)
-
- bracket_x = pl.asarray([hsize, 0, 0, hsize])
- bracket_y = pl.asarray([vsize, vsize, -vsize, -vsize])
-
- # Matplotlib workaround: labels starting with _ aren't displayed
- if label[0] == '_':
- label = ' ' + label
- if options.xlim is None or options.xlim[0] <= (xloc[0] - xshift) <= options.xlim[1]:
- pl.plot(bracket_x + xloc[0] - xshift, bracket_y + yloc[0],
- "-" + color, linewidth=2, label=label)
- if options.xlim is None or options.xlim[0] <= (xloc[1] - xshift) <= options.xlim[1]:
- pl.plot(-bracket_x + xloc[1] - xshift, bracket_y + yloc[1],
- "-" + color, linewidth=2)
-
- # TODO: use matplotlib.patches.Polygon to draw a colored background for
- # each function.
-
- # with maplotlib 1.2, use matplotlib.path.Path to create proper markers
- # see http://matplotlib.org/examples/pylab_examples/marker_path.html
- # This works with matplotlib 0.99.1
- ## pl.plot(xloc[0], yloc[0], "<"+color, markersize=7, label=label)
- ## pl.plot(xloc[1], yloc[1], ">"+color, markersize=7)
-
-
-def read_mprofile_file(filename):
- """Read an mprofile file and return its content.
-
- Returns
- =======
- content: dict
- Keys:
-
- - "mem_usage": (list) memory usage values, in MiB
- - "timestamp": (list) time instant for each memory usage value, in
- second
- - "func_timestamp": (dict) for each function, timestamps and memory
- usage upon entering and exiting.
- - 'cmd_line': (str) command-line ran for this profile.
- """
- func_ts = {}
- mem_usage = []
- timestamp = []
- children = defaultdict(list)
- cmd_line = None
- f = open(filename, "r")
- for l in f:
- if l == '\n':
- raise ValueError('Sampling time was too short')
- field, value = l.split(' ', 1)
- if field == "MEM":
- # mem, timestamp
- values = value.split(' ')
- mem_usage.append(float(values[0]))
- timestamp.append(float(values[1]))
-
- elif field == "FUNC":
- values = value.split(' ')
- f_name, mem_start, start, mem_end, end = values[:5]
- ts = func_ts.get(f_name, [])
- ts.append([float(start), float(end),
- float(mem_start), float(mem_end)])
- func_ts[f_name] = ts
-
- elif field == "CHLD":
- values = value.split(' ')
- chldnum = values[0]
- children[chldnum].append(
- (float(values[1]), float(values[2]))
- )
-
- elif field == "CMDLINE":
- cmd_line = value
- else:
- pass
- f.close()
-
- return {"mem_usage": mem_usage, "timestamp": timestamp,
- "func_timestamp": func_ts, 'filename': filename,
- 'cmd_line': cmd_line, 'children': children}
-
-
-def plot_file(filename, index=0, timestamps=True, children=True, options=None):
- try:
- import pylab as pl
- except ImportError as e:
- print("matplotlib is needed for plotting.")
- print(e)
- sys.exit(1)
- import numpy as np # pylab requires numpy anyway
- mprofile = read_mprofile_file(filename)
-
- if len(mprofile['timestamp']) == 0:
- print('** No memory usage values have been found in the profile '
- 'file.**\nFile path: {0}\n'
- 'File may be empty or invalid.\n'
- 'It can be deleted with "mprof rm {0}"'.format(
- mprofile['filename']))
- sys.exit(0)
-
- # Merge function timestamps and memory usage together
- ts = mprofile['func_timestamp']
- t = mprofile['timestamp']
- mem = mprofile['mem_usage']
- chld = mprofile['children']
-
- if len(ts) > 0:
- for values in ts.values():
- for v in values:
- t.extend(v[:2])
- mem.extend(v[2:4])
-
- mem = np.asarray(mem)
- t = np.asarray(t)
- ind = t.argsort()
- mem = mem[ind]
- t = t[ind]
-
- # Plot curves
- global_start = float(t[0])
- t = t - global_start
-
- max_mem = mem.max()
- max_mem_ind = mem.argmax()
-
- all_colors = ("c", "y", "g", "r", "b")
- mem_line_colors = ("k", "b", "r", "g", "c", "y", "m")
- mem_line_label = time.strftime("%d / %m / %Y - start at %H:%M:%S",
- time.localtime(global_start)) \
- + ".{0:03d}".format(int(round(math.modf(global_start)[0] * 1000)))
-
- pl.plot(t, mem, "+-" + mem_line_colors[index % len(mem_line_colors)],
- label=mem_line_label)
-
- bottom, top = pl.ylim()
- bottom += 0.001
- top -= 0.001
-
- # plot children, if any
- if len(chld) > 0 and children:
- cmpoint = (0,0) # maximal child memory
-
- for idx, (proc, data) in enumerate(chld.items()):
- # Create the numpy arrays from the series data
- cts = np.asarray([item[1] for item in data]) - global_start
- cmem = np.asarray([item[0] for item in data])
-
- # Plot the line to the figure
- pl.plot(cts, cmem, "+-" + mem_line_colors[(idx+1) % len(mem_line_colors)],
- label="child {}".format(proc))
-
- # Detect the maximal child memory point
- cmax_mem = cmem.max()
- if cmax_mem > cmpoint[1]:
- cmpoint = (cts[cmem.argmax()], cmax_mem)
-
- # Add the marker lines for the maximal child memory usage
- pl.vlines(cmpoint[0], pl.ylim()[0]+0.001, pl.ylim()[1] - 0.001, 'r', '--')
- pl.hlines(cmpoint[1], pl.xlim()[0]+0.001, pl.xlim()[1] - 0.001, 'r', '--')
-
- # plot timestamps, if any
- if len(ts) > 0 and timestamps:
- func_num = 0
- for f, exec_ts in ts.items():
- for execution in exec_ts:
- add_brackets(execution[:2], execution[2:], xshift=global_start,
- color=all_colors[func_num % len(all_colors)],
- label=f.split(".")[-1]
- + " %.3fs" % (execution[1] - execution[0]), options=options)
- func_num += 1
-
- if timestamps:
- pl.hlines(max_mem,
- pl.xlim()[0] + 0.001, pl.xlim()[1] - 0.001,
- colors="r", linestyles="--")
- pl.vlines(t[max_mem_ind], bottom, top,
- colors="r", linestyles="--")
- return mprofile
-
-
-def plot_action():
- def xlim_type(value):
- try:
- newvalue = [float(x) for x in value.split(',')]
- except:
- raise ArgumentError("'%s' option must contain two numbers separated with a comma" % value)
- if len(newvalue) != 2:
- raise ArgumentError("'%s' option must contain two numbers separated with a comma" % value)
- return newvalue
-
- desc = """Plots using matplotlib the data file `file.dat` generated
-using `mprof run`. If no .dat file is given, it will take the most recent
-such file in the current directory."""
- parser = ArgumentParser(usage="mprof plot [options] [file.dat]", description=desc)
- parser.add_argument('--version', action='version', version=mp.__version__)
- parser.add_argument("--title", "-t", dest="title", default=None,
- type=str, action="store",
- help="String shown as plot title")
- parser.add_argument("--no-function-ts", "-n", dest="no_timestamps", action="store_true",
- help="Do not display function timestamps on plot.")
- parser.add_argument("--output", "-o",
- help="Save plot to file instead of displaying it.")
- parser.add_argument("--window", "-w", dest="xlim", type=xlim_type,
- help="Plot a time-subset of the data. E.g. to plot between 0 and 20.5 seconds: --window 0,20.5")
- parser.add_argument("--backend",
- help="Specify the Matplotlib backend to use")
- parser.add_argument("profiles", nargs="*",
- help="profiles made by mprof run")
- args = parser.parse_args()
-
- try:
- if args.backend is not None:
- import matplotlib
- matplotlib.use(args.backend)
-
- import pylab as pl
- except ImportError as e:
- print("matplotlib is needed for plotting.")
- print(e)
- sys.exit(1)
-
- profiles = glob.glob("mprofile_??????????????.dat")
- profiles.sort()
-
- if len(args.profiles) == 0:
- if len(profiles) == 0:
- print("No input file found. \nThis program looks for "
- "mprofile_*.dat files, generated by the "
- "'mprof run' command.")
- sys.exit(-1)
- print("Using last profile data.")
- filenames = [profiles[-1]]
- else:
- filenames = []
- for prof in args.profiles:
- if osp.exists(prof):
- if not prof in filenames:
- filenames.append(prof)
- else:
- try:
- n = int(prof)
- if not profiles[n] in filenames:
- filenames.append(profiles[n])
- except ValueError:
- print("Input file not found: " + prof)
- if not len(filenames):
- print("No files found from given input.")
- sys.exit(-1)
-
- fig = pl.figure(figsize=(14, 6), dpi=90)
- ax = fig.add_axes([0.1, 0.1, 0.6, 0.75])
- if args.xlim is not None:
- pl.xlim(args.xlim[0], args.xlim[1])
-
- if len(filenames) > 1 or args.no_timestamps:
- timestamps = False
- else:
- timestamps = True
- for n, filename in enumerate(filenames):
- mprofile = plot_file(filename, index=n, timestamps=timestamps, options=args)
- pl.xlabel("time (in seconds)")
- pl.ylabel("memory used (in MiB)")
-
- if args.title is None and len(filenames) == 1:
- pl.title(mprofile['cmd_line'])
- else:
- if args.title is not None:
- pl.title(args.title)
-
- # place legend within the plot, make partially transparent in
- # case it obscures part of the lineplot
- leg = ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
- leg.get_frame().set_alpha(0.5)
- pl.grid()
- if args.output:
- pl.savefig(args.output)
- else:
- pl.show()
-
-
-if __name__ == "__main__":
- # Workaround for optparse limitation: insert -- before first negative
- # number found.
- negint = re.compile("-[0-9]+")
- for n, arg in enumerate(sys.argv):
- if negint.match(arg):
- sys.argv.insert(n, "--")
- break
- actions = {"rm": rm_action,
- "clean": clean_action,
- "list": list_action,
- "run": run_action,
- "plot": plot_action}
- actions[get_action()]()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/memory_profiler-0.52.0/mprof.bat new/memory_profiler-0.55.0/mprof.bat
--- old/memory_profiler-0.52.0/mprof.bat 2017-11-24 00:14:01.000000000 +0100
+++ new/memory_profiler-0.55.0/mprof.bat 1970-01-01 01:00:00.000000000 +0100
@@ -1,2 +0,0 @@
-@echo off
-python %~dpn0 %*
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/memory_profiler-0.52.0/mprof.py new/memory_profiler-0.55.0/mprof.py
--- old/memory_profiler-0.52.0/mprof.py 1970-01-01 01:00:00.000000000 +0100
+++ new/memory_profiler-0.55.0/mprof.py 2018-12-14 16:13:49.000000000 +0100
@@ -0,0 +1,595 @@
+import glob
+import os
+import os.path as osp
+import sys
+import re
+import copy
+import time
+import math
+import logging
+
+from collections import defaultdict
+from argparse import ArgumentParser, ArgumentError, REMAINDER, RawTextHelpFormatter
+
+import memory_profiler as mp
+
+ALL_ACTIONS = ("run", "rm", "clean", "list", "plot")
+help_msg = """
+Available commands:
+
+ run run a given command or python file
+ rm remove a given file generated by mprof
+ clean clean the current directory from files created by mprof
+ list display existing profiles, with indices
+ plot plot memory consumption generated by mprof run
+
+Type mprof <command> --help for usage help on a specific command.
+For example, mprof plot --help will list all plotting options.
+"""
+
+logger = logging.getLogger(__name__)
+logging.basicConfig()
+
+
+def print_usage():
+ print("Usage: %s <command> <options> <arguments>"
+ % osp.basename(sys.argv[0]))
+ print(help_msg)
+
+
+def get_action():
+ """Pop first argument, check it is a valid action."""
+ if len(sys.argv) <= 1:
+ print_usage()
+ sys.exit(1)
+ if not sys.argv[1] in ALL_ACTIONS:
+ print_usage()
+ sys.exit(1)
+
+ return sys.argv.pop(1)
+
+
+def get_profile_filenames(args):
+ """Return list of profile filenames.
+
+ Parameters
+ ==========
+ args (list)
+ list of filename or integer. An integer is the index of the
+ profile in the list of existing profiles. 0 is the oldest,
+ -1 in the more recent.
+ Non-existing files cause a ValueError exception to be thrown.
+
+ Returns
+ =======
+ filenames (list)
+ list of existing memory profile filenames. It is guaranteed
+ that an given file name will not appear twice in this list.
+ """
+ profiles = glob.glob("mprofile_??????????????.dat")
+ profiles.sort()
+
+ if args is "all":
+ filenames = copy.copy(profiles)
+ else:
+ filenames = []
+ for arg in args:
+ if arg == "--": # workaround
+ continue
+ try:
+ index = int(arg)
+ except ValueError:
+ index = None
+ if index is not None:
+ try:
+ filename = profiles[index]
+ except IndexError:
+ raise ValueError("Invalid index (non-existing file): %s" % arg)
+
+ if filename not in filenames:
+ filenames.append(filename)
+ else:
+ if osp.isfile(arg):
+ if arg not in filenames:
+ filenames.append(arg)
+ elif osp.isdir(arg):
+ raise ValueError("Path %s is a directory" % arg)
+ else:
+ raise ValueError("File %s not found" % arg)
+
+ # Add timestamp files, if any
+ for filename in reversed(filenames):
+ parts = osp.splitext(filename)
+ timestamp_file = parts[0] + "_ts" + parts[1]
+ if osp.isfile(timestamp_file) and timestamp_file not in filenames:
+ filenames.append(timestamp_file)
+
+ return filenames
+
+
+def list_action():
+ """Display existing profiles, with indices."""
+ parser = ArgumentParser(
+ usage='mprof list\nThis command takes no argument.')
+ parser.add_argument('--version', action='version', version=mp.__version__)
+ args = parser.parse_args()
+
+ filenames = get_profile_filenames("all")
+ for n, filename in enumerate(filenames):
+ ts = osp.splitext(filename)[0].split('_')[-1]
+ print("{index} {filename} {hour}:{min}:{sec} {day}/{month}/{year}"
+ .format(index=n, filename=filename,
+ year=ts[:4], month=ts[4:6], day=ts[6:8],
+ hour=ts[8:10], min=ts[10:12], sec=ts[12:14]))
+
+
+def rm_action():
+ """TODO: merge with clean_action (@pgervais)"""
+ parser = ArgumentParser(usage='mprof rm [options] numbers_or_filenames')
+ parser.add_argument('--version', action='version', version=mp.__version__)
+ parser.add_argument("--dry-run", dest="dry_run", default=False,
+ action="store_true",
+ help="""Show what will be done, without actually doing it.""")
+ parser.add_argument("numbers_or_filenames", nargs='*',
+ help="""numbers or filenames removed""")
+ args = parser.parse_args()
+
+ if len(args.numbers_or_filenames) == 0:
+ print("A profile to remove must be provided (number or filename)")
+ sys.exit(1)
+
+ filenames = get_profile_filenames(args.numbers_or_filenames)
+ if args.dry_run:
+ print("Files to be removed: ")
+ for filename in filenames:
+ print(filename)
+ else:
+ for filename in filenames:
+ os.remove(filename)
+
+
+def clean_action():
+ """Remove every profile file in current directory."""
+ parser = ArgumentParser(
+ usage='mprof clean\nThis command takes no argument.')
+ parser.add_argument('--version', action='version', version=mp.__version__)
+ parser.add_argument("--dry-run", dest="dry_run", default=False,
+ action="store_true",
+ help="""Show what will be done, without actually doing it.""")
+ args = parser.parse_args()
+
+ filenames = get_profile_filenames("all")
+ if args.dry_run:
+ print("Files to be removed: ")
+ for filename in filenames:
+ print(filename)
+ else:
+ for filename in filenames:
+ os.remove(filename)
+
+
+def get_cmd_line(args):
+ """Given a set or arguments, compute command-line."""
+ blanks = set(' \t')
+ args = [s if blanks.isdisjoint(s) else "'" + s + "'" for s in args]
+ return ' '.join(args)
+
+
+def run_action():
+ import time, subprocess
+ parser = ArgumentParser(usage="mprof run [options] program", formatter_class=RawTextHelpFormatter)
+ parser.add_argument('--version', action='version', version=mp.__version__)
+ parser.add_argument("--python", dest="python", action="store_true",
+ help="""Activates extra features when the profiling executable is a Python program (currently: function timestamping.)""")
+ parser.add_argument("--nopython", dest="nopython", action="store_true",
+ help="""Disables extra features when the profiled executable is a Python program (currently: function timestamping.)""")
+ parser.add_argument("--interval", "-T", dest="interval", default="0.1", type=float, action="store",
+ help="Sampling period (in seconds), defaults to 0.1")
+ parser.add_argument("--include-children", "-C", dest="include_children", action="store_true",
+ help="""Monitors forked processes as well (sum up all process memory)""")
+ parser.add_argument("--multiprocess", "-M", dest="multiprocess", action="store_true",
+ help="""Monitors forked processes creating individual plots for each child (disables --python features)""")
+ parser.add_argument("--exit-code", "-E", dest="exit_code", action="store_true", help="""Propagate the exit code""")
+ parser.add_argument("--output", "-o", dest="filename",
+ default="mprofile_%s.dat" % time.strftime("%Y%m%d%H%M%S", time.localtime()),
+ help="""File to store results in, defaults to 'mprofile_<YYYYMMDDhhmmss>.dat' in the current directory,
+(where <YYYYMMDDhhmmss> is the date-time of the program start).
+This file contains the process memory consumption, in Mb (one value per line).""")
+ parser.add_argument("program", nargs=REMAINDER,
+ help='Option 1: "<EXECUTABLE> <ARG1> <ARG2>..." - profile executable\n'
+ 'Option 2: " <ARG1> <ARG2>..." - profile python script\n'
+ 'Option 3: (--python flag present) " <ARG1> <ARG2>..." - profile python script with specified interpreter\n'
+ 'Option 4: (--python flag present) " <ARG1> <ARG2>..." - profile python module\n'
+ )
+ args = parser.parse_args()
+
+ if len(args.program) == 0:
+ print("A program to run must be provided. Use -h for help")
+ sys.exit(1)
+
+ print("{1}: Sampling memory every {0}s".format(
+ args.interval, osp.basename(sys.argv[0])))
+
+ mprofile_output = args.filename
+
+ # .. TODO: more than one script as argument ? ..
+ program = args.program
+ if program[0].endswith('.py') and not args.nopython:
+ if args.multiprocess:
+ # in multiprocessing mode you want to spawn a separate
+ # python process
+ if not program[0].startswith("python"):
+ program.insert(0, sys.executable)
+ args.python = False
+ else:
+ args.python = True
+ if args.python:
+ print("running as a Python program...")
+ if not program[0].startswith("python"):
+ program.insert(0, sys.executable)
+ cmd_line = get_cmd_line(program)
+ program[1:1] = ("-m", "memory_profiler", "--timestamp",
+ "-o", mprofile_output)
+ p = subprocess.Popen(program)
+ else:
+ cmd_line = get_cmd_line(program)
+ p = subprocess.Popen(program)
+
+ with open(mprofile_output, "a") as f:
+ f.write("CMDLINE {0}\n".format(cmd_line))
+ mp.memory_usage(proc=p, interval=args.interval, timestamps=True,
+ include_children=args.include_children,
+ multiprocess=args.multiprocess, stream=f)
+
+ if args.exit_code:
+ if p.returncode != 0:
+ logger.error('Program resulted with a non-zero exit code: %s', p.returncode)
+ sys.exit(p.returncode)
+
+
+def add_brackets(xloc, yloc, xshift=0, color="r", label=None, options=None):
+ """Add two brackets on the memory line plot.
+
+ This function uses the current figure.
+
+ Parameters
+ ==========
+ xloc: tuple with 2 values
+ brackets location (on horizontal axis).
+ yloc: tuple with 2 values
+ brackets location (on vertical axis)
+ xshift: float
+ value to subtract to xloc.
+ """
+ try:
+ import pylab as pl
+ except ImportError as e:
+ print("matplotlib is needed for plotting.")
+ print(e)
+ sys.exit(1)
+ height_ratio = 20.
+ vsize = (pl.ylim()[1] - pl.ylim()[0]) / height_ratio
+ hsize = (pl.xlim()[1] - pl.xlim()[0]) / (3. * height_ratio)
+
+ bracket_x = pl.asarray([hsize, 0, 0, hsize])
+ bracket_y = pl.asarray([vsize, vsize, -vsize, -vsize])
+
+ # Matplotlib workaround: labels starting with _ aren't displayed
+ if label[0] == '_':
+ label = ' ' + label
+ if options.xlim is None or options.xlim[0] <= (xloc[0] - xshift) <= options.xlim[1]:
+ pl.plot(bracket_x + xloc[0] - xshift, bracket_y + yloc[0],
+ "-" + color, linewidth=2, label=label)
+ if options.xlim is None or options.xlim[0] <= (xloc[1] - xshift) <= options.xlim[1]:
+ pl.plot(-bracket_x + xloc[1] - xshift, bracket_y + yloc[1],
+ "-" + color, linewidth=2)
+
+ # TODO: use matplotlib.patches.Polygon to draw a colored background for
+ # each function.
+
+ # with maplotlib 1.2, use matplotlib.path.Path to create proper markers
+ # see http://matplotlib.org/examples/pylab_examples/marker_path.html
+ # This works with matplotlib 0.99.1
+ ## pl.plot(xloc[0], yloc[0], "<"+color, markersize=7, label=label)
+ ## pl.plot(xloc[1], yloc[1], ">"+color, markersize=7)
+
+
+def read_mprofile_file(filename):
+ """Read an mprofile file and return its content.
+
+ Returns
+ =======
+ content: dict
+ Keys:
+
+ - "mem_usage": (list) memory usage values, in MiB
+ - "timestamp": (list) time instant for each memory usage value, in
+ second
+ - "func_timestamp": (dict) for each function, timestamps and memory
+ usage upon entering and exiting.
+ - 'cmd_line': (str) command-line ran for this profile.
+ """
+ func_ts = {}
+ mem_usage = []
+ timestamp = []
+ children = defaultdict(list)
+ cmd_line = None
+ f = open(filename, "r")
+ for l in f:
+ if l == '\n':
+ raise ValueError('Sampling time was too short')
+ field, value = l.split(' ', 1)
+ if field == "MEM":
+ # mem, timestamp
+ values = value.split(' ')
+ mem_usage.append(float(values[0]))
+ timestamp.append(float(values[1]))
+
+ elif field == "FUNC":
+ values = value.split(' ')
+ f_name, mem_start, start, mem_end, end = values[:5]
+ ts = func_ts.get(f_name, [])
+ ts.append([float(start), float(end),
+ float(mem_start), float(mem_end)])
+ func_ts[f_name] = ts
+
+ elif field == "CHLD":
+ values = value.split(' ')
+ chldnum = values[0]
+ children[chldnum].append(
+ (float(values[1]), float(values[2]))
+ )
+
+ elif field == "CMDLINE":
+ cmd_line = value
+ else:
+ pass
+ f.close()
+
+ return {"mem_usage": mem_usage, "timestamp": timestamp,
+ "func_timestamp": func_ts, 'filename': filename,
+ 'cmd_line': cmd_line, 'children': children}
+
+
+def plot_file(filename, index=0, timestamps=True, children=True, options=None):
+ try:
+ import pylab as pl
+ except ImportError as e:
+ print("matplotlib is needed for plotting.")
+ print(e)
+ sys.exit(1)
+ import numpy as np # pylab requires numpy anyway
+ mprofile = read_mprofile_file(filename)
+
+ if len(mprofile['timestamp']) == 0:
+ print('** No memory usage values have been found in the profile '
+ 'file.**\nFile path: {0}\n'
+ 'File may be empty or invalid.\n'
+ 'It can be deleted with "mprof rm {0}"'.format(
+ mprofile['filename']))
+ sys.exit(0)
+
+ # Merge function timestamps and memory usage together
+ ts = mprofile['func_timestamp']
+ t = mprofile['timestamp']
+ mem = mprofile['mem_usage']
+ chld = mprofile['children']
+
+ if len(ts) > 0:
+ for values in ts.values():
+ for v in values:
+ t.extend(v[:2])
+ mem.extend(v[2:4])
+
+ mem = np.asarray(mem)
+ t = np.asarray(t)
+ ind = t.argsort()
+ mem = mem[ind]
+ t = t[ind]
+
+ # Plot curves
+ global_start = float(t[0])
+ t = t - global_start
+
+ max_mem = mem.max()
+ max_mem_ind = mem.argmax()
+
+ all_colors = ("c", "y", "g", "r", "b")
+ mem_line_colors = ("k", "b", "r", "g", "c", "y", "m")
+ mem_line_label = time.strftime("%d / %m / %Y - start at %H:%M:%S",
+ time.localtime(global_start)) \
+ + ".{0:03d}".format(int(round(math.modf(global_start)[0] * 1000)))
+
+ pl.plot(t, mem, "+-" + mem_line_colors[index % len(mem_line_colors)],
+ label=mem_line_label)
+
+ bottom, top = pl.ylim()
+ bottom += 0.001
+ top -= 0.001
+
+ # plot children, if any
+ if len(chld) > 0 and children:
+ cmpoint = (0,0) # maximal child memory
+
+ for idx, (proc, data) in enumerate(chld.items()):
+ # Create the numpy arrays from the series data
+ cts = np.asarray([item[1] for item in data]) - global_start
+ cmem = np.asarray([item[0] for item in data])
+
+ # Plot the line to the figure
+ pl.plot(cts, cmem, "+-" + mem_line_colors[(idx+1) % len(mem_line_colors)],
+ label="child {}".format(proc))
+
+ # Detect the maximal child memory point
+ cmax_mem = cmem.max()
+ if cmax_mem > cmpoint[1]:
+ cmpoint = (cts[cmem.argmax()], cmax_mem)
+
+ # Add the marker lines for the maximal child memory usage
+ pl.vlines(cmpoint[0], pl.ylim()[0]+0.001, pl.ylim()[1] - 0.001, 'r', '--')
+ pl.hlines(cmpoint[1], pl.xlim()[0]+0.001, pl.xlim()[1] - 0.001, 'r', '--')
+
+ # plot timestamps, if any
+ if len(ts) > 0 and timestamps:
+ func_num = 0
+ f_labels = function_labels(ts.keys())
+ for f, exec_ts in ts.items():
+ for execution in exec_ts:
+ add_brackets(execution[:2], execution[2:], xshift=global_start,
+ color=all_colors[func_num % len(all_colors)],
+ label=f_labels[f]
+ + " %.3fs" % (execution[1] - execution[0]), options=options)
+ func_num += 1
+
+ if timestamps:
+ pl.hlines(max_mem,
+ pl.xlim()[0] + 0.001, pl.xlim()[1] - 0.001,
+ colors="r", linestyles="--")
+ pl.vlines(t[max_mem_ind], bottom, top,
+ colors="r", linestyles="--")
+ return mprofile
+
+
+def function_labels(dotted_function_names):
+ state = {}
+
+ def set_state_for(function_names, level):
+ for fn in function_names:
+ label = ".".join(fn.split(".")[-level:])
+ label_state = state.setdefault(label, {"functions": [],
+ "level": level})
+ label_state["functions"].append(fn)
+
+ set_state_for(dotted_function_names, 1)
+
+ while True:
+ ambiguous_labels = [label for label in state if len(state[label]["functions"]) > 1]
+ for ambiguous_label in ambiguous_labels:
+ function_names = state[ambiguous_label]["functions"]
+ new_level = state[ambiguous_label]["level"] + 1
+ del state[ambiguous_label]
+ set_state_for(function_names, new_level)
+ if len(ambiguous_labels) == 0:
+ break
+
+ fn_to_label = { label_state["functions"][0] : label for label, label_state in state.items() }
+
+ return fn_to_label
+
+
+def plot_action():
+ def xlim_type(value):
+ try:
+ newvalue = [float(x) for x in value.split(',')]
+ except:
+ raise ArgumentError("'%s' option must contain two numbers separated with a comma" % value)
+ if len(newvalue) != 2:
+ raise ArgumentError("'%s' option must contain two numbers separated with a comma" % value)
+ return newvalue
+
+ desc = """Plots using matplotlib the data file `file.dat` generated
+using `mprof run`. If no .dat file is given, it will take the most recent
+such file in the current directory."""
+ parser = ArgumentParser(usage="mprof plot [options] [file.dat]", description=desc)
+ parser.add_argument('--version', action='version', version=mp.__version__)
+ parser.add_argument("--title", "-t", dest="title", default=None,
+ type=str, action="store",
+ help="String shown as plot title")
+ parser.add_argument("--no-function-ts", "-n", dest="no_timestamps", action="store_true",
+ help="Do not display function timestamps on plot.")
+ parser.add_argument("--output", "-o",
+ help="Save plot to file instead of displaying it.")
+ parser.add_argument("--window", "-w", dest="xlim", type=xlim_type,
+ help="Plot a time-subset of the data. E.g. to plot between 0 and 20.5 seconds: --window 0,20.5")
+ parser.add_argument("--backend",
+ help="Specify the Matplotlib backend to use")
+ parser.add_argument("profiles", nargs="*",
+ help="profiles made by mprof run")
+ args = parser.parse_args()
+
+ try:
+ if args.backend is not None:
+ import matplotlib
+ matplotlib.use(args.backend)
+
+ import pylab as pl
+ except ImportError as e:
+ print("matplotlib is needed for plotting.")
+ print(e)
+ sys.exit(1)
+
+ profiles = glob.glob("mprofile_??????????????.dat")
+ profiles.sort()
+
+ if len(args.profiles) == 0:
+ if len(profiles) == 0:
+ print("No input file found. \nThis program looks for "
+ "mprofile_*.dat files, generated by the "
+ "'mprof run' command.")
+ sys.exit(-1)
+ print("Using last profile data.")
+ filenames = [profiles[-1]]
+ else:
+ filenames = []
+ for prof in args.profiles:
+ if osp.exists(prof):
+ if not prof in filenames:
+ filenames.append(prof)
+ else:
+ try:
+ n = int(prof)
+ if not profiles[n] in filenames:
+ filenames.append(profiles[n])
+ except ValueError:
+ print("Input file not found: " + prof)
+ if not len(filenames):
+ print("No files found from given input.")
+ sys.exit(-1)
+
+ fig = pl.figure(figsize=(14, 6), dpi=90)
+ ax = fig.add_axes([0.1, 0.1, 0.6, 0.75])
+ if args.xlim is not None:
+ pl.xlim(args.xlim[0], args.xlim[1])
+
+ if len(filenames) > 1 or args.no_timestamps:
+ timestamps = False
+ else:
+ timestamps = True
+ for n, filename in enumerate(filenames):
+ mprofile = plot_file(filename, index=n, timestamps=timestamps, options=args)
+ pl.xlabel("time (in seconds)")
+ pl.ylabel("memory used (in MiB)")
+
+ if args.title is None and len(filenames) == 1:
+ pl.title(mprofile['cmd_line'])
+ else:
+ if args.title is not None:
+ pl.title(args.title)
+
+ # place legend within the plot, make partially transparent in
+ # case it obscures part of the lineplot
+ leg = ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
+ leg.get_frame().set_alpha(0.5)
+ pl.grid()
+ if args.output:
+ pl.savefig(args.output)
+ else:
+ pl.show()
+
+def main():
+ # Workaround for optparse limitation: insert -- before first negative
+ # number found.
+ negint = re.compile("-[0-9]+")
+ for n, arg in enumerate(sys.argv):
+ if negint.match(arg):
+ sys.argv.insert(n, "--")
+ break
+ actions = {"rm": rm_action,
+ "clean": clean_action,
+ "list": list_action,
+ "run": run_action,
+ "plot": plot_action}
+ actions[get_action()]()
+
+if __name__ == "__main__":
+ main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/memory_profiler-0.52.0/setup.py new/memory_profiler-0.55.0/setup.py
--- old/memory_profiler-0.52.0/setup.py 2017-11-24 00:14:01.000000000 +0100
+++ new/memory_profiler-0.55.0/setup.py 2018-12-13 16:49:08.000000000 +0100
@@ -1,7 +1,6 @@
import os
import io
import re
-import sys
from setuptools import setup
@@ -40,10 +39,6 @@
"""
-scripts = ['mprof']
-if sys.platform == "win32":
- scripts.append('mprof.bat')
-
setup(
name='memory_profiler',
description='A module for monitoring memory usage of a python program',
@@ -52,8 +47,10 @@
author='Fabian Pedregosa',
author_email='f@bianp.net',
url='http://pypi.python.org/pypi/memory_profiler',
- py_modules=['memory_profiler'],
- scripts=scripts,
+ py_modules=['memory_profiler', 'mprof'],
+ entry_points={
+ 'console_scripts' : ['mprof = mprof:main'],
+ },
install_requires=['psutil'],
classifiers=[_f for _f in CLASSIFIERS.split('\n') if _f],
license='BSD'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/memory_profiler-0.52.0/test/test_exit_code.py new/memory_profiler-0.55.0/test/test_exit_code.py
--- old/memory_profiler-0.52.0/test/test_exit_code.py 1970-01-01 01:00:00.000000000 +0100
+++ new/memory_profiler-0.55.0/test/test_exit_code.py 2018-12-14 16:13:49.000000000 +0100
@@ -0,0 +1,42 @@
+import unittest
+import sys
+import tempfile
+
+
+class TestExitCode(unittest.TestCase):
+
+ def setUp(self):
+ # to be able to import mprof
+ sys.path.append('.')
+ from mprof import run_action
+ self.run_action = run_action
+
+ def test_exit_code_success(self):
+ s = "1+1"
+ tmpfile = tempfile.NamedTemporaryFile('w', suffix='.py')
+ with tmpfile as ofile:
+ ofile.write(s)
+ ofile.flush()
+ sys.argv = ['<ignored>', '--exit-code', tmpfile.name]
+ self.assertRaisesRegexp(SystemExit, '0', self.run_action)
+
+ def test_exit_code_fail(self):
+ s = "raise RuntimeError('I am not working nicely')"
+ tmpfile = tempfile.NamedTemporaryFile('w', suffix='.py')
+ with tmpfile as ofile:
+ ofile.write(s)
+ ofile.flush()
+ sys.argv = ['<ignored>', '--exit-code', tmpfile.name]
+ self.assertRaisesRegexp(SystemExit, '1', self.run_action)
+
+ def test_no_exit_code_success(self):
+ s = "raise RuntimeError('I am not working nicely')"
+ tmpfile = tempfile.NamedTemporaryFile('w', suffix='.py')
+ with tmpfile as ofile:
+ ofile.write(s)
+ ofile.flush()
+ sys.argv = ['<ignored>', tmpfile.name]
+ self.run_action()
+
+if __name__ == '__main__':
+ unittest.main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/memory_profiler-0.52.0/test/test_mprof.py new/memory_profiler-0.55.0/test/test_mprof.py
--- old/memory_profiler-0.52.0/test/test_mprof.py 1970-01-01 01:00:00.000000000 +0100
+++ new/memory_profiler-0.55.0/test/test_mprof.py 2018-12-13 16:49:08.000000000 +0100
@@ -0,0 +1,20 @@
+import unittest
+
+import mprof
+
+class Test_function_labels(unittest.TestCase):
+ def test(self):
+ expected = {
+ "x.z": "z",
+ "x.y": "y",
+ "x.b": "x.b",
+ "f.a.b": "f.a.b",
+ "g.a.b": "g.a.b",
+ "g.a.c": "a.c",
+ "b.c": "b.c",
+ }
+ result = mprof.function_labels(expected.keys())
+ self.assertEqual(expected,result)
+
+if __name__ == "__main__":
+ unittest.main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/memory_profiler-0.52.0/test/test_mprofile.py new/memory_profiler-0.55.0/test/test_mprofile.py
--- old/memory_profiler-0.52.0/test/test_mprofile.py 2017-10-25 18:00:09.000000000 +0200
+++ new/memory_profiler-0.55.0/test/test_mprofile.py 2018-12-13 16:49:08.000000000 +0100
@@ -4,6 +4,7 @@
@profile
def test1(l):
+ """test1 docstring"""
a = [1] * l
time.sleep(1)
return a
@@ -14,8 +15,22 @@
time.sleep(1)
return b
+def test3(l):
+ """test3 docstring"""
+ return l
+
if __name__ == "__main__":
l = 100000
test1(l)
test2(2 * l)
+ # make sure that the function name and docstring are set
+ # by functools.wraps
+ # memory_profile.py def profile func is not None case
+ assert (test1.__name__ == 'test1'), 'function name is incorrect'
+ assert (test1.__doc__ == 'test1 docstring'), 'function docstring is incorrect'
+ # memory_profile.py def profile func is None case
+ profile_maker = profile()
+ profiled_test3 = profile_maker(test3)
+ assert (profiled_test3.__name__ == 'test3'), 'function name is incorrect'
+ assert (profiled_test3.__doc__ == 'test3 docstring'), 'function docstring is incorrect'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/memory_profiler-0.52.0/test/test_tracemalloc.py new/memory_profiler-0.55.0/test/test_tracemalloc.py
--- old/memory_profiler-0.52.0/test/test_tracemalloc.py 2017-10-25 18:00:09.000000000 +0200
+++ new/memory_profiler-0.55.0/test/test_tracemalloc.py 2018-12-13 16:49:08.000000000 +0100
@@ -18,8 +18,6 @@
def test_memory_profiler(test_input, expected):
mem_prof(test_input)
inc, dec = parse_mem_prof()
- assert abs(inc - dec) <= EPSILON, \
- 'inc = {}, dec = {}, err = {}'.format(inc, dec, abs(inc - dec))
assert abs(inc - expected) <= EPSILON, \
'inc = {}, size = {}, err = {}'.format(
inc, expected, abs(inc - expected)