"""Base module for environment managers."""from__future__importannotationsimportloggingimportosimportplatformimportshutilimportsysfrompathlibimportPathfromtypingimportTYPE_CHECKING,castfrom..compatimportcached_propertyfrom..mixinsimportDelCachedPropMixinifTYPE_CHECKING:fromcollections.abcimportGeneratorfromurllib.errorimportURLErrorfrom.._loggingimportRunwayLoggerfrom..utilsimportVersionLOGGER=cast("RunwayLogger",logging.getLogger(__name__))
[docs]defhandle_bin_download_error(exc:URLError,name:str)->None:"""Give user info about their failed download. Raises: SystemExit: Always raised after logging reason. """url_error_msg=str(exc.reason)if"CERTIFICATE_VERIFY_FAILED"notinurl_error_msg:raiseexcLOGGER.error("Attempted to download %s but was unable to verify the TLS ""certificate on its download site.",name,)LOGGER.error("Full TLS error message: %s",url_error_msg)ifplatform.system().startswith("Darwin")and("unable to get local issuer certificate"inurl_error_msg):LOGGER.error("This is likely caused by your Python installation missing root certificates. "'Run "/Applications/Python %s.%s/"Install Certificates.command" to fix it '"(https://stackoverflow.com/a/42334357/2547802)",sys.version_info[0],sys.version_info[1],)sys.exit(1)
[docs]classEnvManager(DelCachedPropMixin):"""Base environment manager class. Attributes: binPath to the binary of the current version. current_version: The current binary version being used. env_dir_name: Name of the directory within the users home directory where binary versions will be stored. path: The current working directory. """_bin_name:strcurrent_version:str|Noneenv_dir_name:strpath:Path
[docs]def__init__(self,bin_name:str,dir_name:str,path:Path|None=None)->None:"""Initialize class. Args: bin_name: Name of the binary file (e.g. kubectl) dir_name: Name of the directory within the users home directory where binary versions will be stored. path: The current working directory. """self._bin_name=bin_name+self.command_suffixself.current_version=Noneself.env_dir_name=dir_nameifplatform.system()=="Windows"else"."+dir_nameself.path=pathifpathelsePath.cwd()
@propertydefbin(self)->Path:"""Path to the version binary. Returns: Path """ifself.current_version:returnself.versions_dir/self.current_version/self._bin_namereturnself.versions_dir/self._bin_name@cached_propertydefcommand_suffix(self)->str:"""Return command suffix based on platform.system."""ifplatform.system()=="Windows":return".exe"return""@cached_propertydefenv_dir(self)->Path:"""Return the directory used to store version binaries."""ifplatform.system()=="Windows":if"APPDATA"inos.environ:returnPath(os.environ["APPDATA"])/self.env_dir_namereturnPath.home()/"AppData"/"Roaming"/self.env_dir_namereturnPath.home()/self.env_dir_name@cached_propertydefversions_dir(self)->Path:"""Return the directory used to store binary. When first used, the existence of the directory is checked and it is created if needed. """returnself.env_dir/"versions"@cached_propertydefversion_file(self)->Path|None:"""Find and return a "<bin version file>" file if one is present. Returns: Path to the <bin> version file. """raiseNotImplementedError
[docs]definstall(self,version_requested:str|None=None)->str:"""Ensure <bin> is installed."""raiseNotImplementedError
[docs]deflist_installed(self)->Generator[Path,None,None]:"""List installed versions of <bin>."""raiseNotImplementedError
[docs]defuninstall(self,version:str|Version)->bool:"""Uninstall a version of the managed binary. Args: version: Version of binary to uninstall. Returns: Whether a version of the binary was uninstalled or not. """version_dir=self.versions_dir/str(version)ifversion_dir.is_dir():LOGGER.notice("uninstalling %s%s from %s...",self._bin_name,version,self.versions_dir,)shutil.rmtree(version_dir)LOGGER.success("uninstalled %s%s",self._bin_name,version)returnTrueLOGGER.error("%s%s not installed",self._bin_name,version)returnFalse