Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7aae00a

Browse files
committed
setattr context manager.
1 parent 3d3472c commit 7aae00a

12 files changed

+148
-196
lines changed

lib/matplotlib/artist.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -897,13 +897,8 @@ def _update_property(self, k, v):
897897
raise AttributeError('Unknown property %s' % k)
898898
return func(v)
899899

900-
store = self.eventson
901-
self.eventson = False
902-
try:
903-
ret = [_update_property(self, k, v)
904-
for k, v in props.items()]
905-
finally:
906-
self.eventson = store
900+
with cbook._setattr_cm(self, eventson=False):
901+
ret = [_update_property(self, k, v) for k, v in props.items()]
907902

908903
if len(ret):
909904
self.pchanged()

lib/matplotlib/backend_bases.py

Lines changed: 98 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -2148,17 +2148,6 @@ def print_figure(self, filename, dpi=None, facecolor=None, edgecolor=None,
21482148
tight bbox is calculated.
21492149
21502150
"""
2151-
self._is_saving = True
2152-
# Remove the figure manager, if any, to avoid resizing the GUI widget.
2153-
# Having *no* manager and a *None* manager are currently different (see
2154-
# Figure.show); should probably be normalized to None at some point.
2155-
_no_manager = object()
2156-
if hasattr(self, 'manager'):
2157-
manager = self.manager
2158-
del self.manager
2159-
else:
2160-
manager = _no_manager
2161-
21622151
if format is None:
21632152
# get format from filename, or from backend's default filetype
21642153
if isinstance(filename, six.string_types):
@@ -2175,111 +2164,113 @@ def print_figure(self, filename, dpi=None, facecolor=None, edgecolor=None,
21752164

21762165
if dpi is None:
21772166
dpi = rcParams['savefig.dpi']
2178-
21792167
if dpi == 'figure':
21802168
dpi = getattr(self.figure, '_original_dpi', self.figure.dpi)
21812169

2182-
if facecolor is None:
2183-
facecolor = rcParams['savefig.facecolor']
2184-
if edgecolor is None:
2185-
edgecolor = rcParams['savefig.edgecolor']
2186-
2187-
origDPI = self.figure.dpi
2188-
origfacecolor = self.figure.get_facecolor()
2189-
origedgecolor = self.figure.get_edgecolor()
2190-
2191-
self.figure.dpi = dpi
2192-
self.figure.set_facecolor(facecolor)
2193-
self.figure.set_edgecolor(edgecolor)
2194-
2195-
bbox_inches = kwargs.pop("bbox_inches", None)
2196-
if bbox_inches is None:
2197-
bbox_inches = rcParams['savefig.bbox']
2198-
2199-
if bbox_inches:
2200-
# call adjust_bbox to save only the given area
2201-
if bbox_inches == "tight":
2202-
# when bbox_inches == "tight", it saves the figure
2203-
# twice. The first save command is just to estimate
2204-
# the bounding box of the figure. A stringIO object is
2205-
# used as a temporary file object, but it causes a
2206-
# problem for some backends (ps backend with
2207-
# usetex=True) if they expect a filename, not a
2208-
# file-like object. As I think it is best to change
2209-
# the backend to support file-like object, i'm going
2210-
# to leave it as it is. However, a better solution
2211-
# than stringIO seems to be needed. -JJL
2170+
# Remove the figure manager, if any, to avoid resizing the GUI widget.
2171+
# Some code (e.g. Figure.show) differentiates between having *no*
2172+
# manager and a *None* manager, which should be fixed at some point,
2173+
# but this should be fine.
2174+
with cbook._setattr_cm(self, _is_saving=True, manager=None), \
2175+
cbook._setattr_cm(self.figure, dpi=dpi):
2176+
2177+
if facecolor is None:
2178+
facecolor = rcParams['savefig.facecolor']
2179+
if edgecolor is None:
2180+
edgecolor = rcParams['savefig.edgecolor']
2181+
2182+
origfacecolor = self.figure.get_facecolor()
2183+
origedgecolor = self.figure.get_edgecolor()
2184+
2185+
self.figure.dpi = dpi
2186+
self.figure.set_facecolor(facecolor)
2187+
self.figure.set_edgecolor(edgecolor)
2188+
2189+
bbox_inches = kwargs.pop("bbox_inches", None)
2190+
if bbox_inches is None:
2191+
bbox_inches = rcParams['savefig.bbox']
2192+
2193+
if bbox_inches:
2194+
# call adjust_bbox to save only the given area
2195+
if bbox_inches == "tight":
2196+
# when bbox_inches == "tight", it saves the figure
2197+
# twice. The first save command is just to estimate
2198+
# the bounding box of the figure. A stringIO object is
2199+
# used as a temporary file object, but it causes a
2200+
# problem for some backends (ps backend with
2201+
# usetex=True) if they expect a filename, not a
2202+
# file-like object. As I think it is best to change
2203+
# the backend to support file-like object, i'm going
2204+
# to leave it as it is. However, a better solution
2205+
# than stringIO seems to be needed. -JJL
2206+
result = print_method(
2207+
io.BytesIO(),
2208+
dpi=dpi,
2209+
facecolor=facecolor,
2210+
edgecolor=edgecolor,
2211+
orientation=orientation,
2212+
dryrun=True,
2213+
**kwargs)
2214+
renderer = self.figure._cachedRenderer
2215+
bbox_inches = self.figure.get_tightbbox(renderer)
2216+
2217+
bbox_artists = kwargs.pop("bbox_extra_artists", None)
2218+
if bbox_artists is None:
2219+
bbox_artists = self.figure.get_default_bbox_extra_artists()
2220+
2221+
bbox_filtered = []
2222+
for a in bbox_artists:
2223+
bbox = a.get_window_extent(renderer)
2224+
if a.get_clip_on():
2225+
clip_box = a.get_clip_box()
2226+
if clip_box is not None:
2227+
bbox = Bbox.intersection(bbox, clip_box)
2228+
clip_path = a.get_clip_path()
2229+
if clip_path is not None and bbox is not None:
2230+
clip_path = \
2231+
clip_path.get_fully_transformed_path()
2232+
bbox = Bbox.intersection(
2233+
bbox, clip_path.get_extents())
2234+
if bbox is not None and (
2235+
bbox.width != 0 or bbox.height != 0):
2236+
bbox_filtered.append(bbox)
2237+
2238+
if bbox_filtered:
2239+
_bbox = Bbox.union(bbox_filtered)
2240+
trans = Affine2D().scale(1.0 / self.figure.dpi)
2241+
bbox_extra = TransformedBbox(_bbox, trans)
2242+
bbox_inches = Bbox.union([bbox_inches, bbox_extra])
2243+
2244+
pad = kwargs.pop("pad_inches", None)
2245+
if pad is None:
2246+
pad = rcParams['savefig.pad_inches']
2247+
2248+
bbox_inches = bbox_inches.padded(pad)
2249+
2250+
restore_bbox = tight_bbox.adjust_bbox(self.figure, bbox_inches,
2251+
canvas.fixed_dpi)
2252+
2253+
_bbox_inches_restore = (bbox_inches, restore_bbox)
2254+
else:
2255+
_bbox_inches_restore = None
2256+
2257+
try:
22122258
result = print_method(
2213-
io.BytesIO(),
2259+
filename,
22142260
dpi=dpi,
22152261
facecolor=facecolor,
22162262
edgecolor=edgecolor,
22172263
orientation=orientation,
2218-
dryrun=True,
2264+
bbox_inches_restore=_bbox_inches_restore,
22192265
**kwargs)
2220-
renderer = self.figure._cachedRenderer
2221-
bbox_inches = self.figure.get_tightbbox(renderer)
2222-
2223-
bbox_artists = kwargs.pop("bbox_extra_artists", None)
2224-
if bbox_artists is None:
2225-
bbox_artists = self.figure.get_default_bbox_extra_artists()
2226-
2227-
bbox_filtered = []
2228-
for a in bbox_artists:
2229-
bbox = a.get_window_extent(renderer)
2230-
if a.get_clip_on():
2231-
clip_box = a.get_clip_box()
2232-
if clip_box is not None:
2233-
bbox = Bbox.intersection(bbox, clip_box)
2234-
clip_path = a.get_clip_path()
2235-
if clip_path is not None and bbox is not None:
2236-
clip_path = clip_path.get_fully_transformed_path()
2237-
bbox = Bbox.intersection(bbox,
2238-
clip_path.get_extents())
2239-
if bbox is not None and (bbox.width != 0 or
2240-
bbox.height != 0):
2241-
bbox_filtered.append(bbox)
2242-
2243-
if bbox_filtered:
2244-
_bbox = Bbox.union(bbox_filtered)
2245-
trans = Affine2D().scale(1.0 / self.figure.dpi)
2246-
bbox_extra = TransformedBbox(_bbox, trans)
2247-
bbox_inches = Bbox.union([bbox_inches, bbox_extra])
2248-
2249-
pad = kwargs.pop("pad_inches", None)
2250-
if pad is None:
2251-
pad = rcParams['savefig.pad_inches']
2252-
2253-
bbox_inches = bbox_inches.padded(pad)
2254-
2255-
restore_bbox = tight_bbox.adjust_bbox(self.figure, bbox_inches,
2256-
canvas.fixed_dpi)
2257-
2258-
_bbox_inches_restore = (bbox_inches, restore_bbox)
2259-
else:
2260-
_bbox_inches_restore = None
2261-
2262-
try:
2263-
result = print_method(
2264-
filename,
2265-
dpi=dpi,
2266-
facecolor=facecolor,
2267-
edgecolor=edgecolor,
2268-
orientation=orientation,
2269-
bbox_inches_restore=_bbox_inches_restore,
2270-
**kwargs)
2271-
finally:
2272-
if bbox_inches and restore_bbox:
2273-
restore_bbox()
2274-
2275-
self.figure.dpi = origDPI
2276-
self.figure.set_facecolor(origfacecolor)
2277-
self.figure.set_edgecolor(origedgecolor)
2278-
self.figure.set_canvas(self)
2279-
if manager is not _no_manager:
2280-
self.manager = manager
2281-
self._is_saving = False
2282-
return result
2266+
finally:
2267+
if bbox_inches and restore_bbox:
2268+
restore_bbox()
2269+
2270+
self.figure.set_facecolor(origfacecolor)
2271+
self.figure.set_edgecolor(origedgecolor)
2272+
self.figure.set_canvas(self)
2273+
return result
22832274

22842275
@classmethod
22852276
def get_default_filetype(cls):

lib/matplotlib/backends/backend_agg.py

Lines changed: 11 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -489,53 +489,33 @@ def buffer_rgba(self):
489489
def print_raw(self, filename_or_obj, *args, **kwargs):
490490
FigureCanvasAgg.draw(self)
491491
renderer = self.get_renderer()
492-
original_dpi = renderer.dpi
493-
renderer.dpi = self.figure.dpi
494-
if isinstance(filename_or_obj, six.string_types):
495-
fileobj = open(filename_or_obj, 'wb')
496-
close = True
497-
else:
498-
fileobj = filename_or_obj
499-
close = False
500-
try:
492+
with cbook._setattr_cm(renderer, dpi=self.figure.dpi), \
493+
cbook.open_file_cm(filename_or_obj, "wb") as fh:
501494
fileobj.write(renderer._renderer.buffer_rgba())
502-
finally:
503-
if close:
504-
fileobj.close()
505-
renderer.dpi = original_dpi
506495
print_rgba = print_raw
507496

508497
def print_png(self, filename_or_obj, *args, **kwargs):
509498
FigureCanvasAgg.draw(self)
510499
renderer = self.get_renderer()
511-
original_dpi = renderer.dpi
512-
renderer.dpi = self.figure.dpi
513500

514-
version_str = 'matplotlib version ' + __version__ + \
515-
', http://matplotlib.org/'
501+
version_str = (
502+
'matplotlib version ' + __version__ + ', http://matplotlib.org/')
516503
metadata = OrderedDict({'Software': version_str})
517504
user_metadata = kwargs.pop("metadata", None)
518505
if user_metadata is not None:
519506
metadata.update(user_metadata)
520507

521-
try:
522-
with cbook.open_file_cm(filename_or_obj, "wb") as fh:
523-
_png.write_png(renderer._renderer, fh,
524-
self.figure.dpi, metadata=metadata)
525-
finally:
526-
renderer.dpi = original_dpi
508+
with cbook._setattr_cm(renderer, dpi=self.figure.dpi), \
509+
cbook.open_file_cm(filename_or_obj, "wb") as fh:
510+
_png.write_png(renderer._renderer, fh,
511+
self.figure.dpi, metadata=metadata)
527512

528513
def print_to_buffer(self):
529514
FigureCanvasAgg.draw(self)
530515
renderer = self.get_renderer()
531-
original_dpi = renderer.dpi
532-
renderer.dpi = self.figure.dpi
533-
try:
534-
result = (renderer._renderer.buffer_rgba(),
535-
(int(renderer.width), int(renderer.height)))
536-
finally:
537-
renderer.dpi = original_dpi
538-
return result
516+
with cbook._setattr_cm(renderer, dpi=self.figure.dpi):
517+
return (renderer._renderer.buffer_rgba(),
518+
(int(renderer.width), int(renderer.height)))
539519

540520
if _has_pil:
541521
# add JPEG support

lib/matplotlib/backends/backend_qt5.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -166,12 +166,9 @@ def cooperative_qwidget_init(self, *args, **kwargs):
166166

167167
@functools.wraps(__init__)
168168
def wrapper(self, **kwargs):
169-
try:
170-
QtWidgets.QWidget.__init__ = cooperative_qwidget_init
169+
with cbook._setattr_cm(QtWidgets.QWidget,
170+
__init__=cooperative_qwidget_init):
171171
__init__(self, **kwargs)
172-
finally:
173-
# Restore __init__
174-
QtWidgets.QWidget.__init__ = qwidget_init
175172

176173
return wrapper
177174

lib/matplotlib/backends/backend_qt5agg.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -127,12 +127,8 @@ def draw(self):
127127
# uses the result of the draw() to update plot elements.
128128
if self._agg_is_drawing:
129129
return
130-
131-
self._agg_is_drawing = True
132-
try:
130+
with cbook._setattr_cm(self, _agg_is_drawing=True):
133131
super(FigureCanvasQTAggBase, self).draw()
134-
finally:
135-
self._agg_is_drawing = False
136132
self.update()
137133

138134
def draw_idle(self):

lib/matplotlib/backends/backend_webagg_core.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -151,14 +151,11 @@ def show(self):
151151

152152
def draw(self):
153153
renderer = self.get_renderer(cleared=True)
154-
155154
self._png_is_old = True
156-
157-
backend_agg.RendererAgg.lock.acquire()
158155
try:
159-
self.figure.draw(renderer)
156+
with backend_agg.RendererAgg.lock:
157+
self.figure.draw(renderer)
160158
finally:
161-
backend_agg.RendererAgg.lock.release()
162159
# Swap the frames
163160
self.manager.refresh_all()
164161

lib/matplotlib/cbook/__init__.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2803,3 +2803,21 @@ def _str_lower_equal(obj, s):
28032803
cannot be used in a boolean context.
28042804
"""
28052805
return isinstance(obj, six.string_types) and obj.lower() == s
2806+
2807+
2808+
@contextlib.contextmanager
2809+
def _setattr_cm(obj, **kwargs):
2810+
"""Temporarily set some attributes; restore original state at context exit.
2811+
"""
2812+
sentinel = object()
2813+
origs = [(attr, getattr(obj, attr, sentinel)) for attr in kwargs]
2814+
try:
2815+
for attr, val in kwargs.items():
2816+
setattr(obj, attr, val)
2817+
yield
2818+
finally:
2819+
for attr, orig in origs:
2820+
if orig is sentinel:
2821+
delattr(obj, attr)
2822+
else:
2823+
setattr(obj, attr, orig)

0 commit comments

Comments
 (0)