File: test_paths.py

package info (click to toggle)
jupyter-core 4.7.1-1%2Bdeb11u1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 612 kB
  • sloc: python: 1,606; makefile: 167; sh: 77; javascript: 1
file content (330 lines) | stat: -rw-r--r-- 10,242 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
"""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
from unittest.mock import patch
import pytest
import sys

from jupyter_core import paths
from jupyter_core.paths import (
    jupyter_config_dir, jupyter_data_dir, jupyter_runtime_dir,
    jupyter_path, jupyter_config_path, ENV_JUPYTER_PATH,
    secure_write, is_hidden, is_file_hidden
)

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_envset():
    true_values = ['', 'True', 'on', 'yes', 'Y', '1', 'anything']
    false_values = ['n', 'No', 'N', 'fAlSE', '0', '0.0', 'Off']
    with patch.dict('os.environ', ((f"FOO_{v}", v) for v in true_values + false_values)):
        for v in true_values:
            assert paths.envset(f"FOO_{v}")
        for v in false_values:
            assert not paths.envset(f"FOO_{v}")
        assert not paths.envset("THIS_VARIABLE_SHOULD_NOT_BE_SET")

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_jupyter_config_path():
    path = jupyter_config_path()
    assert path[0] == jupyter_config_dir()
    assert path[1] == paths.ENV_CONFIG_PATH[0]

def test_jupyter_config_path_prefer_env():
    with patch.dict('os.environ', {'JUPYTER_PREFER_ENV_PATH': 'true'}):
        path = jupyter_config_path()
    assert path[0] == paths.ENV_CONFIG_PATH[0]
    assert path[1] == jupyter_config_dir()

def test_jupyter_config_path_env():
    path_env = os.pathsep.join([
        pjoin('foo', 'bar'),
        pjoin('bar', 'baz', ''), # trailing /
    ])

    with patch.dict('os.environ', {'JUPYTER_CONFIG_PATH': path_env}):
        path = jupyter_config_path()
    assert path[:2] == [pjoin('foo', 'bar'), pjoin('bar', 'baz')]

def test_is_hidden():
    with tempfile.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))


@pytest.mark.skipif(sys.platform != "win32", reason="only run on windows")
def test_is_hidden_win32():
    import ctypes
    with tempfile.TemporaryDirectory() as root:
        subdir1 = os.path.join(root, '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)


@pytest.mark.skipif(sys.platform != "win32", reason="only runs on windows")
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', 'd'])
        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)


@pytest.mark.skipif(sys.platform == "win32", reason="does not run on windows")
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)