From e5f4aabf5a6581832a4c2cf6c851de7b58d317f7 2010-01-09 00:14:05 From: Fernando Perez Date: 2010-01-09 00:14:05 Subject: [PATCH] Make IPython work if a recent numpy is not available. I updated the numpy testing decorators module and didn't notice they recently introduced dependencies that make it use the rest of numpy. We may later want to revisit this, as my solution is ugly, but I simply hacked it to load locally the code they added, which we now carry as _numpy_testing_utils. Ultimately the solution to this should be taken up with upstream numpy, so that we all agree on a common set of testing decorators with no other dependencies aside from the stdlib. But for now, this will let us move forward as that discussion could take a while. --- diff --git a/IPython/external/_numpy_testing_utils.py b/IPython/external/_numpy_testing_utils.py new file mode 100644 index 0000000..8bcbb05 --- /dev/null +++ b/IPython/external/_numpy_testing_utils.py @@ -0,0 +1,120 @@ +# IPython: modified copy of numpy.testing.utils, so numpy.testing.decorators +# works without numpy being installed. +""" +Utility function to facilitate testing. +""" + +import os +import sys +import re +import operator +import types +import warnings + +# The following two classes are copied from python 2.6 warnings module (context +# manager) +class WarningMessage(object): + + """ + Holds the result of a single showwarning() call. + + Notes + ----- + `WarningMessage` is copied from the Python 2.6 warnings module, + so it can be used in NumPy with older Python versions. + + """ + + _WARNING_DETAILS = ("message", "category", "filename", "lineno", "file", + "line") + + def __init__(self, message, category, filename, lineno, file=None, + line=None): + local_values = locals() + for attr in self._WARNING_DETAILS: + setattr(self, attr, local_values[attr]) + if category: + self._category_name = category.__name__ + else: + self._category_name = None + + def __str__(self): + return ("{message : %r, category : %r, filename : %r, lineno : %s, " + "line : %r}" % (self.message, self._category_name, + self.filename, self.lineno, self.line)) + +class WarningManager: + """ + A context manager that copies and restores the warnings filter upon + exiting the context. + + The 'record' argument specifies whether warnings should be captured by a + custom implementation of ``warnings.showwarning()`` and be appended to a + list returned by the context manager. Otherwise None is returned by the + context manager. The objects appended to the list are arguments whose + attributes mirror the arguments to ``showwarning()``. + + The 'module' argument is to specify an alternative module to the module + named 'warnings' and imported under that name. This argument is only useful + when testing the warnings module itself. + + Notes + ----- + `WarningManager` is a copy of the ``catch_warnings`` context manager + from the Python 2.6 warnings module, with slight modifications. + It is copied so it can be used in NumPy with older Python versions. + + """ + def __init__(self, record=False, module=None): + self._record = record + if module is None: + self._module = sys.modules['warnings'] + else: + self._module = module + self._entered = False + + def __enter__(self): + if self._entered: + raise RuntimeError("Cannot enter %r twice" % self) + self._entered = True + self._filters = self._module.filters + self._module.filters = self._filters[:] + self._showwarning = self._module.showwarning + if self._record: + log = [] + def showwarning(*args, **kwargs): + log.append(WarningMessage(*args, **kwargs)) + self._module.showwarning = showwarning + return log + else: + return None + + def __exit__(self): + if not self._entered: + raise RuntimeError("Cannot exit %r without entering first" % self) + self._module.filters = self._filters + self._module.showwarning = self._showwarning + +def assert_warns(warning_class, func, *args, **kw): + """Fail unless a warning of class warning_class is thrown by callable when + invoked with arguments args and keyword arguments kwargs. + + If a different type of warning is thrown, it will not be caught, and the + test case will be deemed to have suffered an error. + """ + + # XXX: once we may depend on python >= 2.6, this can be replaced by the + # warnings module context manager. + ctx = WarningManager(record=True) + l = ctx.__enter__() + warnings.simplefilter('always') + try: + func(*args, **kw) + if not len(l) > 0: + raise AssertionError("No warning raised when calling %s" + % func.__name__) + if not l[0].category is warning_class: + raise AssertionError("First warning for %s is not a " \ + "%s( is %s)" % (func.__name__, warning_class, l[0])) + finally: + ctx.__exit__() diff --git a/IPython/external/decorators.py b/IPython/external/decorators.py index 68335cc..6f457f1 100644 --- a/IPython/external/decorators.py +++ b/IPython/external/decorators.py @@ -16,8 +16,17 @@ function name, setup and teardown functions and so on - see import warnings import sys -from numpy.testing.utils import \ - WarningManager, WarningMessage +# IPython changes: make this work if numpy not available +# Original code: +#from numpy.testing.utils import \ +# WarningManager, WarningMessage +# Our version: +try: + from numpy.testing.utils import WarningManager, WarningMessage +except ImportError: + from _numpy_testing_utils import WarningManager, WarningMessage + +# End IPython changes def slow(t): """