mpl-interactions icon indicating copy to clipboard operation
mpl-interactions copied to clipboard

Name collision(s) in pyplot.interactive_plot

Open origen-jak opened this issue 1 year ago • 0 comments

Bug report

My first time using this library and just by chance I managed to run into a weird edge case in the first few minutes:

Bug summary

If you use a variable name in your target function, that is also declared in the pyplot.interactive_plot function, it'll throw a confusing TypeError due to the name collision. For me it was declaring a variable named xlim.

Code for reproduction

Using the Basic example from the doc's, but changing the declared variable name in the user defined function f to trigger a name collision:

%matplotlib ipympl
import matplotlib.pyplot as plt
import numpy as np

import mpl_interactions.ipyplot as iplt

# Name collision defined here: xlim
def f(x, xlim, beta):
    return np.sin(x * xlim) * x**beta

x = np.linspace(0, 2 * np.pi, 1000)
xlim = np.linspace(5, 10)
beta = np.linspace(0.25, 1)

fig, ax = plt.subplots()
controls = iplt.plot(x, f, xlim=xlim, beta=beta)  # Pow!

Actual outcome

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[1], line 15
     12 beta = np.linspace(0.25, 1)
     14 fig, ax = plt.subplots()
---> 15 controls = iplt.plot(x, f, xlim=xlim, beta=beta)

File ...\mpl_interactions\pyplot.py:227, in interactive_plot(parametric, ax, slider_formats, xlim, ylim, force_ipywidgets, play_buttons, controls, display_controls, *args, **kwargs)
    224 controls._register_function(update, fig, params.keys())
    226 if x_and_y:
--> 227     x_, y_ = eval_xy(x, y, params)
    228     if fmt:
    229         lines = ax.plot(x_, y_, fmt, **plot_kwargs)

File ...\mpl_interactions\helpers.py:173, in eval_xy(x_, y_, params, cache)
    171             cache[y_] = y
    172     else:
--> 173         y = y_(x, **params)
    174 else:
    175     y = y_

TypeError: f() missing 1 required positional argument: 'xlim'

The exception thrown is confusing as it classifies xlim as a missing positional argument, when the user supplied it as a keyword argument.

Expected outcome

The code should work without throwing an exception, exactly as in the documentation.

Some ideas for a fix:

  • prefix all the variables declared in interactive_plot with a _.: e.g. _xlim
  • Add a validation step to check for name collisions and throw a helpful exception if found. e.g. '_xlim is a reserved name, please change your argument name in function f' or similar.

Thanks for your work, it's a helpful package.

Version Info

  • Operating system: Windows 11
  • Matplotlib version: 3.8.0
  • Matplotlib backend (print(matplotlib.get_backend())): ipympl.backend_nbagg
  • Python version: 3.11.5

origen-jak avatar Sep 20 '24 08:09 origen-jak