Skip to content
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

Test a few magics #12950

Draft
wants to merge 14 commits into
base: main
Choose a base branch
from
Empty file.
7 changes: 7 additions & 0 deletions IPython/core/tests/test_magics/conftest.py
@@ -0,0 +1,7 @@
import pytest


@pytest.fixture
def ipython():
'''Get access to the global IPython instance.'''
return get_ipython()
Expand Up @@ -33,7 +33,7 @@
from IPython.utils.tempdir import (TemporaryDirectory,
TemporaryWorkingDirectory)
from IPython.utils.process import find_cmd
from .test_debugger import PdbTestInput
from ..test_debugger import PdbTestInput

import pytest

Expand Down
73 changes: 73 additions & 0 deletions IPython/core/tests/test_magics/test_auto.py
@@ -0,0 +1,73 @@
import pytest

from IPython.testing import tools as tt


class TestAutomagic:
'''Tests for the %automagic line magic.'''

def test_set_true(self, ipython):
'''Ensure that the magic works after enabling.'''
ipython.run_line_magic('automagic', 'true')
ipython.run_cell('1 + 1')
ipython.run_cell('recall')
assert ipython.rl_next_input == '2'

def test_set_false(self, ipython):
'''Ensure that the magic doesn't work after disabling.'''
ipython.run_line_magic('automagic', 'false')
ipython.run_cell('1 + 1')
res = ipython.run_cell('recall')
assert isinstance(res.error_in_exec, NameError)

def test_toggle(self, ipython):
'''Ensure that running %automagic without arguments toggles it.'''
ipython.run_line_magic('automagic', '')
ipython.run_cell('1 + 1')
ipython.run_cell('recall')
assert ipython.rl_next_input == '2'

ipython.run_line_magic('automagic', '')
ipython.run_cell('1 + 1')
res = ipython.run_cell('recall')
assert isinstance(res.error_in_exec, NameError)


class TestAutocall:
'''Tests for the %autocall line magic.'''

@staticmethod
@pytest.fixture
def autocall_smart(ipython):
'''Set %autocall into Smart mode.'''
previous_value = ipython.autocall
ipython.run_line_magic('autocall', '1')

yield

ipython.run_line_magic('autocall', str(previous_value))

def test_invalid_input(self, ipython):
'''Ensure that invalid input is gracefully handled.'''
with tt.AssertPrints('Valid modes: (0->Off, 1->Smart, 2->Full'):
ipython.run_line_magic('autocall', 'random')

def test_exact_value(self, ipython, autocall_smart):
'''Ensure that the magic can be enabled with the integer value of the mode.'''
ipython.run_line_magic('autocall', '2')
out = ipython.run_cell('int "5"').result
assert out == 5

def test_toggle_already_set(self, ipython, autocall_smart):
'''Ensure that Smart mode can be enabled by toggling while in Off mode.'''
ipython.run_line_magic('autocall', '0')
ipython.run_line_magic('autocall', '')
out = ipython.run_cell('int "5"').result
assert out == 5

def test_toggle_not_set(self, ipython, autocall_smart):
'''Ensure that %autocall can be disabled by toggling while in any mode
except for Off.'''
ipython.run_line_magic('autocall', '')
out = ipython.run_cell('print').result
assert out == print
141 changes: 141 additions & 0 deletions IPython/core/tests/test_magics/test_history.py
@@ -0,0 +1,141 @@
'''Tests of the magics from core.magics.history.'''
import pytest

from IPython.testing import tools as tt


@pytest.fixture
def history(ipython):
'''Populate the global IPython instance with some lines of history.'''
ipython.history_manager.reset() # Clear any existing history.
cmds = ['a = 1',
'def b():\n return a ** 2',
'print(a, b())',
'%recall 1']
for cmd in cmds:
ipython.run_cell(cmd, store_history=True)

yield cmds

ipython.history_manager.reset()
ipython.user_ns.pop('a')
ipython.user_ns.pop('b')


class TestRecall:
'''Tests for the %recall line magic.'''

def test_no_args(self, ipython):
'''Ensure that %recall without arguments recalls the result of the last cell.'''
ipython.run_cell('1 + 2')
ipython.run_line_magic('recall', '')
assert ipython.rl_next_input == '3'

def test_index_in_history(self, ipython, history):
'''Ensure that an integer argument is interpreted as an index in cell history
and the input of the corresponding cell is recalled.'''
ipython.run_line_magic('recall', '1')
assert ipython.rl_next_input == history[0]

def test_search_in_namespace(self, ipython, history):
'''Ensure that a non-integer string is interpreted as an item in the namespace.'''
ipython.run_line_magic('recall', 'a')
assert ipython.rl_next_input == '1'

@pytest.mark.regression
def test_search_no_error(self, ipython, history):
'''Ensure that no error message is printed when a search in the namespace is successful.'''
with tt.AssertNotPrints("Couldn't evaluate or find in history"):
ipython.run_line_magic('recall', 'a')

def test_search_in_history(self, ipython, history):
'''Ensure that a non-integer string that wasn't found in the namespace
is interpreted as the search query in the history.'''
ipython.run_line_magic('recall', 'def')
assert ipython.rl_next_input == history[1]

def test_search_failed(self, ipython, history):
'''Ensure that an error message is printed when a non-integer argument wasn't found
neither in the namespace nor the history.'''
with tt.AssertPrints("Couldn't evaluate or find in history"):
ipython.run_line_magic('recall', 'not_in_ns_or_history')


class TestRerun:
'''Tests for the %rerun line magic.'''

error_not_found = 'No lines in history match specification'
error_integer_expected = '-l option expects a single integer argument'

def test_lines_zero(self, ipython):
'''Ensure that the error message about a failed search is not printed
when 0 last lines are requested.'''
with tt.AssertNotPrints(self.error_not_found):
ipython.run_line_magic('rerun', '-l 0')

def test_lines_non_integer(self, ipython):
'''Ensure that invalid input is gracefully handled.'''
with tt.AssertPrints(self.error_integer_expected):
ipython.run_line_magic('rerun', '-l one')

def test_search(self, ipython, history):
'''Ensure that rerunning by search term works.'''
executing_def = '\n'.join((
'=== Executing: ===',
history[1],
'=== Output: ===',
))
with tt.AssertPrints(executing_def):
ipython.run_line_magic('rerun', '-g def')

with tt.AssertPrints(self.error_not_found):
ipython.run_line_magic('rerun', 'non-existent')

def test_range(self, ipython, history):
'''Ensure that one can rerun several lines by specifying a range.'''
executing_234 = '\n'.join((
'=== Executing: ===',
history[1],
history[2],
history[3],
'=== Output: ===',
'1 1',
))
with tt.AssertPrints(executing_234):
ipython.run_line_magic('rerun', '2-4')
assert ipython.rl_next_input == history[0]

def test_no_args(self, ipython, history):
'''Ensure that the last line is rerun
if the magic is called without arguments.'''
executing_recall = '\n'.join((
'=== Executing: ===',
history[3],
'=== Output: ===',
))
with tt.AssertPrints(executing_recall):
ipython.run_line_magic('rerun', '')
assert ipython.rl_next_input == history[0]

@staticmethod
@pytest.fixture
def increment_a(ipython):
'''Define a variable `a` and place a statement
incrementing that variable in the history.'''
ipython.history_manager.reset()
ipython.run_cell('a = 0', store_history=True)
ipython.run_cell('a += 1', store_history=True)

yield

ipython.user_ns.pop('a')

def test_can_rerun_reruns(self, ipython, increment_a):
'''Ensure that nothing prevents the user from rerunning
other calls to %rerun.'''
ipython.run_cell('%rerun -g a', store_history=True)

with tt.AssertNotPrints(self.error_not_found):
ipython.run_line_magic('rerun', '-g rerun')

assert ipython.user_ns['a'] == 3
57 changes: 57 additions & 0 deletions IPython/core/tests/test_magics/test_namespace.py
@@ -0,0 +1,57 @@
'''Tests of the magics from core.magics.namespace.'''
import math

import pytest

from IPython.testing import tools as tt


@pytest.fixture
def variables(ipython):
'''Populate the user namespace with variables of different types.
Returns a list of tuples (variable name, variable value, variable type).
The variables are removed from the namespace on teardown.'''
def named_function():
return 42

added_variables = [
('int1', 1, 'int'),
('int2', 5 + 6, 'int'),
('float1', 1.0, 'float'),
('float2', math.pi, 'float'),
('str1', "string", 'str'),
('str2', "", 'str'),
('function1', lambda: None, 'function'),
('function2', named_function, 'function'),
('list1', [], 'list'),
('list2', [1, 2, 3], 'list'),
('list3', [1, '2', None], 'list'),
('module', math, 'module'),
]

for var_name, var_value, _var_type in added_variables:
ipython.user_ns[var_name] = var_value

yield added_variables

for var_name, _var_value, _var_type in added_variables:
ipython.user_ns.pop(var_name)


class TestWhoLs:
'''Tests for the %who_ls line magic.'''

def test_no_args_no_vars(self, ipython):
'''No arguments passed to the magic, no variables in the namespace.'''
variables = ipython.run_line_magic('who_ls', '')
assert variables == []

def test_type_filtering(self, ipython, variables):
'''Variables of different types in the namespace, filtering based on a certain type.'''
ints_and_modules = sorted(var[0] for var in variables if var[2] in ('int', 'module'))
assert ipython.run_line_magic('who_ls', 'int module') == ints_and_modules

def test_all_vars(self, ipython, variables):
'''Variables of different types in the namespace, no filtering.'''
all_variables = sorted(var[0] for var in variables)
assert ipython.run_line_magic('who_ls', '') == all_variables
2 changes: 2 additions & 0 deletions pytest.ini
@@ -1,2 +1,4 @@
[pytest]
addopts = --durations=10
markers =
regression: regression tests