"""These settings utilities help us compose settings across multiple files... warning:: These functions only work when called from a settings file because they operate on the calling scope. Do not call these functions from any other location."""importreimportsysimporttypingastfromfunctoolsimportpartialfromosimportPathLikefrompathlibimportPathimportenvironimportimportlib_resourcesfromdjango.core.exceptionsimportImproperlyConfiguredre.Patternclass_NotGiven:pass_environ:t.Optional[environ.FileAwareEnv]=None_base_dir:t.Optional[Path]=None_email_rgx:t.Optional[re.Pattern]=None
[docs]defsplit_email_str(email:str)->t.Tuple[str,str]:""" Split an email string into display name and email address. If there is no display name it will be the empty string. :param email: An email string. E.g.: * ``name@example.com`` * ``Last, First <name@example.com>`` * ``"First Last" <name@example.com>`` :return: A 2-tuple of (display name, email address) """global_email_rgxif"<"notinemail:return("",email)ifnot_email_rgx:_email_rgx=re.compile(r"\s+[\"']?(?P<display>.*)[\"']?\s+<(?P<email>.*)>")ifnot(match:=_email_rgx.search(email)):raiseImproperlyConfigured(f"{email} is not a valid email string.")returnmatch.groupdict["display"].strip(),match.groupdict["email"].strip()
def_is_relative_to(parent:Path,child:Path)->bool:# TODO remove when python >=3.9ifsys.version_info[:2]<(3,9):try:child.relative_to(parent)returnTrueexceptValueError:returnFalsereturnchild.is_relative_to(parent)
[docs]defenv(**kwargs)->environ.FileAwareEnv:""" Fetch the :class:`environ.Env` object that should be used to configure settings. If an environment file exists it will be read in. The environment file precendence is: 1. The path in the environment variable SLM_ENV 2. BASE_DIR / .env :return: The :class:`~environ.FileAwareEnv` object configured to read our environment variables and files. """global_environif_environisNone:scope=sys._getframe(1).f_globalsdefault_env=scope.get("BASE_DIR","")ifdefault_env:default_env=str(Path(default_env)/".env")DEBUG=kwargs.pop("DEBUG",(bool,False))_environ=environ.FileAwareEnv(DEBUG=DEBUG,**kwargs)override_env=_environ.str("SLM_ENV",scope.get("SLM_ENV",default_env))ifoverride_envandPath(override_env).is_file():_environ.read_env(str(override_env))return_environ
[docs]defbase_dir(scope=None)->Path:""" Fetch the :setting:`BASE_DIR`, this will raise an :exc:`~django.core.exceptions.ImproperlyConfigured` exception if it does not exist yet. :param scope: The settings scope (do not pass if calling from a settings file) :return: The :setting:`BASE_DIR` as a :class:`pathlib.Path` object. :raises: :exc:`~django.core.exceptions.ImproperlyConfigured` if :setting:`BASE_DIR` is not defined. """global_base_dirifnot_base_dir:scope=scopeorsys._getframe(1).f_globals_base_dir=scope.get("BASE_DIR",None)if_base_dirisNone:raiseImproperlyConfigured("BASE_DIR was needed before it was defined!")_base_dir=Path(_base_dir)ifnot_base_dir.is_dir():ifscope.get("DEBUG",False):_base_dir.mkdir(mode=0o775,parents=True,exist_ok=True)else:raiseImproperlyConfigured(f"BASE_DIR: {_base_dir} is not a directory that exists.")return_base_dir
[docs]defset_default(var_name:str,default:t.Any,set_if_none:bool=True)->t.Any:""" Set the value of the given variable in the calling scope to a default value if it has been not been previously defined. :param var_name: The name of the variable to set in the calling scope. :param default: The value to set the variable to if its undefined :param set_if_none: Treat the variable as undefined if it is None, default: True :return: the variable that was set """scope=sys._getframe(1).f_globalsifvar_namenotinscopeorset_if_noneandscope[var_name]isNone:scope[var_name]=defaultreturnscope[var_name]
[docs]defis_defined(var_name:str)->bool:""" Returns true if the given variable has been defined in the calling scope. :param var_name: The name of the variable to check for :return: True if the variable is defined, false otherwise """returnvar_nameinsys._getframe(1).f_globals
[docs]defget_setting(var_name:str,default:t.Any=_NotGiven)->t.Any:""" Returns the value of the setting if it exists, if it does not exist the value given to default is returned. If no default value is given and the setting does not exist a NameError is raised :param var_name: The name of the variable to check for :param default: The default value to return if the variable does not exist :raises NameError: if the name is undefined and no default is given. """value=sys._getframe(1).f_globals.get(var_name,default)ifvalueis_NotGiven:raiseNameError(f"{var_name} setting variable is not defined.",name=var_name)returnvalue
[docs]defslm_path(value:str,must_exist:bool=False,mk_dirs:bool=False)->t.Optional[str]:""" Resolve a configuration path. If the path is relative it will be resolved under :setting:`BASE_DIR`. If the value is falsey it will be returned as-is even if ``must_exist`` is set. :param value: The path as a string :param must_exist: If True, raise an :exc:`~django.core.exceptions.ImproperlyConfigured` if the path does not exist. :param mk_dirs: If True and the path is relative to :setting:`BASE_DIR` create the path as a directory if it does not already exist. :raises: :exc:`~django.core.exceptions.ImproperlyConfigured` if :setting:`BASE_DIR` is needed but not defined, or does not exist or if ``must_exist`` is ``True`` but the path does not exist. """ifnotvalue:returnvaluepth=Path(str(value))ifrelative:=notpth.is_absolute():pth=base_dir(sys._getframe(1).f_globals)/pthifnotpth.exists()andmk_dirs:relative=relativeor_is_relative_to(base_dir(sys._getframe(1).f_globals),pth)ifrelative:pth.mkdir(mode=0o775ifsys._getframe(1).f_globals.get("DEBUG",False)else0o770,parents=True,exist_ok=True,)ifmust_existandnotpth.exists():raiseImproperlyConfigured(f"{pth} does not exist!")returnstr(pth)
slm_path_must_exist=partial(slm_path,must_exist=True)"""A shortcut for :func:`~slm.settings.slm_path` with ``must_exist=True``."""slm_path_mk_dirs_must_exist=partial(slm_path,must_exist=True,mk_dirs=True)"""A shortcut for :func:`~slm.settings.slm_path` with ``must_exist=True`` and``mk_dirs=True``."""
[docs]defunset(var_name:str):""" Unset the value of the given variable in the calling scope. :param var_name: The name of the variable to unset in the calling scope. """scope=sys._getframe(1).f_globalsifvar_nameinscope:delscope[var_name]
[docs]defresource(package:str,file:PathLike):""" Use a packaged resource file as a settings file include. If you are including a settings file from another package you should pass this to split_settings.tools.include: .. code-block:: python include(resource("slm.settings", "root.py")) :param package: The string import path of the package containing the module :param file: The name of the python settings module :return: The full string path to the resource. """profile2=importlib_resources.files(package)/filewithimportlib_resources.as_file(profile2)asprofile2_path:returnstr(profile2_path)