Source code for browsepy.compat

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import os
import os.path
import sys
import itertools

import warnings
import functools

import posixpath
import ntpath

FS_ENCODING = sys.getfilesystemencoding()
PY_LEGACY = sys.version_info < (3, )
TRUE_VALUES = frozenset(('true', 'yes', '1', 'enable', 'enabled', True, 1))

try:
    from os import scandir, walk
except ImportError:
    from scandir import scandir, walk  # noqa

try:
    from shutil import get_terminal_size
except ImportError:
    from backports.shutil_get_terminal_size import get_terminal_size  # noqa


[docs]def isexec(path): ''' Check if given path points to an executable file. :param path: file path :type path: str :return: True if executable, False otherwise :rtype: bool ''' return os.path.isfile(path) and os.access(path, os.X_OK)
[docs]def fsdecode(path, os_name=os.name, fs_encoding=FS_ENCODING, errors=None): ''' Decode given path. :param path: path will be decoded if using bytes :type path: bytes or str :param os_name: operative system name, defaults to os.name :type os_name: str :param fs_encoding: current filesystem encoding, defaults to autodetected :type fs_encoding: str :return: decoded path :rtype: str ''' if not isinstance(path, bytes): return path if not errors: use_strict = PY_LEGACY or os_name == 'nt' errors = 'strict' if use_strict else 'surrogateescape' return path.decode(fs_encoding, errors=errors)
[docs]def fsencode(path, os_name=os.name, fs_encoding=FS_ENCODING, errors=None): ''' Encode given path. :param path: path will be encoded if not using bytes :type path: bytes or str :param os_name: operative system name, defaults to os.name :type os_name: str :param fs_encoding: current filesystem encoding, defaults to autodetected :type fs_encoding: str :return: encoded path :rtype: bytes ''' if isinstance(path, bytes): return path if not errors: use_strict = PY_LEGACY or os_name == 'nt' errors = 'strict' if use_strict else 'surrogateescape' return path.encode(fs_encoding, errors=errors)
[docs]def getcwd(fs_encoding=FS_ENCODING, cwd_fnc=os.getcwd): ''' Get current work directory's absolute path. Like os.getcwd but garanteed to return an unicode-str object. :param fs_encoding: filesystem encoding, defaults to autodetected :type fs_encoding: str :param cwd_fnc: callable used to get the path, defaults to os.getcwd :type cwd_fnc: Callable :return: path :rtype: str ''' path = fsdecode(cwd_fnc(), fs_encoding=fs_encoding) return os.path.abspath(path)
[docs]def getdebug(environ=os.environ, true_values=TRUE_VALUES): ''' Get if app is expected to be ran in debug mode looking at environment variables. :param environ: environment dict-like object :type environ: collections.abc.Mapping :returns: True if debug contains a true-like string, False otherwise :rtype: bool ''' return environ.get('DEBUG', '').lower() in true_values
[docs]def deprecated(func_or_text, environ=os.environ): ''' Decorator used to mark functions as deprecated. It will result in a warning being emmitted hen the function is called. Usage: >>> @deprecated ... def fnc(): ... pass Usage (custom message): >>> @deprecated('This is deprecated') ... def fnc(): ... pass :param func_or_text: message or callable to decorate :type func_or_text: callable :param environ: optional environment mapping :type environ: collections.abc.Mapping :returns: nested decorator or new decorated function (depending on params) :rtype: callable ''' def inner(func): message = ( 'Deprecated function {}.'.format(func.__name__) if callable(func_or_text) else func_or_text ) @functools.wraps(func) def new_func(*args, **kwargs): with warnings.catch_warnings(): if getdebug(environ): warnings.simplefilter('always', DeprecationWarning) warnings.warn(message, category=DeprecationWarning, stacklevel=3) return func(*args, **kwargs) return new_func return inner(func_or_text) if callable(func_or_text) else inner
[docs]def usedoc(other): ''' Decorator which copies __doc__ of given object into decorated one. Usage: >>> def fnc1(): ... """docstring""" ... pass >>> @usedoc(fnc1) ... def fnc2(): ... pass >>> fnc2.__doc__ 'docstring'collections.abc.D :param other: anything with a __doc__ attribute :type other: any :returns: decorator function :rtype: callable ''' def inner(fnc): fnc.__doc__ = fnc.__doc__ or getattr(other, '__doc__') return fnc return inner
[docs]def pathsplit(value, sep=os.pathsep): ''' Get enviroment PATH elements as list. This function only cares about spliting across OSes. :param value: path string, as given by os.environ['PATH'] :type value: str :param sep: PATH separator, defaults to os.pathsep :type sep: str :yields: every path :ytype: str ''' for part in value.split(sep): if part[:1] == part[-1:] == '"' or part[:1] == part[-1:] == '\'': part = part[1:-1] yield part
[docs]def pathparse(value, sep=os.pathsep, os_sep=os.sep): ''' Get enviroment PATH directories as list. This function cares about spliting, escapes and normalization of paths across OSes. :param value: path string, as given by os.environ['PATH'] :type value: str :param sep: PATH separator, defaults to os.pathsep :type sep: str :param os_sep: OS filesystem path separator, defaults to os.sep :type os_sep: str :yields: every path :ytype: str ''' escapes = [] normpath = ntpath.normpath if os_sep == '\\' else posixpath.normpath if '\\' not in (os_sep, sep): escapes.extend(( ('\\\\', '<ESCAPE-ESCAPE>', '\\'), ('\\"', '<ESCAPE-DQUOTE>', '"'), ('\\\'', '<ESCAPE-SQUOTE>', '\''), ('\\%s' % sep, '<ESCAPE-PATHSEP>', sep), )) for original, escape, unescape in escapes: value = value.replace(original, escape) for part in pathsplit(value, sep=sep): if part[-1:] == os_sep and part != os_sep: part = part[:-1] for original, escape, unescape in escapes: part = part.replace(escape, unescape) yield normpath(fsdecode(part))
[docs]def pathconf(path, os_name=os.name, isdir_fnc=os.path.isdir, pathconf_fnc=getattr(os, 'pathconf', None), pathconf_names=getattr(os, 'pathconf_names', ())): ''' Get all pathconf variables for given path. :param path: absolute fs path :type path: str :returns: dictionary containing pathconf keys and their values (both str) :rtype: dict ''' if pathconf_fnc and pathconf_names: return {key: pathconf_fnc(path, key) for key in pathconf_names} if os_name == 'nt': maxpath = 246 if isdir_fnc(path) else 259 # 260 minus <END> else: maxpath = 255 # conservative sane default return { 'PC_PATH_MAX': maxpath, 'PC_NAME_MAX': maxpath - len(path), }
ENV_PATH = tuple(pathparse(os.getenv('PATH', ''))) ENV_PATHEXT = tuple(pathsplit(os.getenv('PATHEXT', '')))
[docs]def which(name, env_path=ENV_PATH, env_path_ext=ENV_PATHEXT, is_executable_fnc=isexec, path_join_fnc=os.path.join, os_name=os.name): ''' Get command absolute path. :param name: name of executable command :type name: str :param env_path: OS environment executable paths, defaults to autodetected :type env_path: list of str :param is_executable_fnc: callable will be used to detect if path is executable, defaults to `isexec` :type is_executable_fnc: Callable :param path_join_fnc: callable will be used to join path components :type path_join_fnc: Callable :param os_name: os name, defaults to os.name :type os_name: str :return: absolute path :rtype: str or None ''' for path in env_path: for suffix in env_path_ext: exe_file = path_join_fnc(path, name) + suffix if is_executable_fnc(exe_file): return exe_file return None
[docs]def re_escape(pattern, chars=frozenset("()[]{}?*+|^$\\.-#")): ''' Escape all special regex characters in pattern. Logic taken from regex module. :param pattern: regex pattern to escape :type patterm: str :returns: escaped pattern :rtype: str ''' escape = '\\{}'.format return ''.join( escape(c) if c in chars or c.isspace() else '\\000' if c == '\x00' else c for c in pattern )
if PY_LEGACY: FileNotFoundError = OSError # noqa range = xrange # noqa filter = itertools.ifilter basestring = basestring # noqa unicode = unicode # noqa chr = unichr # noqa bytes = str # noqa else: FileNotFoundError = FileNotFoundError range = range filter = filter basestring = str unicode = str chr = chr bytes = bytes