Skip to content

Loading…

Issue warning if too many figures are open #1919

Merged
merged 2 commits into from

5 participants

@astrofrog

Users often ask me why they are running out of memory when plotting in loops, and this is because they are not calling plt.close when using pyplot. I wonder whether it might make sense to emit a warning above say 20 plots saying that the number of open figures has now exceeded 20, and that the user may want to close some to avoid running out of memory. I just made up the value of 20, but I was trying to think above what value it's unlikely the user deliberately has this many figures open. This could be an RC parameter so that users who do want to open 1000 figures (and have the RAM for it) can.

@pelson
Matplotlib Developers member

I'm :+1: for a solution which makes it obvious to noobs what is wrong, but allows the more experienced folk to get around.

Nice idea @astrofrog.

@mdboom
Matplotlib Developers member

Yes -- it's an interesting idea. I'm going to tentatively milestone this as 1.3.x (not as a blocker), because I think it would be nice to have and fairly simple to implement. It also should get lots of release candidate testing on the other hand, since it feels like something that someone might have a legit problem with, though I'm not creative enough to guess exactly what.

@mdboom mdboom was assigned
@efiring efiring commented on an outdated diff
lib/matplotlib/rcsetup.py
@@ -666,6 +666,7 @@ def __call__(self, s):
'figure.edgecolor': ['w', validate_color], # edgecolor; white
'figure.frameon': [True, validate_bool],
'figure.autolayout': [False, validate_bool],
+ 'figure.max_num_figures': [20, validate_int],
@efiring Matplotlib Developers member
efiring added a note

Maybe call this "figure.max_open_warning" to show explicitly that it is a warning threshold, not a hard limit.

@pelson Matplotlib Developers member
pelson added a note

Might be worth adding a validate_int_gt_0 validator - then you don't need to check it is positive in pyplot...

@pelson Matplotlib Developers member
pelson added a note

Scrap that. I remember now that a negative number is important... :smile:

@mdboom Matplotlib Developers member
mdboom added a note

@efiring: Yes, good suggestion on including warning in the name.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@pelson pelson commented on an outdated diff
lib/matplotlib/pyplot.py
@@ -344,6 +344,17 @@ def figure(num=None, # autoincrement if None, else integer from 1-N
figManager = _pylab_helpers.Gcf.get_fig_manager(num)
if figManager is None:
+ max_num_figures = rcParams['figure.max_num_figures']
+
+ if (max_num_figures >= 1 and
+ len(allnums) >= max_num_figures):
+ warnings.warn(
+ "More than %d figures have been opened. Figures created "
+ "through the pyplot interface are retained until explicitly "
+ "closed. (To control this warning, see the rcParam "
+ "'figure.max_num_figures'." %
@pelson Matplotlib Developers member
pelson added a note

Bracket doesn't get closed in message.

Should the message indicate (in better words) that this will end up taking a lot of memory and slow things down?

@pelson Matplotlib Developers member
pelson added a note

Should the message indicate (in better words) that this will end up taking a lot of memory and slow things down?

Yes I think so. Currently it is a very informative message, but it doesn't tell you what the actual problem is.

@mdboom Matplotlib Developers member
mdboom added a note

How about:

More than %d figures have been opened. Figures created
through the pyplot interface (`matplotlib.pyplot.figure()`) are
retained until explicitly closed and may consume too much
memory.  (To control this warning, see the rcParam 
`figure.max_num_figures`).

It's wordy. Any other suggestions?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@mdboom mdboom merged commit 3d17d95 into matplotlib:master
@dragoljub

Good Add! I just ran into this and having the warning made it clear how to avoid the memory explosion! :+1:

@mdboom mdboom deleted the mdboom:max-num-figures branch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
Showing with 40 additions and 2 deletions.
  1. +13 −1 doc/users/whats_new.rst
  2. +13 −0 lib/matplotlib/pyplot.py
  3. +1 −0 lib/matplotlib/rcsetup.py
  4. +10 −1 lib/matplotlib/tests/test_figure.py
  5. +3 −0 matplotlibrc.template
View
14 doc/users/whats_new.rst
@@ -45,6 +45,18 @@ rcParam has been set, and will not retroactively affect already
existing text objects. This brings their behavior in line with most
other rcParams.
+Catch opening too many figures using pyplot
+-------------------------------------------
+Figures created through `pyplot.figure` are retained until they are
+explicitly closed. It is therefore common for new users of matplotlib
+to run out of memory when creating a large series of figures in a
+loop without closing them.
+
+matplotlib will now display a `RuntimeWarning` when too many figures
+have been opened at once. By default, this is displayed for 20 or
+more figures, but the exact number may be controlled using the
+``figure.max_num_figures`` rcParam.
+
``axes.xmargin`` and ``axes.ymargin`` added to rcParams
-------------------------------------------------------
``rcParam`` values (``axes.xmargin`` and ``axes.ymargin``) were added
@@ -111,7 +123,7 @@ conversion (`mpl.rc('svg', fonttype='none')`).
More robust boxplots
--------------------
Paul Hobson provided a fix to the :func:`~matplotlib.pyplot.boxplot`
-method that prevent whiskers from being drawn inside the box for
+method that prevent whiskers from being drawn inside the box for
oddly distributed data sets.
Triangular grid interpolation
View
13 lib/matplotlib/pyplot.py
@@ -394,6 +394,19 @@ def figure(num=None, # autoincrement if None, else integer from 1-N
figManager = _pylab_helpers.Gcf.get_fig_manager(num)
if figManager is None:
+ max_open_warning = rcParams['figure.max_open_warning']
+
+ if (max_open_warning >= 1 and
+ len(allnums) >= max_open_warning):
+ warnings.warn(
+ "More than %d figures have been opened. Figures "
+ "created through the pyplot interface "
+ "(`matplotlib.pyplot.figure`) are retained until "
+ "explicitly closed and may consume too much memory. "
+ "(To control this warning, see the rcParam "
+ "`figure.max_num_figures`)." %
+ max_open_warning, RuntimeWarning)
+
if get_backend().lower() == 'ps':
dpi = 72
View
1 lib/matplotlib/rcsetup.py
@@ -680,6 +680,7 @@ def __call__(self, s):
'figure.edgecolor': ['w', validate_color], # edgecolor; white
'figure.frameon': [True, validate_bool],
'figure.autolayout': [False, validate_bool],
+ 'figure.max_open_warning': [20, validate_int],
'figure.subplot.left': [0.125, ValidateInterval(0, 1, closedmin=True,
closedmax=True)],
View
11 lib/matplotlib/tests/test_figure.py
@@ -1,4 +1,4 @@
-from nose.tools import assert_equal, assert_true
+from nose.tools import assert_equal, assert_true, assert_raises
from matplotlib.testing.decorators import image_comparison, cleanup
import matplotlib.pyplot as plt
@@ -92,6 +92,15 @@ def test_alpha():
alpha=0.6,
facecolor='red'))
+@cleanup
+def test_too_many_figures():
+ import warnings
+
+ with warnings.catch_warnings(record=True) as w:
+ for i in range(22):
+ fig = plt.figure()
+ assert len(w) == 1
+
if __name__ == "__main__":
import nose
View
3 matplotlibrc.template
@@ -318,6 +318,9 @@ text.hinting_factor : 8 # Specifies the amount of softness for hinting in the
#figure.edgecolor : white # figure edgecolor
#figure.autolayout : False # When True, automatically adjust subplot
# parameters to make the plot fit the figure
+#figure.max_open_warning : 20 # The maximum number of figures to open through
+ # the pyplot interface before emitting a warning.
+ # If less than one this feature is disabled.
# The figure subplot parameters. All dimensions are a fraction of the
# figure width or height
Something went wrong with that request. Please try again.