You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
307 lines
8.9 KiB
307 lines
8.9 KiB
"""Tests for paths"""
|
|
|
|
# Copyright (c) Jupyter Development Team.
|
|
# Distributed under the terms of the Modified BSD License.
|
|
|
|
import os
|
|
import re
|
|
import stat
|
|
import shutil
|
|
import tempfile
|
|
|
|
try:
|
|
from unittest.mock import patch
|
|
except ImportError:
|
|
from mock import patch # py2
|
|
|
|
from jupyter_core import paths
|
|
from jupyter_core.paths import (
|
|
jupyter_config_dir, jupyter_data_dir, jupyter_runtime_dir,
|
|
jupyter_path, ENV_JUPYTER_PATH,
|
|
secure_write, is_hidden, is_file_hidden
|
|
)
|
|
from ipython_genutils.tempdir import TemporaryDirectory
|
|
from ipython_genutils.py3compat import cast_unicode
|
|
from ipython_genutils.testing.decorators import skip_if_not_win32, skip_win32
|
|
from .mocking import darwin, windows, linux
|
|
|
|
pjoin = os.path.join
|
|
|
|
|
|
xdg_env = {
|
|
'XDG_CONFIG_HOME': '/tmp/xdg/config',
|
|
'XDG_DATA_HOME': '/tmp/xdg/data',
|
|
'XDG_RUNTIME_DIR': '/tmp/xdg/runtime',
|
|
}
|
|
xdg = patch.dict('os.environ', xdg_env)
|
|
no_xdg = patch.dict('os.environ', {
|
|
'XDG_CONFIG_HOME': '',
|
|
'XDG_DATA_HOME': '',
|
|
'XDG_RUNTIME_DIR': '',
|
|
})
|
|
|
|
appdata = patch.dict('os.environ', {'APPDATA': 'appdata'})
|
|
|
|
no_config_env = patch.dict('os.environ', {
|
|
'JUPYTER_CONFIG_DIR': '',
|
|
'JUPYTER_DATA_DIR': '',
|
|
'JUPYTER_RUNTIME_DIR': '',
|
|
'JUPYTER_PATH': '',
|
|
})
|
|
|
|
jupyter_config_env = '/jupyter-cfg'
|
|
config_env = patch.dict('os.environ', {'JUPYTER_CONFIG_DIR': jupyter_config_env})
|
|
|
|
|
|
def realpath(path):
|
|
return os.path.realpath(os.path.expanduser(path))
|
|
|
|
home_jupyter = realpath('~/.jupyter')
|
|
|
|
|
|
def test_config_dir_darwin():
|
|
with darwin, no_config_env:
|
|
config = jupyter_config_dir()
|
|
assert config == home_jupyter
|
|
|
|
with darwin, config_env:
|
|
config = jupyter_config_dir()
|
|
assert config == jupyter_config_env
|
|
|
|
|
|
def test_config_dir_windows():
|
|
with windows, no_config_env:
|
|
config = jupyter_config_dir()
|
|
assert config == home_jupyter
|
|
|
|
with windows, config_env:
|
|
config = jupyter_config_dir()
|
|
assert config == jupyter_config_env
|
|
|
|
|
|
def test_config_dir_linux():
|
|
with windows, no_config_env:
|
|
config = jupyter_config_dir()
|
|
assert config == home_jupyter
|
|
|
|
with windows, config_env:
|
|
config = jupyter_config_dir()
|
|
assert config == jupyter_config_env
|
|
|
|
|
|
def test_data_dir_env():
|
|
data_env = 'runtime-dir'
|
|
with patch.dict('os.environ', {'JUPYTER_DATA_DIR': data_env}):
|
|
data = jupyter_data_dir()
|
|
assert data == data_env
|
|
|
|
|
|
def test_data_dir_darwin():
|
|
with darwin:
|
|
data = jupyter_data_dir()
|
|
assert data == realpath('~/Library/Jupyter')
|
|
|
|
with darwin, xdg:
|
|
# darwin should ignore xdg
|
|
data = jupyter_data_dir()
|
|
assert data == realpath('~/Library/Jupyter')
|
|
|
|
|
|
def test_data_dir_windows():
|
|
with windows, appdata:
|
|
data = jupyter_data_dir()
|
|
assert data == pjoin('appdata', 'jupyter')
|
|
|
|
with windows, appdata, xdg:
|
|
# windows should ignore xdg
|
|
data = jupyter_data_dir()
|
|
assert data == pjoin('appdata', 'jupyter')
|
|
|
|
|
|
def test_data_dir_linux():
|
|
with linux, no_xdg:
|
|
data = jupyter_data_dir()
|
|
assert data == realpath('~/.local/share/jupyter')
|
|
|
|
with linux, xdg:
|
|
data = jupyter_data_dir()
|
|
assert data == pjoin(xdg_env['XDG_DATA_HOME'], 'jupyter')
|
|
|
|
|
|
def test_runtime_dir_env():
|
|
rtd_env = 'runtime-dir'
|
|
with patch.dict('os.environ', {'JUPYTER_RUNTIME_DIR': rtd_env}):
|
|
runtime = jupyter_runtime_dir()
|
|
assert runtime == rtd_env
|
|
|
|
|
|
def test_runtime_dir_darwin():
|
|
with darwin:
|
|
runtime = jupyter_runtime_dir()
|
|
assert runtime == realpath('~/Library/Jupyter/runtime')
|
|
|
|
with darwin, xdg:
|
|
# darwin should ignore xdg
|
|
runtime = jupyter_runtime_dir()
|
|
assert runtime == realpath('~/Library/Jupyter/runtime')
|
|
|
|
|
|
def test_runtime_dir_windows():
|
|
with windows, appdata:
|
|
runtime = jupyter_runtime_dir()
|
|
assert runtime == pjoin('appdata', 'jupyter', 'runtime')
|
|
|
|
with windows, appdata, xdg:
|
|
# windows should ignore xdg
|
|
runtime = jupyter_runtime_dir()
|
|
assert runtime == pjoin('appdata', 'jupyter', 'runtime')
|
|
|
|
|
|
def test_runtime_dir_linux():
|
|
with linux, no_xdg:
|
|
runtime = jupyter_runtime_dir()
|
|
assert runtime == realpath('~/.local/share/jupyter/runtime')
|
|
|
|
with linux, xdg:
|
|
runtime = jupyter_runtime_dir()
|
|
assert runtime == pjoin(xdg_env['XDG_DATA_HOME'], 'jupyter', 'runtime')
|
|
|
|
|
|
def test_jupyter_path():
|
|
system_path = ['system', 'path']
|
|
with no_config_env, patch.object(paths, 'SYSTEM_JUPYTER_PATH', system_path):
|
|
path = jupyter_path()
|
|
assert path[0] == jupyter_data_dir()
|
|
assert path[-2:] == system_path
|
|
|
|
|
|
def test_jupyter_path_env():
|
|
path_env = os.pathsep.join([
|
|
pjoin('foo', 'bar'),
|
|
pjoin('bar', 'baz', ''), # trailing /
|
|
])
|
|
|
|
with patch.dict('os.environ', {'JUPYTER_PATH': path_env}):
|
|
path = jupyter_path()
|
|
assert path[:2] == [pjoin('foo', 'bar'), pjoin('bar', 'baz')]
|
|
|
|
|
|
def test_jupyter_path_sys_prefix():
|
|
with patch.object(paths, 'ENV_JUPYTER_PATH', ['sys_prefix']):
|
|
path = jupyter_path()
|
|
assert 'sys_prefix' in path
|
|
|
|
|
|
def test_jupyter_path_subdir():
|
|
path = jupyter_path('sub1', 'sub2')
|
|
for p in path:
|
|
assert p.endswith(pjoin('', 'sub1', 'sub2'))
|
|
|
|
|
|
def test_is_hidden():
|
|
with TemporaryDirectory() as root:
|
|
subdir1 = os.path.join(root, 'subdir')
|
|
os.makedirs(subdir1)
|
|
assert not is_hidden(subdir1, root)
|
|
assert not is_file_hidden(subdir1)
|
|
|
|
subdir2 = os.path.join(root, '.subdir2')
|
|
os.makedirs(subdir2)
|
|
assert is_hidden(subdir2, root)
|
|
assert is_file_hidden(subdir2)
|
|
# root dir is always visible
|
|
assert not is_hidden(subdir2, subdir2)
|
|
|
|
subdir34 = os.path.join(root, 'subdir3', '.subdir4')
|
|
os.makedirs(subdir34)
|
|
assert is_hidden(subdir34, root)
|
|
assert is_hidden(subdir34)
|
|
|
|
subdir56 = os.path.join(root, '.subdir5', 'subdir6')
|
|
os.makedirs(subdir56)
|
|
assert is_hidden(subdir56, root)
|
|
assert is_hidden(subdir56)
|
|
assert not is_file_hidden(subdir56)
|
|
assert not is_file_hidden(subdir56, os.stat(subdir56))
|
|
|
|
|
|
@skip_if_not_win32
|
|
def test_is_hidden_win32():
|
|
import ctypes
|
|
with TemporaryDirectory() as root:
|
|
root = cast_unicode(root)
|
|
subdir1 = os.path.join(root, u'subdir')
|
|
os.makedirs(subdir1)
|
|
assert not is_hidden(subdir1, root)
|
|
r = ctypes.windll.kernel32.SetFileAttributesW(subdir1, 0x02)
|
|
print(r) # Helps debugging
|
|
assert is_hidden(subdir1, root)
|
|
assert is_file_hidden(subdir1)
|
|
|
|
|
|
@skip_if_not_win32
|
|
def test_secure_write_win32():
|
|
def fetch_win32_permissions(filename):
|
|
'''Extracts file permissions on windows using icacls'''
|
|
role_permissions = {}
|
|
for index, line in enumerate(os.popen("icacls %s" % filename).read().splitlines()):
|
|
if index == 0:
|
|
line = line.split(filename)[-1].strip().lower()
|
|
match = re.match(r'\s*([^:]+):\(([^\)]*)\)', line)
|
|
if match:
|
|
usergroup, permissions = match.groups()
|
|
usergroup = usergroup.lower().split('\\')[-1]
|
|
permissions = set(p.lower() for p in permissions.split(','))
|
|
role_permissions[usergroup] = permissions
|
|
elif not line.strip():
|
|
break
|
|
return role_permissions
|
|
|
|
def check_user_only_permissions(fname):
|
|
# Windows has it's own permissions ACL patterns
|
|
import win32api
|
|
username = win32api.GetUserName().lower()
|
|
permissions = fetch_win32_permissions(fname)
|
|
print(permissions) # for easier debugging
|
|
assert username in permissions
|
|
assert permissions[username] == set(['r', 'w'])
|
|
assert 'administrators' in permissions
|
|
assert permissions['administrators'] == set(['f'])
|
|
assert 'everyone' not in permissions
|
|
assert len(permissions) == 2
|
|
|
|
directory = tempfile.mkdtemp()
|
|
fname = os.path.join(directory, 'check_perms')
|
|
try:
|
|
with secure_write(fname) as f:
|
|
f.write('test 1')
|
|
check_user_only_permissions(fname)
|
|
with open(fname, 'r') as f:
|
|
assert f.read() == 'test 1'
|
|
finally:
|
|
shutil.rmtree(directory)
|
|
|
|
|
|
@skip_win32
|
|
def test_secure_write_unix():
|
|
directory = tempfile.mkdtemp()
|
|
fname = os.path.join(directory, 'check_perms')
|
|
try:
|
|
with secure_write(fname) as f:
|
|
f.write('test 1')
|
|
mode = os.stat(fname).st_mode
|
|
assert 0o0600 == (stat.S_IMODE(mode) & 0o7677) # tolerate owner-execute bit
|
|
with open(fname, 'r') as f:
|
|
assert f.read() == 'test 1'
|
|
|
|
# Try changing file permissions ahead of time
|
|
os.chmod(fname, 0o755)
|
|
with secure_write(fname) as f:
|
|
f.write('test 2')
|
|
mode = os.stat(fname).st_mode
|
|
assert 0o0600 == (stat.S_IMODE(mode) & 0o7677) # tolerate owner-execute bit
|
|
with open(fname, 'r') as f:
|
|
assert f.read() == 'test 2'
|
|
finally:
|
|
shutil.rmtree(directory)
|