Beispiel #1
0
def get_dirs(*args: Any, pkgname: OptStr = None, **kwds: Any) -> StrDict:
    """Get application specific environmental directories.

    This function returns application specific system directories by platform
    independent names to allow platform independent storage for caching,
    logging, configuration and permanent data storage.

    Args:
        *args: Optional arguments that specify the application, as required by
            the function '.env.update_dirs'.
        **kwds: Optional keyword arguments that specify the application, as
            required by the function '.env.update_dirs'.

    Returns:
        Dictionary containing paths of application specific environmental
        directories.

    """
    # Get package name from callers top level module
    pkgname = pkgname or pkg.get_root_name(stack.get_caller_module_name(-2))

    # Update appdirs if not present or if optional arguments are given
    try:
        return globals()['cache'][pkgname]['dirs'].copy()
    except KeyError:
        update_dirs(*args, pkgname=pkgname, **kwds)
    return globals()['cache'][pkgname]['dirs'].copy()
Beispiel #2
0
def copytree(source: NestPath,
             target: NestPath,
             pkgname: OptStr = None) -> None:
    """Copy directory structure from given source to target directory.

    Args:
        source: Path like structure, which comprises the path of a source folder
        target: Path like structure, which comprises the path of a destination
            folder

    Returns:
        True if the operation was successful.

    """
    # Get package name from callers top level module
    pkgname = pkgname or pkg.get_root_name(stack.get_caller_module_name(-2))

    # Recursive copy function, that allows existing files
    def copy(source: pathlib.Path, target: pathlib.Path) -> None:
        if source.is_dir():
            if not target.is_dir():
                target.mkdir()
            for each in source.glob('*'):
                copy(each, target / each.name)
        else:
            shutil.copy(source, target)

    copy(expand(source, pkgname=pkgname), expand(target, pkgname=pkgname))
Beispiel #3
0
def get_vars(*args: Any, pkgname: OptStr = None, **kwds: Any) -> StrDict:
    """Get dictionary with environment and application variables.

    Environment variables comprise static and runtime properties of the
    operating system like 'username' or 'hostname'. Application variables in
    turn, are intended to describe the application distribution by authorship
    information, bibliographic information, status, formal conditions and notes
    or warnings. For mor information see :PEP:`345`.

    Args:
        *args: Optional arguments that specify the application, as required by
            :func:`~hup.base.env.update_vars`.
        **kwds: Optional keyword arguments that specify the application, as
            required by :func:`~hup.base.env.update_vars`.

    Returns:
        Dictionary containing application variables.

    """
    # Get package name from callers top level module
    pkgname = pkgname or pkg.get_root_name(stack.get_caller_module_name(-2))

    # Update variables if not present or if optional arguments are given
    try:
        return globals()['cache'][pkgname]['vars'].copy()
    except KeyError:
        update_vars(*args, pkgname=pkgname, **kwds)
    return globals()['cache'][pkgname]['vars'].copy()
Beispiel #4
0
def get_module(name: OptStr = None, errors: bool = True) -> OptModule:
    """Get reference to a module instance.

    Args:
        name: Optional name of module- If provided, the name is required to be
            a fully qualified name. By default a refrence to the module of the
            current caller is returned.
        errors: Boolean value which determines if an error is raised, if the
            module could not be found. By default errors are raised.

    Returns:
        Module reference or None, if the name does not point to a valid module.

    """
    # Set default values
    name = name or stack.get_caller_module_name(-1)

    # Try to import a module with importlib
    try:
        return importlib.import_module(name)
    except ModuleNotFoundError:
        if errors:
            raise
        return None
    except ImportError:
        if errors:
            raise
        return None
Beispiel #5
0
def get_dir(dirname: str,
            *args: Any,
            pkgname: OptStr = None,
            **kwds: Any) -> pathlib.Path:
    """Get application specific environmental directory by name.

    This function returns application specific system directories by platform
    independent names to allow platform independent storage for caching,
    logging, configuration and permanent data storage.

    Args:
        dirname: Environmental directory name. Allowed values are:

            :user_cache_dir: Cache directory of user
            :user_config_dir: Configuration directory of user
            :user_data_dir: Data directory of user
            :user_log_dir: Logging directory of user
            :site_config_dir: Site global configuration directory
            :site_data_dir: Site global data directory
            :site_package_dir: Site global package directory
            :site_temp_dir: Site global directory for temporary files
            :package_dir: Current package directory
            :package_data_dir: Current package data directory
        *args: Optional arguments that specify the application, as required by
            the function '.env.update_dirs'.
        **kwds: Optional keyword arguments that specify the application, as
            required by the function '.env.update_dirs'.

    Returns:
        String containing path of environmental directory or None if the
        pathname is not supported.

    """
    # Get package name from callers top level module
    pkgname = pkgname or pkg.get_root_name(stack.get_caller_module_name(-2))

    # Check type of 'dirname'
    check.has_type("'dirname'", dirname, str)

    # Update derectories if not present or if any optional arguments are given
    try:
        dirs = globals()['cache'][pkgname]['dirs']
    except KeyError:
        update_dirs(*args, pkgname=pkgname, **kwds)
    dirs = globals()['cache'][pkgname]['dirs']

    # Check value of 'dirname'
    if dirname not in dirs:
        raise ValueError(f"directory name '{dirname}' is not valid")

    return dirs[dirname]
Beispiel #6
0
def touch(path: NestPath,
          parents: bool = True,
          mode: int = 0o666,
          exist_ok: bool = True,
          pkgname: OptStr = None) -> bool:
    """Create an empty file at the specified path.

    Args:
        path: Nested :term:`path-like object`, which represents a valid filename
            in the directory structure of the operating system.
        parents: Boolean value, which determines if missing parents of the path
            are created as needed.
        mode: Integer value, which specifies the properties if the file. For
            more information see :func:`os.chmod`.
        exist_ok: Boolean value which determines, if the function returns False,
            if the file already exists.

    Returns:
        True if the file could be created, else False.

    """
    # Get package name from callers top level module
    pkgname = pkgname or pkg.get_root_name(stack.get_caller_module_name(-2))

    filepath = expand(path, pkgname=pkgname)

    # Check type of 'filepath'
    if not isinstance(filepath, pathlib.Path):
        return False

    # Check if directory exists and optionally create it
    dirpath = filepath.parent
    if not dirpath.is_dir():
        if not parents:
            return False
        dirpath.mkdir(parents=True, exist_ok=True)
        if not dirpath.is_dir():
            return False

    # Check if file already exsists
    if filepath.is_file() and not exist_ok:
        return False

    # Touch file with given
    filepath.touch(mode=mode, exist_ok=exist_ok)

    return filepath.is_file()
Beispiel #7
0
def is_file(path: NestPath, pkgname: OptStr = None) -> bool:
    """Determine if given path points to a file.

    Extends :meth:`pathlib.Path.is_file` by nested paths and path variable
    expansion.

    Args:
        path: Path like structure, which is expandable to a valid path.

    Returns:
        True if the path points to a directory (or a symbolic link pointing
        to a directory), False if it points to another kind of file.

    """
    # Get package name from callers top level module
    pkgname = pkgname or pkg.get_root_name(stack.get_caller_module_name(-2))

    return expand(path, pkgname=pkgname).is_file()
Beispiel #8
0
def rmdir(*args: NestPath, pkgname: OptStr = None) -> bool:
    """Remove directory.

    Args:
        *args: Path like structure, which identifies the path of a directory

    Returns:
        True if the directory could be deleted

    """
    # Get package name from callers top level module
    pkgname = pkgname or pkg.get_root_name(stack.get_caller_module_name(-2))

    path = expand(*args, pkgname=pkgname)

    if not path.is_dir():
        return False
    shutil.rmtree(str(path), ignore_errors=True)

    return not path.exists()
Beispiel #9
0
def fileext(*args: NestPath, pkgname: OptStr = None) -> str:
    """Fileextension of file.

    Args:
        *args: Path like arguments, respectively given by a tree of strings,
            which can be joined to a path.

    Returns:
        String containing fileextension of file.

    Examples:
        >>> fileext(('a', ('b', 'c')), 'base.ext')
        'ext'

    """
    # Get package name from callers top level module
    pkgname = pkgname or pkg.get_root_name(stack.get_caller_module_name(-2))

    path = expand(*args, pkgname=pkgname)
    if path.is_dir():
        return ''
    return str(path.suffix).lstrip('.')
Beispiel #10
0
def filename(*args: NestPath, pkgname: OptStr = None) -> str:
    """Extract file name from a path like structure.

    Args:
        *args: Path like arguments, respectively given by a tree of strings,
            which can be joined to a path.

    Returns:
        String containing normalized directory path of file.

    Examples:
        >>> filename(('a', ('b', 'c')), 'base.ext')
        'base.ext'

    """
    # Get package name from callers top level module
    pkgname = pkgname or pkg.get_root_name(stack.get_caller_module_name(-2))

    path = expand(*args, pkgname=pkgname)
    if path.is_dir():
        return ''
    return str(path.name)
Beispiel #11
0
def update_vars(filepath: OptPathLike = None, pkgname: OptStr = None) -> None:
    """Update environment and application variables.

    Environment variables comprise static and runtime properties of the
    operating system like 'username' or 'hostname'. Application variables in
    turn, are intended to describe the application distribution by authorship
    information, bibliographic information, status, formal conditions and notes
    or warnings. For mor information see :PEP:`345`.

    Args:
        filepath: Valid filepath to python module, that contains the application
            variables as module attributes. By default the current top level
            module is used.

    """
    # Get package specific environment variables by parsing a given file for
    # module attributes. By default the file of the callers current top level
    # module is taken. If the name is not given, then use the name of the
    # current top level module.
    pkgname = pkgname or pkg.get_root_name(stack.get_caller_module_name(-2))
    filepath = filepath or pkg.get_module(pkgname).__file__
    text = pathlib.Path(filepath).read_text()
    pattern = r"^[ ]*__([^\d\W]\w*)__[ ]*=[ ]*['\"]([^'\"]*)['\"]"
    matches = re.finditer(pattern, text, re.M)
    info = {str(m.group(1)): str(m.group(2)) for m in matches}
    info['name'] = info.get('name', pkgname)

    # Get plattform specific environment variables
    info['encoding'] = get_encoding()
    info['hostname'] = get_hostname()
    info['osname'] = get_osname()
    info['username'] = get_username()

    # Update globals
    if not 'cache' in globals():
        globals()['cache'] = {}
    if not pkgname in globals()['cache']:
        globals()['cache'][pkgname] = {}
    globals()['cache'][pkgname]['vars'] = info
Beispiel #12
0
def mkdir(*args: NestPath, pkgname: OptStr = None) -> bool:
    """Create directory.

    Args:
        *args: Path like structure, which comprises the path of a new directory

    Returns:
        True if the directory already exists, or the operation was successful.

    """
    # Get package name from callers top level module
    pkgname = pkgname or pkg.get_root_name(stack.get_caller_module_name(-2))

    path = expand(*args, pkgname=pkgname)
    if path.is_dir():
        return True

    try:
        os.makedirs(path)
    except Exception as err:
        raise OSError("could not create directory") from err

    return path.is_dir()
Beispiel #13
0
def get_var(varname: str,
            *args: Any,
            pkgname: OptStr = None,
            **kwds: Any) -> OptStr:
    """Get environment or application variable.

    Environment variables comprise static and runtime properties of the
    operating system like 'username' or 'hostname'. Application variables in
    turn, are intended to describe the application distribution by authorship
    information, bibliographic information, status, formal conditions and notes
    or warnings. For mor information see :PEP:`345`.

    Args:
        varname: Name of environment variable. Typical application variable
            names are:
            'name': The name of the distribution
            'version': A string containing the distribution's version number
            'status': Development status of the distributed application.
                Typical values are 'Prototype', 'Development', or 'Production'
            'description': A longer description of the distribution that can
                run to several paragraphs.
            'keywords': A list of additional keywords to be used to assist
                searching for the distribution in a larger catalog.
            'url': A string containing the URL for the distribution's
                homepage.
            'license': Text indicating the license covering the distribution
            'copyright': Notice of statutorily prescribed form that informs
                users of the distribution to published copyright ownership.
            'author': A string containing the author's name at a minimum;
                additional contact information may be provided.
            'email': A string containing the author's e-mail address. It can
                contain a name and e-mail address, as described in :rfc:`822`.
            'maintainer': A string containing the maintainer's name at a
                minimum; additional contact information may be provided.
            'company': The company, which created or maintains the distribution.
            'organization': The organization, twhich created or maintains the
                distribution.
            'credits': list with strings, acknowledging further contributors,
                Teams or supporting organizations.
        *args: Optional arguments that specify the application, as required by
            the function :func:`~hup.base.env.update_vars`.
        pkgname:
        **kwds: Optional keyword arguments that specify the application, as
            required by the function :func:`~hup.base.env.update_vars`.

    Returns:
        String representing the value of the application variable.

    """
    # Check type of 'varname'
    check.has_type("'varname'", varname, str)

    # Get package name from callers top level module
    pkgname = pkgname or pkg.get_root_name(stack.get_caller_module_name(-2))

    # Update variables if not present or if optional arguments are given
    try:
        appvars = globals()['cache'][pkgname]['vars']
    except KeyError:
        update_vars(*args, pkgname=pkgname, **kwds)
    appvars = globals()['cache'][pkgname]['vars']

    return appvars.get(varname, None)
Beispiel #14
0
def expand(*args: NestPath,
           udict: OptStrDict = None,
           pkgname: OptStr = None,
           envdirs: bool = True) -> pathlib.Path:
    r"""Expand path variables.

    Args:
        *args: Path like arguments, respectively given by a tree of strings,
            which can be joined to a path.
        udict: dictionary for user variables.
            Thereby the keys in the dictionary are encapsulated
            by the symbol '%'. The user variables may also include references.
        envdirs: Boolen value which determines if environmental path variables
            are expanded. For a full list of valid environmental path variables
            see '.env.get_dirs'. Default is True

    Returns:
        String containing valid path syntax.

    Examples:
        >>> expand('%var1%/c', 'd', udict = {'var1': 'a/%var2%', 'var2': 'b'})
        'a\\b\\c\\d'

    """
    # Get package name from callers top level module
    pkgname = pkgname or pkg.get_root_name(stack.get_caller_module_name(-2))

    path = join_path(*args)
    udict = udict or {}

    # Create mapping with path variables
    pvars = {}
    if envdirs:
        for key, val in get_dirs(pkgname=pkgname).items():
            pvars[key] = str(val)
    if udict:
        for key, val in udict.items():
            pvars[key] = str(join_path(val))

    # Itereratively expand directories
    update = True
    i = 0
    while update:
        update = False
        for key, val in pvars.items():
            if '%' + key + '%' not in str(path):
                continue
            try:
                path = pathlib.Path(str(path).replace('%' + key + '%', val))
            except TypeError:
                del pvars[key]
            update = True
        i += 1
        if i > sys.getrecursionlimit():
            raise RecursionError('cyclic dependency in variables detected')
        path = pathlib.Path(path)

    # Expand unix style home path '~'
    if envdirs:
        path = path.expanduser()

    return path
Beispiel #15
0
def update_dirs(appname: OptStr = None,
                appauthor: OptStrOrBool = None,
                version: OptStr = None,
                pkgname: OptStr = None,
                **kwds: Any) -> None:
    """Update application specific directories from name, author and version.

    This function retrieves application specific directories from the package
    `appdirs`_. Additionally the directory 'site_package_dir' is retrieved fom
    the standard library package distutils and 'package_dir' and
    'package_data_dir' from the current top level module.

    Args:
        appname: is the name of application. If None, just the system directory
            is returned.
        appauthor: is the name of the appauthor or distributing body for this
            application. Typically it is the owning company name. You may pass
            False to disable it. Only applied in windows.
        version: is an optional version path element to append to the path.
            You might want to use this if you want multiple versions of your
            app to be able to run independently. If used, this would typically
            be "<major>.<minor>". Only applied when appname is present.
        **kwds: Optional directory name specific keyword arguments. For more
            information see `appdirs`_.

    .. _appdirs: http://github.com/ActiveState/appdirs

    """
    # Get package name from callers top level module
    pkgname = pkgname or pkg.get_root_name(stack.get_caller_module_name(-2))

    # Get system directories
    dirs: StrDictOfPaths = {}
    dirs['home'] = get_home()
    dirs['cwd'] = get_cwd()

    # Get application directories from appdirs
    appname = appname or get_var('name', pkgname=pkgname)
    appauthor = appauthor or get_var('author', pkgname=pkgname)
    app_dirs = appdirs.AppDirs(appname=appname,
                               appauthor=appauthor,
                               version=version,
                               **kwds)
    dirnames = [
        'user_cache_dir', 'user_config_dir', 'user_data_dir', 'user_log_dir',
        'site_config_dir', 'site_data_dir'
    ]
    for dirname in dirnames:
        dirs[dirname] = pathlib.Path(getattr(app_dirs, dirname))

    # Get distribution directories from distutils
    path = pathlib.Path(sysconfig.get_python_lib(), appname)
    dirs['site_package_dir'] = path

    # Tempdir from module tempfile
    tempdir = pathlib.Path(tempfile.gettempdir())
    dirs['site_temp_dir'] = tempdir

    # Get current package directories from top level module
    path = pathlib.Path(pkg.get_root().__file__).parent
    dirs['package_dir'] = path
    dirs['package_data_dir'] = path / 'data'
    dirs['package_temp_dir'] = tempdir / appname

    # Create package temp dir if it does not exist
    dirs['package_temp_dir'].mkdir(parents=True, exist_ok=True)  # type: ignore
    # Update globals
    if not 'cache' in globals():
        globals()['cache'] = {}
    if not pkgname in globals()['cache']:
        globals()['cache'][pkgname] = {}
    globals()['cache'][pkgname]['dirs'] = dirs
Beispiel #16
0
 def test_get_caller_module_name(self) -> None:
     name = stack.get_caller_module_name()
     self.assertEqual(name, __name__)