"""Test the Jupyter command-line""" import json import os import sys import sysconfig from subprocess import check_output, PIPE, CalledProcessError from unittest.mock import patch import pytest from jupyter_core import __version__ from jupyter_core.command import list_subcommands from jupyter_core.paths import ( jupyter_config_dir, jupyter_data_dir, jupyter_runtime_dir, jupyter_path, jupyter_config_path, ) resetenv = patch.dict(os.environ) def setup_module(): resetenv.start() os.environ.pop('JUPYTER_PREFER_ENV_PATH', None) def teardown_module(): resetenv.stop() def get_jupyter_output(cmd): """Get output of a jupyter command""" if not isinstance(cmd, list): cmd = [cmd] return check_output([sys.executable, '-m', 'jupyter_core'] + cmd, stderr=PIPE).decode('utf8').strip() def write_executable(path, source): if sys.platform == 'win32': script = path.dirpath() / path.purebasename + '-script.py' exe = path.dirpath() / path.purebasename + '.exe' else: script = path script.write(source) script.chmod(0o700) if sys.platform == 'win32': try: import pkg_resources w = pkg_resources.resource_string('setuptools', 'cli-32.exe') except (ImportError, FileNotFoundError): pytest.skip('Need pkg_resources/setuptools to make scripts executable on Windows') exe.write(w, 'wb') exe.chmod(0o700) def assert_output(cmd, expected): assert get_jupyter_output(cmd) == expected def test_config_dir(): assert_output('--config-dir', jupyter_config_dir()) def test_data_dir(): assert_output('--data-dir', jupyter_data_dir()) def test_runtime_dir(): assert_output('--runtime-dir', jupyter_runtime_dir()) def test_paths(): output = get_jupyter_output('--paths') for d in (jupyter_config_dir(), jupyter_data_dir(), jupyter_runtime_dir()): assert d in output for key in ('config', 'data', 'runtime'): assert ('%s:' % key) in output for path in (jupyter_config_path(), jupyter_path()): for d in path: assert d in output def test_paths_json(): output = get_jupyter_output(['--paths', '--json']) data = json.loads(output) assert sorted(data) == ['config', 'data', 'runtime'] for key, path in data.items(): assert isinstance(path, list) def test_paths_debug(): vars = [ 'JUPYTER_PREFER_ENV_PATH', 'JUPYTER_NO_CONFIG', 'JUPYTER_CONFIG_PATH', 'JUPYTER_CONFIG_DIR', 'JUPYTER_PATH', 'JUPYTER_DATA_DIR', 'JUPYTER_RUNTIME_DIR' ] output = get_jupyter_output(['--paths', '--debug']) for v in vars: assert f"{v} is not set" in output with patch.dict('os.environ', [(v, 'y') for v in vars]): output = get_jupyter_output(['--paths', '--debug']) for v in vars: assert f"{v} is set" in output def test_subcommand_not_given(): with pytest.raises(CalledProcessError): get_jupyter_output([]) def test_help(): output = get_jupyter_output('-h') assert '--help' in output def test_subcommand_not_found(): with pytest.raises(CalledProcessError) as excinfo: get_jupyter_output('nonexistant-subcommand') stderr = excinfo.value.stderr.decode('utf8') assert 'Jupyter command `jupyter-nonexistant-subcommand` not found.' in stderr @patch.object(sys, 'argv', [__file__] + sys.argv[1:]) def test_subcommand_list(tmpdir): a = tmpdir.mkdir("a") for cmd in ('jupyter-foo-bar', 'jupyter-xyz', 'jupyter-babel-fish'): a.join(cmd).write('') b = tmpdir.mkdir("b") for cmd in ('jupyter-foo', 'jupyterstuff', 'jupyter-yo-eyropa-ganymyde-callysto'): b.join(cmd).write('') c = tmpdir.mkdir("c") for cmd in ('jupyter-baz', 'jupyter-bop'): c.join(cmd).write('') path = os.pathsep.join(map(str, [a, b])) def get_path(dummy): return str(c) with patch.object(sysconfig, 'get_path', get_path): with patch.dict('os.environ', {'PATH': path}): subcommands = list_subcommands() assert subcommands == [ 'babel-fish', 'baz', 'bop', 'foo', 'xyz', 'yo-eyropa-ganymyde-callysto', ] def test_not_on_path(tmpdir): a = tmpdir.mkdir("a") jupyter = a.join('jupyter') jupyter.write( 'from jupyter_core import command; command.main()' ) jupyter.chmod(0o700) witness = a.join('jupyter-witness') witness_src = '#!%s\n%s\n' % (sys.executable, 'print("WITNESS ME")') write_executable(witness, witness_src) env = {'PATH': ''} if 'SYSTEMROOT' in os.environ: # Windows http://bugs.python.org/issue20614 env[str('SYSTEMROOT')] = os.environ['SYSTEMROOT'] if sys.platform == 'win32': env[str('PATHEXT')] = '.EXE' # This won't work on windows unless out = check_output([sys.executable, str(jupyter), 'witness'], env=env) assert b'WITNESS' in out def test_path_priority(tmpdir): a = tmpdir.mkdir("a") jupyter = a.join('jupyter') jupyter.write( 'from jupyter_core import command; command.main()' ) jupyter.chmod(0o700) witness_a = a.join('jupyter-witness') witness_a_src = '#!%s\n%s\n' % (sys.executable, 'print("WITNESS A")') write_executable(witness_a, witness_a_src) b = tmpdir.mkdir("b") witness_b = b.join('jupyter-witness') witness_b_src = '#!%s\n%s\n' % (sys.executable, 'print("WITNESS B")') write_executable(witness_b, witness_b_src) env = {'PATH': str(b)} if 'SYSTEMROOT' in os.environ: # Windows http://bugs.python.org/issue20614 env[str('SYSTEMROOT')] = os.environ['SYSTEMROOT'] if sys.platform == 'win32': env[str('PATHEXT')] = '.EXE' out = check_output([sys.executable, str(jupyter), 'witness'], env=env) assert b'WITNESS A' in out def test_argv0(tmpdir): a = tmpdir.mkdir("a") jupyter = a.join('jupyter') jupyter.write( 'from jupyter_core import command; command.main()' ) jupyter.chmod(0o700) witness_a = a.join('jupyter-witness') witness_a_src = f'''#!{sys.executable} import sys print(sys.argv[0]) ''' write_executable(witness_a, witness_a_src) env = {} if 'SYSTEMROOT' in os.environ: # Windows http://bugs.python.org/issue20614 env[str('SYSTEMROOT')] = os.environ['SYSTEMROOT'] if sys.platform == 'win32': env[str('PATHEXT')] = '.EXE' out = check_output([sys.executable, str(jupyter), 'witness'], env=env) # Make sure the first argv is the full path to the executing script assert f'{jupyter}-witness'.encode() in out