Source code for easy_as_pypi_appdirs.app_dirs_with_mkdir

# Author: Landon Bouma <https://tallybark.com/>
# Project: https://github.com/doblabs/easy-as-pypi-appdirs#🛣
# Copyright © 2018-2020 Landon Bouma. All rights reserved.
# License: MIT

"""AppDirs subclass adds mkdir side effect."""

from functools import update_wrapper

import appdirs

from .exists_or_mkdirs import must_ensure_directory_exists
from .singleton import Singleton

__all__ = (
    "AppDirsWithMkdir",
    # PRIVATE:
    #  'mkdir_side_effect',
)


def mkdir_side_effect(func):
    def _mkdir_side_effect(app_dirs, *args, **kwargs):
        app_dir_path = func(app_dirs, *args, **kwargs)
        if app_dirs.create:
            # Ensure path exists, which might raise, most likely one of:
            #   PermissionError, FileExistsError, or NotADirectoryError.
            must_ensure_directory_exists(app_dir_path)
        return app_dir_path

    return update_wrapper(_mkdir_side_effect, func)


# aka AppDirsWithMkdirSideEffect
[docs] class AppDirsWithMkdir(appdirs.AppDirs, metaclass=Singleton): """Singleton AppDirs with ``mkdir`` side effect. - As a Singleton, this class can be instantiated and used without knowing the appname, provided some bootstrap code registers it. E.g., when the app is first loaded, have it call:: first_instance = AppDirsWithMkdir(appname='my-awesome-app') and then, later, access the same instance by instantiating anew: user_data_dir = AppDirsWithMkdir().user_data_dir - Has a Side effect, which is that, just by querying a directory path, this class creates that path, or ensures it exists. The author (lb) isn't super jazzed by this "feature", but it was useful to get a downstream app up and running more easily without having to manage mkdir'ing application directories... and now it's been baked in so long I don't want to spend the time changing it... it's just something of which to be aware: - Asking for an application directory path may create that path. - Regarding Singleton (again), which is not the most respected pattern (some might say it's always a smell), consider this classic discussion on ways to implement Singleton in Python, and whether or not they're a good idea: - *Creating a singleton in Python*: https://stackoverflow.com/questions/6760685/creating-a-singleton-in-python """ # Although the class is a Singleton, a user may need to access the class # without actually creating it or before it's actually created. So here's # a (hacky) class member to indicate when the singleton instance is ready. is_ready = None def __init__(self, *args, **kwargs): """Add create flag value to instance.""" # The appdirs.AppDirs base class takes a number of parameters: # appname=None, # appauthor=None, # version=None, # roaming=False, # multipath=False, # but we only care about appname. super(AppDirsWithMkdir, self).__init__(*args, **kwargs) self._raise_if_appname_not_specified() AppDirsWithMkdir.is_ready = True self._default_ensure_dirs_on_query() def __eq__(self, other): """Compare two AppDirsWithMkdir using the ``appname`` for each object.""" return self.appname == other.appname def _raise_if_appname_not_specified(self): if self.appname: return msg = "DEV: Call register_application before using AppDirs or AppDirsWithMkdir." raise Exception(msg) def _default_ensure_dirs_on_query(self): # FIXME: (lb): I'm not super cool with this side-effect: # Calling any property herein will cause its # directory path to be created! Creating paths # should be a deliberate action and not a side effect # of just asking for a path. In any case, it currently # works this way, so just rolling with the flow, for now. # See Click: it has concept of lazy-creating paths, i.e., # only create path when a file therein opened for write. self.create = True # *** @property def safe(self): """Return parent object, without `mkdirs -p` side effect.""" return super(AppDirsWithMkdir, self) # *** @property @mkdir_side_effect def user_data_dir(self): """Return ``user_data_dir``.""" return appdirs.AppDirs.user_data_dir.fget(self) @property @mkdir_side_effect def site_data_dir(self): """Return ``site_data_dir``.""" return appdirs.AppDirs.site_data_dir.fget(self) @property @mkdir_side_effect def user_config_dir(self): """Return ``user_config_dir``.""" return appdirs.AppDirs.user_config_dir.fget(self) @property @mkdir_side_effect def site_config_dir(self): """Return ``site_config_dir``.""" return appdirs.AppDirs.site_config_dir.fget(self) @property @mkdir_side_effect def user_cache_dir(self): """Return ``user_cache_dir``.""" return appdirs.AppDirs.user_cache_dir.fget(self) @property @mkdir_side_effect def user_state_dir(self): """Return ``user_state_dir``.""" return appdirs.AppDirs.user_state_dir.fget(self) @property @mkdir_side_effect def user_log_dir(self): """Return ``user_log_dir``.""" return appdirs.AppDirs.user_log_dir.fget(self)
# *** # ***