# ubelt.util_platform module

The goal of this module is to provide an idiomatic cross-platform pattern of accessing platform dependent file systems.

Standard application directory structure: cache, config, and other XDG standards [XDG_Spec]. This is similar to the more focused `appdirs` module [AS_appdirs] (deprecated as of 2023-02-10) and its successor `platformdirs` [PlatDirs].

Note

Table mapping the type of directory to the system default environment variable. Inspired by [SO_43853548], [SO_11113974], and [harawata_appdirs].

           | Linux            | Win32          |   Darwin
    data   | $XDG_DATA_HOME   | %APPDATA%      | ~/Library/Application Support
    config | $XDG_CONFIG_HOME | %APPDATA%      | ~/Library/Application Support
    cache  | $XDG_CACHE_HOME  | %LOCALAPPDATA% | ~/Library/Caches

    If an environment variable is not specified the defaults are:
        APPDATA      = ~/AppData/Roaming
        LOCALAPPDATA = ~/AppData/Local

        XDG_DATA_HOME   = ~/.local/share
        XDG_CACHE_HOME  = ~/.cache
        XDG_CONFIG_HOME = ~/.config

References

[XDG_Spec]

https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html

[SO_43853548]

https://stackoverflow.com/questions/43853548/xdg-windows

[SO_11113974]

https://stackoverflow.com/questions/11113974/cross-plat-path

[harawata_appdirs]

https://github.com/harawata/appdirs#supported-directories

[AS_appdirs]

https://github.com/ActiveState/appdirs

[PlatDirs]

https://pypi.org/project/platformdirs/

ubelt.util_platform.find_exe(name, multi=False, path=None)

  Locate a command.

  Search your local filesystem for an executable and return the first matching file with executable permission.

  Parameters:

    - name (str | PathLike) – globstr of matching filename

    - multi (bool) – if True return all matches instead of just the first. Defaults to False.

    - path (str | PathLike | Iterable[str | PathLike] | None) – If specified, overrides the system PATH variable.

  Returns:

    returns matching executable(s).

  Return type:

    str | List[str] | None

  SeeAlso:

    `shutil.which()` - which is available in Python 3.3+.

  Note

  This is essentially the `which` UNIX command

  References

  [SO_377017]

  https://stackoverflow.com/questions/377017/test-if-executable-exists-in-python/377028#377028

  [shutil_which]

  https://docs.python.org/dev/library/shutil.html#shutil.which

  Example

      >>> # The following are programs commonly exposed via the PATH variable.
      >>> # Exact results may differ between machines.
      >>> # xdoctest: +IGNORE_WANT
      >>> import ubelt as ub
      >>> print(ub.find_exe('ls'))
      >>> print(ub.find_exe('ping'))
      >>> print(ub.find_exe('which'))
      >>> print(ub.find_exe('which', multi=True))
      >>> print(ub.find_exe('ping', multi=True))
      >>> print(ub.find_exe('noexist', multi=True))
      /usr/bin/ls
      /usr/bin/ping
      /usr/bin/which
      ['/usr/bin/which', '/bin/which']
      ['/usr/bin/ping', '/bin/ping']
      []

  Example

      >>> import ubelt as ub
      >>> assert not ub.find_exe('!noexist', multi=False)
      >>> assert ub.find_exe('ping', multi=False) or ub.find_exe('ls', multi=False)
      >>> assert not ub.find_exe('!noexist', multi=True)
      >>> assert ub.find_exe('ping', multi=True) or ub.find_exe('ls', multi=True)

  Benchmark

      >>> # xdoctest: +IGNORE_WANT
      >>> import ubelt as ub
      >>> import shutil
      >>> from timerit import Timerit
      >>> for timer in Timerit(1000, bestof=10, label='ub.find_exe'):
      >>>     ub.find_exe('which')
      >>> for timer in Timerit(1000, bestof=10, label='shutil.which'):
      >>>     shutil.which('which')
      Timed best=25.339 µs, mean=25.809 ± 0.3 µs for ub.find_exe
      Timed best=28.600 µs, mean=28.986 ± 0.3 µs for shutil.which

ubelt.util_platform.find_path(name, path=None, exact=False)

  Search for a file or directory on your local filesystem by name (file must be in a directory specified in a PATH environment variable)

  Parameters:

    - name (str | PathLike) – file name to match. If exact is False this may be a glob pattern

    - path (str | Iterable[str | PathLike] | None) – list of directories to search either specified as an `os.pathsep` separated string or a list of directories. Defaults to environment PATH.

    - exact (bool) – if True, only returns exact matches. Defaults to False.

  Yields:

    str – candidate - a path that matches `name`

  Note

  Running with `name=''` (i.e. `ub.find_path('')`) will simply yield all directories in your PATH.

  Note

  For recursive behavior set `path=(d for d, _, _ in os.walk('.'))`, where `'.'` might be replaced by the root directory of interest.

  Example

      >>> # xdoctest: +IGNORE_WANT
      >>> import ubelt as ub
      >>> print(list(ub.find_path('ping', exact=True)))
      >>> print(list(ub.find_path('bin')))
      >>> print(list(ub.find_path('gcc*')))
      >>> print(list(ub.find_path('cmake*')))
      ['/usr/bin/ping', '/bin/ping']
      []
      [... '/usr/bin/gcc-11', '/usr/bin/gcc-ranlib', ...]
      [... '/usr/bin/cmake-gui', '/usr/bin/cmake', ...]

  Example

      >>> import ubelt as ub
      >>> from os.path import dirname
      >>> path = dirname(dirname(ub.util_platform.__file__))
      >>> res = sorted(ub.find_path('ubelt/util_*.py', path=path))
      >>> assert len(res) >= 10
      >>> res = sorted(ub.find_path('ubelt/util_platform.py', path=path, exact=True))
      >>> print(res)
      >>> assert len(res) == 1

ubelt.util_platform.ensure_app_cache_dir(appname, *args)

  Calls `get_app_cache_dir()` but ensures the directory exists.

  Note

  New applications should prefer `ubelt.util_path.Path.appdir()` i.e. `ubelt.Path.appdir(appname, *args, type='cache').ensuredir()`.

  Parameters:

    - appname (str) – the name of the application

    - *args – any other subdirectories may be specified

  Returns:

    the path to the ensured directory

  Return type:

    str

  SeeAlso:

    `get_app_cache_dir()`

  Example

      >>> import ubelt as ub
      >>> dpath = ub.ensure_app_cache_dir('ubelt')
      >>> assert exists(dpath)

ubelt.util_platform.ensure_app_config_dir(appname, *args)

  Calls `get_app_config_dir()` but ensures the directory exists.

  Note

  New applications should prefer `ubelt.util_path.Path.appdir()` i.e. `ubelt.Path.appdir(appname, *args, type='config').ensuredir()`.

  Parameters:

    - appname (str) – the name of the application

    - *args – any other subdirectories may be specified

  Returns:

    the path to the ensured directory

  Return type:

    str

  SeeAlso:

    `get_app_config_dir()`

  Example

      >>> import ubelt as ub
      >>> dpath = ub.ensure_app_config_dir('ubelt')
      >>> assert exists(dpath)

ubelt.util_platform.ensure_app_data_dir(appname, *args)

  Calls `get_app_data_dir()` but ensures the directory exists.

  Note

  New applications should prefer `ubelt.util_path.Path.appdir()` i.e. `ubelt.Path.appdir(appname, *args, type='data').ensuredir()`.

  Parameters:

    - appname (str) – the name of the application

    - *args – any other subdirectories may be specified

  Returns:

    the path to the ensured directory

  Return type:

    str

  SeeAlso:

    `get_app_data_dir()`

  Example

      >>> import ubelt as ub
      >>> dpath = ub.ensure_app_data_dir('ubelt')
      >>> assert exists(dpath)

ubelt.util_platform.get_app_cache_dir(appname, *args)

  Returns a writable directory for an application. This should be used for temporary deletable data.

  Note

  New applications should prefer `ubelt.util_path.Path.appdir()` i.e. `ubelt.Path.appdir(appname, *args, type='cache')`.

  Parameters:

    - appname (str) – the name of the application

    - *args – any other subdirectories may be specified

  Returns:

    the path to the ensured directory

  Return type:

    str

  Returns:

    dpath - writable cache directory for this application

  Return type:

    str

  SeeAlso:

    `ensure_app_cache_dir()`

ubelt.util_platform.get_app_config_dir(appname, *args)

  Returns a writable directory for an application This should be used for persistent configuration files.

  Note

  New applications should prefer `ubelt.util_path.Path.appdir()` i.e. `ubelt.Path.appdir(appname, *args, type='config')`.

  Parameters:

    - appname (str) – the name of the application

    - *args – any other subdirectories may be specified

  Returns:

    dpath - writable config directory for this application

  Return type:

    str

  SeeAlso:

    `ensure_app_config_dir()`

ubelt.util_platform.get_app_data_dir(appname, *args)

  Returns a writable directory for an application. This should be used for temporary deletable data.

  Note

  New applications should prefer `ubelt.util_path.Path.appdir()` i.e. `ubelt.Path.appdir(appname, *args, type='data')`.

  Parameters:

    - appname (str) – the name of the application

    - *args – any other subdirectories may be specified

  Returns:

    dpath - writable data directory for this application

  Return type:

    str

  SeeAlso:

    `ensure_app_data_dir()`

ubelt.util_platform.platform_cache_dir()

  Returns a directory which should be writable for any application This should be used for temporary deletable data.

  Returns:

    path to the cache dir used by the current operating system

  Return type:

    str

ubelt.util_platform.platform_config_dir()

  Returns a directory which should be writable for any application This should be used for persistent configuration files.

  Returns:

    path to the cache dir used by the current operating system

  Return type:

    str

ubelt.util_platform.platform_data_dir()

  Returns path for user-specific data files

  Returns:

    path to the data dir used by the current operating system

  Return type:

    str