Skip to content

setattr context manager. #10314

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions lib/matplotlib/artist.py
Original file line number Diff line number Diff line change
Expand Up @@ -880,13 +880,8 @@ def _update_property(self, k, v):
raise AttributeError('Unknown property %s' % k)
return func(v)

store = self.eventson
self.eventson = False
try:
ret = [_update_property(self, k, v)
for k, v in props.items()]
finally:
self.eventson = store
with cbook._setattr_cm(self, eventson=False):
ret = [_update_property(self, k, v) for k, v in props.items()]

if len(ret):
self.pchanged()
Expand Down
192 changes: 92 additions & 100 deletions lib/matplotlib/backend_bases.py
Original file line number Diff line number Diff line change
Expand Up @@ -2113,17 +2113,6 @@ def print_figure(self, filename, dpi=None, facecolor=None, edgecolor=None,
tight bbox is calculated.

"""
self._is_saving = True
# Remove the figure manager, if any, to avoid resizing the GUI widget.
# Having *no* manager and a *None* manager are currently different (see
# Figure.show); should probably be normalized to None at some point.
_no_manager = object()
if hasattr(self, 'manager'):
manager = self.manager
del self.manager
else:
manager = _no_manager

if format is None:
# get format from filename, or from backend's default filetype
if isinstance(filename, getattr(os, "PathLike", ())):
Expand All @@ -2142,104 +2131,107 @@ def print_figure(self, filename, dpi=None, facecolor=None, edgecolor=None,

if dpi is None:
dpi = rcParams['savefig.dpi']

if dpi == 'figure':
dpi = getattr(self.figure, '_original_dpi', self.figure.dpi)

if facecolor is None:
facecolor = rcParams['savefig.facecolor']
if edgecolor is None:
edgecolor = rcParams['savefig.edgecolor']

origDPI = self.figure.dpi
origfacecolor = self.figure.get_facecolor()
origedgecolor = self.figure.get_edgecolor()

self.figure.dpi = dpi
self.figure.set_facecolor(facecolor)
self.figure.set_edgecolor(edgecolor)

bbox_inches = kwargs.pop("bbox_inches", None)
if bbox_inches is None:
bbox_inches = rcParams['savefig.bbox']

if bbox_inches:
# call adjust_bbox to save only the given area
if bbox_inches == "tight":
# When bbox_inches == "tight", it saves the figure twice. The
# first save command (to a BytesIO) is just to estimate the
# bounding box of the figure.
# Remove the figure manager, if any, to avoid resizing the GUI widget.
# Some code (e.g. Figure.show) differentiates between having *no*
# manager and a *None* manager, which should be fixed at some point,
# but this should be fine.
with cbook._setattr_cm(self, _is_saving=True, manager=None), \
cbook._setattr_cm(self.figure, dpi=dpi):

if facecolor is None:
facecolor = rcParams['savefig.facecolor']
if edgecolor is None:
edgecolor = rcParams['savefig.edgecolor']

origfacecolor = self.figure.get_facecolor()
origedgecolor = self.figure.get_edgecolor()

self.figure.dpi = dpi
self.figure.set_facecolor(facecolor)
self.figure.set_edgecolor(edgecolor)

bbox_inches = kwargs.pop("bbox_inches", None)
if bbox_inches is None:
bbox_inches = rcParams['savefig.bbox']

if bbox_inches:
# call adjust_bbox to save only the given area
if bbox_inches == "tight":
# When bbox_inches == "tight", it saves the figure twice.
# The first save command (to a BytesIO) is just to estimate
# the bounding box of the figure.
result = print_method(
io.BytesIO(),
dpi=dpi,
facecolor=facecolor,
edgecolor=edgecolor,
orientation=orientation,
dryrun=True,
**kwargs)
renderer = self.figure._cachedRenderer
bbox_inches = self.figure.get_tightbbox(renderer)

bbox_artists = kwargs.pop("bbox_extra_artists", None)
if bbox_artists is None:
bbox_artists = \
self.figure.get_default_bbox_extra_artists()

bbox_filtered = []
for a in bbox_artists:
bbox = a.get_window_extent(renderer)
if a.get_clip_on():
clip_box = a.get_clip_box()
if clip_box is not None:
bbox = Bbox.intersection(bbox, clip_box)
clip_path = a.get_clip_path()
if clip_path is not None and bbox is not None:
clip_path = \
clip_path.get_fully_transformed_path()
bbox = Bbox.intersection(
bbox, clip_path.get_extents())
if bbox is not None and (
bbox.width != 0 or bbox.height != 0):
bbox_filtered.append(bbox)

if bbox_filtered:
_bbox = Bbox.union(bbox_filtered)
trans = Affine2D().scale(1.0 / self.figure.dpi)
bbox_extra = TransformedBbox(_bbox, trans)
bbox_inches = Bbox.union([bbox_inches, bbox_extra])

pad = kwargs.pop("pad_inches", None)
if pad is None:
pad = rcParams['savefig.pad_inches']

bbox_inches = bbox_inches.padded(pad)

restore_bbox = tight_bbox.adjust_bbox(self.figure, bbox_inches,
canvas.fixed_dpi)

_bbox_inches_restore = (bbox_inches, restore_bbox)
else:
_bbox_inches_restore = None

try:
result = print_method(
io.BytesIO(),
filename,
dpi=dpi,
facecolor=facecolor,
edgecolor=edgecolor,
orientation=orientation,
dryrun=True,
bbox_inches_restore=_bbox_inches_restore,
**kwargs)
renderer = self.figure._cachedRenderer
bbox_inches = self.figure.get_tightbbox(renderer)

bbox_artists = kwargs.pop("bbox_extra_artists", None)
if bbox_artists is None:
bbox_artists = self.figure.get_default_bbox_extra_artists()

bbox_filtered = []
for a in bbox_artists:
bbox = a.get_window_extent(renderer)
if a.get_clip_on():
clip_box = a.get_clip_box()
if clip_box is not None:
bbox = Bbox.intersection(bbox, clip_box)
clip_path = a.get_clip_path()
if clip_path is not None and bbox is not None:
clip_path = clip_path.get_fully_transformed_path()
bbox = Bbox.intersection(bbox,
clip_path.get_extents())
if bbox is not None and (bbox.width != 0 or
bbox.height != 0):
bbox_filtered.append(bbox)

if bbox_filtered:
_bbox = Bbox.union(bbox_filtered)
trans = Affine2D().scale(1.0 / self.figure.dpi)
bbox_extra = TransformedBbox(_bbox, trans)
bbox_inches = Bbox.union([bbox_inches, bbox_extra])

pad = kwargs.pop("pad_inches", None)
if pad is None:
pad = rcParams['savefig.pad_inches']

bbox_inches = bbox_inches.padded(pad)

restore_bbox = tight_bbox.adjust_bbox(self.figure, bbox_inches,
canvas.fixed_dpi)

_bbox_inches_restore = (bbox_inches, restore_bbox)
else:
_bbox_inches_restore = None

try:
result = print_method(
filename,
dpi=dpi,
facecolor=facecolor,
edgecolor=edgecolor,
orientation=orientation,
bbox_inches_restore=_bbox_inches_restore,
**kwargs)
finally:
if bbox_inches and restore_bbox:
restore_bbox()

self.figure.dpi = origDPI
self.figure.set_facecolor(origfacecolor)
self.figure.set_edgecolor(origedgecolor)
self.figure.set_canvas(self)
if manager is not _no_manager:
self.manager = manager
self._is_saving = False
return result
finally:
if bbox_inches and restore_bbox:
restore_bbox()

self.figure.set_facecolor(origfacecolor)
self.figure.set_edgecolor(origedgecolor)
self.figure.set_canvas(self)
return result

@classmethod
def get_default_filetype(cls):
Expand Down
44 changes: 12 additions & 32 deletions lib/matplotlib/backends/backend_agg.py
Original file line number Diff line number Diff line change
Expand Up @@ -486,53 +486,33 @@ def buffer_rgba(self):
def print_raw(self, filename_or_obj, *args, **kwargs):
FigureCanvasAgg.draw(self)
renderer = self.get_renderer()
original_dpi = renderer.dpi
renderer.dpi = self.figure.dpi
if isinstance(filename_or_obj, six.string_types):
fileobj = open(filename_or_obj, 'wb')
close = True
else:
fileobj = filename_or_obj
close = False
try:
fileobj.write(renderer._renderer.buffer_rgba())
finally:
if close:
fileobj.close()
renderer.dpi = original_dpi
with cbook._setattr_cm(renderer, dpi=self.figure.dpi), \
cbook.open_file_cm(filename_or_obj, "wb") as fh:
fh.write(renderer._renderer.buffer_rgba())
print_rgba = print_raw

def print_png(self, filename_or_obj, *args, **kwargs):
FigureCanvasAgg.draw(self)
renderer = self.get_renderer()
original_dpi = renderer.dpi
renderer.dpi = self.figure.dpi

version_str = 'matplotlib version ' + __version__ + \
', http://matplotlib.org/'
version_str = (
'matplotlib version ' + __version__ + ', http://matplotlib.org/')
metadata = OrderedDict({'Software': version_str})
user_metadata = kwargs.pop("metadata", None)
if user_metadata is not None:
metadata.update(user_metadata)

try:
with cbook.open_file_cm(filename_or_obj, "wb") as fh:
_png.write_png(renderer._renderer, fh,
self.figure.dpi, metadata=metadata)
finally:
renderer.dpi = original_dpi
with cbook._setattr_cm(renderer, dpi=self.figure.dpi), \
cbook.open_file_cm(filename_or_obj, "wb") as fh:
_png.write_png(renderer._renderer, fh,
self.figure.dpi, metadata=metadata)

def print_to_buffer(self):
FigureCanvasAgg.draw(self)
renderer = self.get_renderer()
original_dpi = renderer.dpi
renderer.dpi = self.figure.dpi
try:
result = (renderer._renderer.buffer_rgba(),
(int(renderer.width), int(renderer.height)))
finally:
renderer.dpi = original_dpi
return result
with cbook._setattr_cm(renderer, dpi=self.figure.dpi):
return (renderer._renderer.buffer_rgba(),
(int(renderer.width), int(renderer.height)))

if _has_pil:
# add JPEG support
Expand Down
14 changes: 4 additions & 10 deletions lib/matplotlib/backends/backend_qt5.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import matplotlib

from matplotlib import backend_tools, cbook
from matplotlib._pylab_helpers import Gcf
from matplotlib.backend_bases import (
_Backend, FigureCanvasBase, FigureManagerBase, NavigationToolbar2,
Expand All @@ -20,7 +21,6 @@
from matplotlib.backends.qt_editor.formsubplottool import UiSubplotTool
from matplotlib.figure import Figure
from matplotlib.backend_managers import ToolManager
from matplotlib import backend_tools

from .qt_compat import (
QtCore, QtGui, QtWidgets, _getSaveFileName, is_pyqt5, __version__, QT_API)
Expand Down Expand Up @@ -169,12 +169,9 @@ def cooperative_qwidget_init(self, *args, **kwargs):

@functools.wraps(__init__)
def wrapper(self, **kwargs):
try:
QtWidgets.QWidget.__init__ = cooperative_qwidget_init
with cbook._setattr_cm(QtWidgets.QWidget,
__init__=cooperative_qwidget_init):
__init__(self, **kwargs)
finally:
# Restore __init__
QtWidgets.QWidget.__init__ = qwidget_init

return wrapper

Expand Down Expand Up @@ -492,11 +489,8 @@ def draw(self):
# that uses the result of the draw() to update plot elements.
if self._is_drawing:
return
self._is_drawing = True
try:
with cbook._setattr_cm(self, _is_drawing=True):
super().draw()
finally:
self._is_drawing = False
self.update()

def draw_idle(self):
Expand Down
18 changes: 18 additions & 0 deletions lib/matplotlib/cbook/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2070,3 +2070,21 @@ def method(self, *args, **kwargs):
raise NotImplementedError("Parent class already defines aliases")
cls._alias_map = alias_d
return cls


@contextlib.contextmanager
def _setattr_cm(obj, **kwargs):
"""Temporarily set some attributes; restore original state at context exit.
"""
sentinel = object()
origs = [(attr, getattr(obj, attr, sentinel)) for attr in kwargs]
try:
for attr, val in kwargs.items():
setattr(obj, attr, val)
yield
finally:
for attr, orig in origs:
if orig is sentinel:
delattr(obj, attr)
else:
setattr(obj, attr, orig)
13 changes: 3 additions & 10 deletions lib/matplotlib/font_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,11 +218,7 @@ def win32InstalledFonts(directory=None, fontext='ttf'):
direc = os.path.abspath(direc).lower()
if os.path.splitext(direc)[1][1:] in fontext:
items.add(direc)
except EnvironmentError:
continue
except WindowsError:
continue
except MemoryError:
except (EnvironmentError, MemoryError, WindowsError):
continue
return list(items)
finally:
Expand Down Expand Up @@ -520,17 +516,14 @@ def createFontList(fontfiles, fontext='ttf'):
seen.add(fname)
if fontext == 'afm':
try:
fh = open(fpath, 'rb')
with open(fpath, 'rb') as fh:
font = afm.AFM(fh)
except EnvironmentError:
_log.info("Could not open font file %s", fpath)
continue
try:
font = afm.AFM(fh)
except RuntimeError:
_log.info("Could not parse font file %s", fpath)
continue
finally:
fh.close()
try:
prop = afmFontProperty(fpath, font)
except KeyError:
Expand Down
Loading