1import difflib 2import subprocess 3import sys 4from pathlib import Path 5 6import pytest 7 8import matplotlib as mpl 9from matplotlib import pyplot as plt 10from matplotlib.cbook import MatplotlibDeprecationWarning 11 12 13def test_pyplot_up_to_date(tmpdir): 14 gen_script = Path(mpl.__file__).parents[2] / "tools/boilerplate.py" 15 if not gen_script.exists(): 16 pytest.skip("boilerplate.py not found") 17 orig_contents = Path(plt.__file__).read_text() 18 plt_file = tmpdir.join('pyplot.py') 19 plt_file.write_text(orig_contents, 'utf-8') 20 21 subprocess.run([sys.executable, str(gen_script), str(plt_file)], 22 check=True) 23 new_contents = plt_file.read_text('utf-8') 24 25 if orig_contents != new_contents: 26 diff_msg = '\n'.join( 27 difflib.unified_diff( 28 orig_contents.split('\n'), new_contents.split('\n'), 29 fromfile='found pyplot.py', 30 tofile='expected pyplot.py', 31 n=0, lineterm='')) 32 pytest.fail( 33 "pyplot.py is not up-to-date. Please run " 34 "'python tools/boilerplate.py' to update pyplot.py. " 35 "This needs to be done from an environment where your " 36 "current working copy is installed (e.g. 'pip install -e'd). " 37 "Here is a diff of unexpected differences:\n%s" % diff_msg 38 ) 39 40 41def test_copy_docstring_and_deprecators(recwarn): 42 @mpl._api.rename_parameter("(version)", "old", "new") 43 @mpl._api.make_keyword_only("(version)", "kwo") 44 def func(new, kwo=None): 45 pass 46 47 @plt._copy_docstring_and_deprecators(func) 48 def wrapper_func(new, kwo=None): 49 pass 50 51 wrapper_func(None) 52 wrapper_func(new=None) 53 wrapper_func(None, kwo=None) 54 wrapper_func(new=None, kwo=None) 55 assert not recwarn 56 with pytest.warns(MatplotlibDeprecationWarning): 57 wrapper_func(old=None) 58 with pytest.warns(MatplotlibDeprecationWarning): 59 wrapper_func(None, None) 60 61 62def test_pyplot_box(): 63 fig, ax = plt.subplots() 64 plt.box(False) 65 assert not ax.get_frame_on() 66 plt.box(True) 67 assert ax.get_frame_on() 68 plt.box() 69 assert not ax.get_frame_on() 70 plt.box() 71 assert ax.get_frame_on() 72 73 74def test_stackplot_smoke(): 75 # Small smoke test for stackplot (see #12405) 76 plt.stackplot([1, 2, 3], [1, 2, 3]) 77 78 79def test_nrows_error(): 80 with pytest.raises(TypeError): 81 plt.subplot(nrows=1) 82 with pytest.raises(TypeError): 83 plt.subplot(ncols=1) 84 85 86def test_ioff(): 87 plt.ion() 88 assert mpl.is_interactive() 89 with plt.ioff(): 90 assert not mpl.is_interactive() 91 assert mpl.is_interactive() 92 93 plt.ioff() 94 assert not mpl.is_interactive() 95 with plt.ioff(): 96 assert not mpl.is_interactive() 97 assert not mpl.is_interactive() 98 99 100def test_ion(): 101 plt.ioff() 102 assert not mpl.is_interactive() 103 with plt.ion(): 104 assert mpl.is_interactive() 105 assert not mpl.is_interactive() 106 107 plt.ion() 108 assert mpl.is_interactive() 109 with plt.ion(): 110 assert mpl.is_interactive() 111 assert mpl.is_interactive() 112 113 114def test_nested_ion_ioff(): 115 # initial state is interactive 116 plt.ion() 117 118 # mixed ioff/ion 119 with plt.ioff(): 120 assert not mpl.is_interactive() 121 with plt.ion(): 122 assert mpl.is_interactive() 123 assert not mpl.is_interactive() 124 assert mpl.is_interactive() 125 126 # redundant contexts 127 with plt.ioff(): 128 with plt.ioff(): 129 assert not mpl.is_interactive() 130 assert mpl.is_interactive() 131 132 with plt.ion(): 133 plt.ioff() 134 assert mpl.is_interactive() 135 136 # initial state is not interactive 137 plt.ioff() 138 139 # mixed ioff/ion 140 with plt.ion(): 141 assert mpl.is_interactive() 142 with plt.ioff(): 143 assert not mpl.is_interactive() 144 assert mpl.is_interactive() 145 assert not mpl.is_interactive() 146 147 # redundant contexts 148 with plt.ion(): 149 with plt.ion(): 150 assert mpl.is_interactive() 151 assert not mpl.is_interactive() 152 153 with plt.ioff(): 154 plt.ion() 155 assert not mpl.is_interactive() 156 157 158def test_close(): 159 try: 160 plt.close(1.1) 161 except TypeError as e: 162 assert str(e) == "close() argument must be a Figure, an int, " \ 163 "a string, or None, not <class 'float'>" 164 165 166def test_subplot_reuse(): 167 ax1 = plt.subplot(121) 168 assert ax1 is plt.gca() 169 ax2 = plt.subplot(122) 170 assert ax2 is plt.gca() 171 ax3 = plt.subplot(121) 172 assert ax1 is plt.gca() 173 assert ax1 is ax3 174 175 176def test_axes_kwargs(): 177 # plt.axes() always creates new axes, even if axes kwargs differ. 178 plt.figure() 179 ax = plt.axes() 180 ax1 = plt.axes() 181 assert ax is not None 182 assert ax1 is not ax 183 plt.close() 184 185 plt.figure() 186 ax = plt.axes(projection='polar') 187 ax1 = plt.axes(projection='polar') 188 assert ax is not None 189 assert ax1 is not ax 190 plt.close() 191 192 plt.figure() 193 ax = plt.axes(projection='polar') 194 ax1 = plt.axes() 195 assert ax is not None 196 assert ax1.name == 'rectilinear' 197 assert ax1 is not ax 198 plt.close() 199 200 201def test_subplot_replace_projection(): 202 # plt.subplot() searches for axes with the same subplot spec, and if one 203 # exists, and the kwargs match returns it, create a new one if they do not 204 fig = plt.figure() 205 ax = plt.subplot(1, 2, 1) 206 ax1 = plt.subplot(1, 2, 1) 207 ax2 = plt.subplot(1, 2, 2) 208 # This will delete ax / ax1 as they fully overlap 209 ax3 = plt.subplot(1, 2, 1, projection='polar') 210 ax4 = plt.subplot(1, 2, 1, projection='polar') 211 assert ax is not None 212 assert ax1 is ax 213 assert ax2 is not ax 214 assert ax3 is not ax 215 assert ax3 is ax4 216 217 assert ax not in fig.axes 218 assert ax2 in fig.axes 219 assert ax3 in fig.axes 220 221 assert ax.name == 'rectilinear' 222 assert ax2.name == 'rectilinear' 223 assert ax3.name == 'polar' 224 225 226def test_subplot_kwarg_collision(): 227 ax1 = plt.subplot(projection='polar', theta_offset=0) 228 ax2 = plt.subplot(projection='polar', theta_offset=0) 229 assert ax1 is ax2 230 ax3 = plt.subplot(projection='polar', theta_offset=1) 231 assert ax1 is not ax3 232 assert ax1 not in plt.gcf().axes 233 234 235def test_gca_kwargs(): 236 # plt.gca() returns an existing axes, unless there were no axes. 237 plt.figure() 238 ax = plt.gca() 239 ax1 = plt.gca() 240 assert ax is not None 241 assert ax1 is ax 242 plt.close() 243 244 # plt.gca() raises a DeprecationWarning if called with kwargs. 245 plt.figure() 246 with pytest.warns( 247 MatplotlibDeprecationWarning, 248 match=r'Calling gca\(\) with keyword arguments was deprecated'): 249 ax = plt.gca(projection='polar') 250 ax1 = plt.gca() 251 assert ax is not None 252 assert ax1 is ax 253 assert ax1.name == 'polar' 254 plt.close() 255 256 # plt.gca() ignores keyword arguments if an axes already exists. 257 plt.figure() 258 ax = plt.gca() 259 with pytest.warns( 260 MatplotlibDeprecationWarning, 261 match=r'Calling gca\(\) with keyword arguments was deprecated'): 262 ax1 = plt.gca(projection='polar') 263 assert ax is not None 264 assert ax1 is ax 265 assert ax1.name == 'rectilinear' 266 plt.close() 267 268 269def test_subplot_projection_reuse(): 270 # create an axes 271 ax1 = plt.subplot(111) 272 # check that it is current 273 assert ax1 is plt.gca() 274 # make sure we get it back if we ask again 275 assert ax1 is plt.subplot(111) 276 # create a polar plot 277 ax2 = plt.subplot(111, projection='polar') 278 assert ax2 is plt.gca() 279 # this should have deleted the first axes 280 assert ax1 not in plt.gcf().axes 281 # assert we get it back if no extra parameters passed 282 assert ax2 is plt.subplot(111) 283 # now check explicitly setting the projection to rectilinear 284 # makes a new axes 285 ax3 = plt.subplot(111, projection='rectilinear') 286 assert ax3 is plt.gca() 287 assert ax3 is not ax2 288 assert ax2 not in plt.gcf().axes 289 290 291def test_subplot_polar_normalization(): 292 ax1 = plt.subplot(111, projection='polar') 293 ax2 = plt.subplot(111, polar=True) 294 ax3 = plt.subplot(111, polar=True, projection='polar') 295 assert ax1 is ax2 296 assert ax1 is ax3 297 298 with pytest.raises(ValueError, 299 match="polar=True, yet projection='3d'"): 300 ax2 = plt.subplot(111, polar=True, projection='3d') 301 302 303def test_subplot_change_projection(): 304 ax = plt.subplot() 305 projections = ('aitoff', 'hammer', 'lambert', 'mollweide', 306 'polar', 'rectilinear', '3d') 307 for proj in projections: 308 ax_next = plt.subplot(projection=proj) 309 assert ax_next is plt.subplot() 310 assert ax_next.name == proj 311 assert ax is not ax_next 312 ax = ax_next 313