Skip to content

Commit

Permalink
Merge pull request scipy#12558 from peterbell10/scipy-fft-function
Browse files Browse the repository at this point in the history
MAINT: Remove hack to allow scipy.fft to act like a function
  • Loading branch information
ev-br authored Jul 17, 2020
2 parents 6bd8975 + a56e3d9 commit 07aee78
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 64 deletions.
13 changes: 3 additions & 10 deletions scipy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,8 @@
'use numpy.random.{0} instead')
rand = _deprecated(_msg.format('rand'))(rand)
randn = _deprecated(_msg.format('randn'))(randn)
from numpy.fft import fft, ifft
# fft is especially problematic, so we deprecate it with a shorter window
fft_msg = ('Using scipy.fft as a function is deprecated and will be '
'removed in SciPy 1.5.0, use scipy.fft.fft instead.')
# for wrapping in scipy.fft.__call__, so the stacklevel is one off from the
# usual (2)
_dep_fft = _deprecated(fft_msg, stacklevel=3)(fft)
fft = _deprecated(fft_msg)(fft)
# fft is especially problematic, so was removed in SciPy 1.6.0
from numpy.fft import ifft
ifft = _deprecated('scipy.ifft is deprecated and will be removed in SciPy '
'2.0.0, use scipy.fft.ifft instead')(ifft)
import numpy.lib.scimath as _sci
Expand All @@ -101,7 +95,7 @@
globals()[_key] = _fun

__all__ += _num.__all__
__all__ += ['randn', 'rand', 'fft', 'ifft']
__all__ += ['randn', 'rand', 'ifft']

del _num
# Remove the linalg imported from NumPy so that the scipy.linalg package can be
Expand Down Expand Up @@ -152,4 +146,3 @@

# This makes "from scipy import fft" return scipy.fft, not np.fft
del fft
from . import fft
12 changes: 4 additions & 8 deletions scipy/_lib/tests/test__util.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,21 +141,17 @@ def test_mapwrapper_parallel():

# get our custom ones and a few from the "import *" cases
@pytest.mark.parametrize(
'key', ('fft', 'ifft', 'diag', 'arccos',
'randn', 'rand', 'array'))
'key', ('ifft', 'diag', 'arccos', 'randn', 'rand', 'array'))
def test_numpy_deprecation(key):
"""Test that 'from numpy import *' functions are deprecated."""
if key in ('fft', 'ifft', 'diag', 'arccos'):
if key in ('ifft', 'diag', 'arccos'):
arg = [1.0, 0.]
elif key == 'finfo':
arg = float
else:
arg = 2
func = getattr(scipy, key)
if key == 'fft':
match = r'scipy\.fft.*deprecated.*1.5.0.*'
else:
match = r'scipy\.%s is deprecated.*2\.0\.0' % key
match = r'scipy\.%s is deprecated.*2\.0\.0' % key
with deprecated_call(match=match) as dep:
func(arg) # deprecated
# in case we catch more than one dep warning
Expand All @@ -164,7 +160,7 @@ def test_numpy_deprecation(key):
assert 'test__util' in basenames
if key in ('rand', 'randn'):
root = np.random
elif key in ('fft', 'ifft'):
elif key == 'ifft':
root = np.fft
else:
root = np
Expand Down
17 changes: 0 additions & 17 deletions scipy/fft/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,20 +96,3 @@
from scipy._lib._testutils import PytestTester
test = PytestTester(__name__)
del PytestTester


# Hack to allow numpy.fft.fft to be called as scipy.fft
import sys
class _FFTModule(sys.modules[__name__].__class__):
@staticmethod
def __call__(*args, **kwargs):
from scipy import _dep_fft
return _dep_fft(*args, **kwargs)


import os
if os.environ.get('_SCIPY_BUILDING_DOC') != 'True':
sys.modules[__name__].__class__ = _FFTModule
del os
del _FFTModule
del sys
68 changes: 39 additions & 29 deletions scipy/fft/tests/test_fft_function.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,46 @@
import numpy as np
import subprocess
import sys

TEST_BODY = r"""
import pytest
import numpy as np
from numpy.testing import assert_allclose
import scipy
import sys
import pytest
if hasattr(scipy, 'fft'):
raise AssertionError("scipy.fft should require an explicit import")
np.random.seed(1234)
x = np.random.randn(10) + 1j * np.random.randn(10)
X = np.fft.fft(x)
# Callable before scipy.fft is imported
with pytest.deprecated_call(match=r'2\.0\.0'):
y = scipy.ifft(X)
assert_allclose(y, x)
# Callable after scipy.fft is imported
import scipy.fft
with pytest.deprecated_call(match=r'2\.0\.0'):
y = scipy.ifft(X)
assert_allclose(y, x)
"""

def test_fft_function():
# Many NumPy symbols are imported into the scipy namespace, including
# numpy.fft.fft as scipy.fft, conflicting with this module (gh-10253)
np.random.seed(1234)

# Callable before scipy.fft is imported
import scipy
x = np.random.randn(10) + 1j * np.random.randn(10)
with pytest.deprecated_call(match=r'1\.5\.0'):
X = scipy.fft(x)
with pytest.deprecated_call(match=r'2\.0\.0'):
y = scipy.ifft(X)
assert_allclose(y, x)

# Callable after scipy.fft is imported
import scipy.fft
assert_allclose(X, scipy.fft.fft(x))
with pytest.deprecated_call(match=r'1\.5\.0'):
X = scipy.fft(x)
assert_allclose(X, scipy.fft.fft(x))
with pytest.deprecated_call(match=r'2\.0\.0'):
y = scipy.ifft(X)
assert_allclose(y, x)

# Callable when imported using from
# Historically, scipy.fft was an alias for numpy.fft.fft
# Ensure there are no conflicts with the FFT module (gh-10253)

# Test must run in a subprocess so scipy.fft is not already imported
subprocess.check_call([sys.executable, '-c', TEST_BODY])

# scipy.fft is the correct module
from scipy import fft
with pytest.deprecated_call(match=r'1\.5\.0'):
X = fft(x)
with pytest.deprecated_call(match=r'2\.0\.0'):
y = scipy.ifft(X)
assert_allclose(y, x)
assert not callable(fft)
assert fft.__name__ == 'scipy.fft'

from scipy import ifft
assert ifft.__wrapped__ is np.fft.ifft

0 comments on commit 07aee78

Please sign in to comment.