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)