|
|
- """Notebook related utilities"""
-
- # Copyright (c) Jupyter Development Team.
- # Distributed under the terms of the Modified BSD License.
-
- from __future__ import print_function
-
- import ctypes
- import errno
- import os
- import stat
- import sys
- from distutils.version import LooseVersion
-
- try:
- from urllib.parse import quote, unquote, urlparse
- except ImportError:
- from urllib import quote, unquote
- from urlparse import urlparse
-
- from ipython_genutils import py3compat
-
- # UF_HIDDEN is a stat flag not defined in the stat module.
- # It is used by BSD to indicate hidden files.
- UF_HIDDEN = getattr(stat, 'UF_HIDDEN', 32768)
-
-
- def exists(path):
- """Replacement for `os.path.exists` which works for host mapped volumes
- on Windows containers
- """
- try:
- os.lstat(path)
- except OSError:
- return False
- return True
-
-
- def url_path_join(*pieces):
- """Join components of url into a relative url
-
- Use to prevent double slash when joining subpath. This will leave the
- initial and final / in place
- """
- initial = pieces[0].startswith('/')
- final = pieces[-1].endswith('/')
- stripped = [s.strip('/') for s in pieces]
- result = '/'.join(s for s in stripped if s)
- if initial: result = '/' + result
- if final: result = result + '/'
- if result == '//': result = '/'
- return result
-
- def url_is_absolute(url):
- """Determine whether a given URL is absolute"""
- return urlparse(url).path.startswith("/")
-
- def path2url(path):
- """Convert a local file path to a URL"""
- pieces = [ quote(p) for p in path.split(os.sep) ]
- # preserve trailing /
- if pieces[-1] == '':
- pieces[-1] = '/'
- url = url_path_join(*pieces)
- return url
-
- def url2path(url):
- """Convert a URL to a local file path"""
- pieces = [ unquote(p) for p in url.split('/') ]
- path = os.path.join(*pieces)
- return path
-
- def url_escape(path):
- """Escape special characters in a URL path
-
- Turns '/foo bar/' into '/foo%20bar/'
- """
- parts = py3compat.unicode_to_str(path, encoding='utf8').split('/')
- return u'/'.join([quote(p) for p in parts])
-
- def url_unescape(path):
- """Unescape special characters in a URL path
-
- Turns '/foo%20bar/' into '/foo bar/'
- """
- return u'/'.join([
- py3compat.str_to_unicode(unquote(p), encoding='utf8')
- for p in py3compat.unicode_to_str(path, encoding='utf8').split('/')
- ])
-
-
- def is_file_hidden_win(abs_path, stat_res=None):
- """Is a file hidden?
-
- This only checks the file itself; it should be called in combination with
- checking the directory containing the file.
-
- Use is_hidden() instead to check the file and its parent directories.
-
- Parameters
- ----------
- abs_path : unicode
- The absolute path to check.
- stat_res : os.stat_result, optional
- Ignored on Windows, exists for compatibility with POSIX version of the
- function.
- """
- if os.path.basename(abs_path).startswith('.'):
- return True
-
- win32_FILE_ATTRIBUTE_HIDDEN = 0x02
- try:
- attrs = ctypes.windll.kernel32.GetFileAttributesW(
- py3compat.cast_unicode(abs_path)
- )
- except AttributeError:
- pass
- else:
- if attrs > 0 and attrs & win32_FILE_ATTRIBUTE_HIDDEN:
- return True
-
- return False
-
- def is_file_hidden_posix(abs_path, stat_res=None):
- """Is a file hidden?
-
- This only checks the file itself; it should be called in combination with
- checking the directory containing the file.
-
- Use is_hidden() instead to check the file and its parent directories.
-
- Parameters
- ----------
- abs_path : unicode
- The absolute path to check.
- stat_res : os.stat_result, optional
- The result of calling stat() on abs_path. If not passed, this function
- will call stat() internally.
- """
- if os.path.basename(abs_path).startswith('.'):
- return True
-
- if stat_res is None or stat.S_ISLNK(stat_res.st_mode):
- try:
- stat_res = os.stat(abs_path)
- except OSError as e:
- if e.errno == errno.ENOENT:
- return False
- raise
-
- # check that dirs can be listed
- if stat.S_ISDIR(stat_res.st_mode):
- # use x-access, not actual listing, in case of slow/large listings
- if not os.access(abs_path, os.X_OK | os.R_OK):
- return True
-
- # check UF_HIDDEN
- if getattr(stat_res, 'st_flags', 0) & UF_HIDDEN:
- return True
-
- return False
-
- if sys.platform == 'win32':
- is_file_hidden = is_file_hidden_win
- else:
- is_file_hidden = is_file_hidden_posix
-
- def is_hidden(abs_path, abs_root=''):
- """Is a file hidden or contained in a hidden directory?
-
- This will start with the rightmost path element and work backwards to the
- given root to see if a path is hidden or in a hidden directory. Hidden is
- determined by either name starting with '.' or the UF_HIDDEN flag as
- reported by stat.
-
- If abs_path is the same directory as abs_root, it will be visible even if
- that is a hidden folder. This only checks the visibility of files
- and directories *within* abs_root.
-
- Parameters
- ----------
- abs_path : unicode
- The absolute path to check for hidden directories.
- abs_root : unicode
- The absolute path of the root directory in which hidden directories
- should be checked for.
- """
- if os.path.normpath(abs_path) == os.path.normpath(abs_root):
- return False
-
- if is_file_hidden(abs_path):
- return True
-
- if not abs_root:
- abs_root = abs_path.split(os.sep, 1)[0] + os.sep
- inside_root = abs_path[len(abs_root):]
- if any(part.startswith('.') for part in inside_root.split(os.sep)):
- return True
-
- # check UF_HIDDEN on any location up to root.
- # is_file_hidden() already checked the file, so start from its parent dir
- path = os.path.dirname(abs_path)
- while path and path.startswith(abs_root) and path != abs_root:
- if not exists(path):
- path = os.path.dirname(path)
- continue
- try:
- # may fail on Windows junctions
- st = os.lstat(path)
- except OSError:
- return True
- if getattr(st, 'st_flags', 0) & UF_HIDDEN:
- return True
- path = os.path.dirname(path)
-
- return False
-
- def samefile_simple(path, other_path):
- """
- Fill in for os.path.samefile when it is unavailable (Windows+py2).
-
- Do a case-insensitive string comparison in this case
- plus comparing the full stat result (including times)
- because Windows + py2 doesn't support the stat fields
- needed for identifying if it's the same file (st_ino, st_dev).
-
- Only to be used if os.path.samefile is not available.
-
- Parameters
- -----------
- path: String representing a path to a file
- other_path: String representing a path to another file
-
- Returns
- -----------
- same: Boolean that is True if both path and other path are the same
- """
- path_stat = os.stat(path)
- other_path_stat = os.stat(other_path)
- return (path.lower() == other_path.lower()
- and path_stat == other_path_stat)
-
-
- def to_os_path(path, root=''):
- """Convert an API path to a filesystem path
-
- If given, root will be prepended to the path.
- root must be a filesystem path already.
- """
- parts = path.strip('/').split('/')
- parts = [p for p in parts if p != ''] # remove duplicate splits
- path = os.path.join(root, *parts)
- return path
-
- def to_api_path(os_path, root=''):
- """Convert a filesystem path to an API path
-
- If given, root will be removed from the path.
- root must be a filesystem path already.
- """
- if os_path.startswith(root):
- os_path = os_path[len(root):]
- parts = os_path.strip(os.path.sep).split(os.path.sep)
- parts = [p for p in parts if p != ''] # remove duplicate splits
- path = '/'.join(parts)
- return path
-
-
- def check_version(v, check):
- """check version string v >= check
-
- If dev/prerelease tags result in TypeError for string-number comparison,
- it is assumed that the dependency is satisfied.
- Users on dev branches are responsible for keeping their own packages up to date.
- """
- try:
- return LooseVersion(v) >= LooseVersion(check)
- except TypeError:
- return True
-
-
- # Copy of IPython.utils.process.check_pid:
-
- def _check_pid_win32(pid):
- import ctypes
- # OpenProcess returns 0 if no such process (of ours) exists
- # positive int otherwise
- return bool(ctypes.windll.kernel32.OpenProcess(1,0,pid))
-
- def _check_pid_posix(pid):
- """Copy of IPython.utils.process.check_pid"""
- try:
- os.kill(pid, 0)
- except OSError as err:
- if err.errno == errno.ESRCH:
- return False
- elif err.errno == errno.EPERM:
- # Don't have permission to signal the process - probably means it exists
- return True
- raise
- else:
- return True
-
- if sys.platform == 'win32':
- check_pid = _check_pid_win32
- else:
- check_pid = _check_pid_posix
|