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 <tchva...@suse.com>
+
+- 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: "<PYTHON_SCRIPT> <ARG1> <ARG2>..." - 
profile python script\n'
-                             'Option 3: (--python flag present) 
"<PYTHON_EXECUTABLE> <PYTHON_SCRIPT> <ARG1> <ARG2>..." - profile python script 
with specified interpreter\n'
-                             'Option 4: (--python flag present) 
"<PYTHON_MODULE> <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: "<PYTHON_SCRIPT> <ARG1> <ARG2>..." - 
profile python script\n'
+                             'Option 3: (--python flag present) 
"<PYTHON_EXECUTABLE> <PYTHON_SCRIPT> <ARG1> <ARG2>..." - profile python script 
with specified interpreter\n'
+                             'Option 4: (--python flag present) 
"<PYTHON_MODULE> <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)


Reply via email to