def test_check_package_global(): """Test for an installed package.""" first_package = list(pkg_resources.working_set)[0] installed_package = first_package.project_name installed_version = first_package.version assert package.is_installed(installed_package) assert package.is_installed(f"{installed_package}=={installed_version}") assert package.is_installed(f"{installed_package}>={installed_version}") assert package.is_installed(f"{installed_package}<={installed_version}") assert not package.is_installed(f"{installed_package}<{installed_version}")
def test_check_package_previous_failed_install(): """Test for when a previously install package failed and left cruft behind.""" first_package = list(pkg_resources.working_set)[0] installed_package = first_package.project_name installed_version = first_package.version with patch( "homeassistant.util.package.pkg_resources.get_distribution", side_effect=pkg_resources.ExtractionError, ), patch("homeassistant.util.package.version", return_value=None): assert not package.is_installed(installed_package) assert not package.is_installed( f"{installed_package}=={installed_version}")
def install_requirements(integration: Integration, requirements: Set[str]) -> bool: """Install integration requirements. Return True if successful. """ global PIPDEPTREE_CACHE ensure_cache() for req in requirements: match = PIP_REGEX.search(req) if not match: integration.add_error( "requirements", f"Failed to parse requirement {req} before installation", ) continue install_args = match.group(1) requirement_arg = match.group(2) is_installed = False normalized = normalize_package_name(requirement_arg) if normalized and "==" in requirement_arg: ver = requirement_arg.split("==")[-1] item = PIPDEPTREE_CACHE.get(normalized) is_installed = item and item["installed_version"] == ver if not is_installed: try: is_installed = pkg_util.is_installed(req) except ValueError: is_installed = False if is_installed: continue args = [sys.executable, "-m", "pip", "install", "--quiet"] if install_args: args.append(install_args) args.append(requirement_arg) try: result = subprocess.run(args, check=True, capture_output=True, text=True) except subprocess.SubprocessError: integration.add_error( "requirements", f"Requirement {req} failed to install", ) else: # Clear the pipdeptree cache if something got installed if "Successfully installed" in result.stdout: PIPDEPTREE_CACHE = None if integration.errors: return False return True
async def async_process_requirements(hass: HomeAssistant, name: str, requirements: list[str]) -> None: """Install the requirements for a component or platform. This method is a coroutine. It will raise RequirementsNotFound if an requirement can't be satisfied. """ pip_lock = hass.data.get(DATA_PIP_LOCK) if pip_lock is None: pip_lock = hass.data[DATA_PIP_LOCK] = asyncio.Lock() kwargs = pip_kwargs(hass.config.config_dir) async with pip_lock: for req in requirements: if pkg_util.is_installed(req): continue def _install(req: str, kwargs: dict[str, Any]) -> bool: """Install requirement.""" return pkg_util.install_package(req, **kwargs) ret = await hass.async_add_executor_job(_install, req, kwargs) if not ret: raise RequirementsNotFound(name, [req])
async def async_process_requirements(hass: HomeAssistant, name: str, requirements: List[str]) -> bool: """Install the requirements for a component or platform. This method is a coroutine. """ pip_lock = hass.data.get(DATA_PIP_LOCK) if pip_lock is None: pip_lock = hass.data[DATA_PIP_LOCK] = asyncio.Lock() kwargs = pip_kwargs(hass.config.config_dir) async with pip_lock: for req in requirements: if pkg_util.is_installed(req): continue ret = await hass.async_add_executor_job(_install, hass, req, kwargs) if not ret: _LOGGER.error( "Not initializing %s because could not install " "requirement %s", name, req, ) return False return True
async def _async_process_requirements( hass: HomeAssistant, name: str, req: str, install_failure_history: set[str], kwargs: Any, ) -> None: """Install a requirement and save failures.""" if req in install_failure_history: _LOGGER.info( "Multiple attempts to install %s failed, install will be retried after next configuration check or restart", req, ) raise RequirementsNotFound(name, [req]) if pkg_util.is_installed(req): return def _install(req: str, kwargs: dict[str, Any]) -> bool: """Install requirement.""" return pkg_util.install_package(req, **kwargs) for _ in range(MAX_INSTALL_FAILURES): if await hass.async_add_executor_job(_install, req, kwargs): return install_failure_history.add(req) raise RequirementsNotFound(name, [req])
def test_get_distribution_falls_back_to_version(): """Test for get_distribution failing and fallback to version.""" first_package = list(pkg_resources.working_set)[0] installed_package = first_package.project_name installed_version = first_package.version with patch( "homeassistant.util.package.pkg_resources.get_distribution", side_effect=pkg_resources.ExtractionError, ): assert package.is_installed(installed_package) assert package.is_installed( f"{installed_package}=={installed_version}") assert package.is_installed( f"{installed_package}>={installed_version}") assert package.is_installed( f"{installed_package}<={installed_version}") assert not package.is_installed( f"{installed_package}<{installed_version}")
def run(args: List) -> int: """Run a script.""" scripts = [] path = os.path.dirname(__file__) for fil in os.listdir(path): if fil == "__pycache__": continue if os.path.isdir(os.path.join(path, fil)): scripts.append(fil) elif fil != "__init__.py" and fil.endswith(".py"): scripts.append(fil[:-3]) if not args: print("Please specify a script to run.") print("Available scripts:", ", ".join(scripts)) return 1 if args[0] not in scripts: print("Invalid script specified.") print("Available scripts:", ", ".join(scripts)) return 1 script = importlib.import_module(f"homeassistant.scripts.{args[0]}") config_dir = extract_config_dir() loop = asyncio.get_event_loop() if not is_virtual_env(): loop.run_until_complete(async_mount_local_lib_path(config_dir)) _pip_kwargs = pip_kwargs(config_dir) logging.basicConfig(stream=sys.stdout, level=logging.INFO) for req in getattr(script, "REQUIREMENTS", []): if is_installed(req): continue if not install_package(req, **_pip_kwargs): print("Aborting script, could not install dependency", req) return 1 asyncio.set_event_loop_policy(runner.HassEventLoopPolicy(False)) return script.run(args[1:]) # type: ignore
def run(args: List) -> int: """Run a script.""" scripts = [] path = os.path.dirname(__file__) for fil in os.listdir(path): if fil == '__pycache__': continue elif os.path.isdir(os.path.join(path, fil)): scripts.append(fil) elif fil != '__init__.py' and fil.endswith('.py'): scripts.append(fil[:-3]) if not args: print('Please specify a script to run.') print('Available scripts:', ', '.join(scripts)) return 1 if args[0] not in scripts: print('Invalid script specified.') print('Available scripts:', ', '.join(scripts)) return 1 script = importlib.import_module('homeassistant.scripts.' + args[0]) config_dir = extract_config_dir() loop = asyncio.get_event_loop() if not is_virtual_env(): loop.run_until_complete(async_mount_local_lib_path(config_dir)) _pip_kwargs = pip_kwargs(config_dir) logging.basicConfig(stream=sys.stdout, level=logging.INFO) for req in getattr(script, 'REQUIREMENTS', []): if is_installed(req): continue if not install_package(req, **_pip_kwargs): print('Aborting script, could not install dependency', req) return 1 return script.run(args[1:]) # type: ignore
def install_requirements(integration: Integration, requirements: Set[str]) -> bool: """Install integration requirements. Return True if successful. """ for req in requirements: try: is_installed = pkg_util.is_installed(req) except ValueError: is_installed = False if is_installed: continue match = PIP_REGEX.search(req) if not match: integration.add_error( "requirements", f"Failed to parse requirement {req} before installation", ) continue install_args = match.group(1) requirement_arg = match.group(2) args = [sys.executable, "-m", "pip", "install", "--quiet"] if install_args: args.append(install_args) args.append(requirement_arg) try: subprocess.run(args, check=True) except subprocess.SubprocessError: integration.add_error( "requirements", f"Requirement {req} failed to install", ) if integration.errors: return False return True
def test_check_package_zip(): """Test for an installed zip package.""" assert not package.is_installed(TEST_ZIP_REQ)
def test_check_package_version_does_not_match(): """Test for version mismatch.""" installed_package = list(pkg_resources.working_set)[0].project_name assert not package.is_installed(f"{installed_package}==999.999.999") assert not package.is_installed(f"{installed_package}>=999.999.999")
def test_check_package_global(): """Test for an installed package.""" installed_package = list(pkg_resources.working_set)[0].project_name assert package.is_installed(installed_package)
async def post(self, request): hass = request.app["hass"] fileExplorer = hass.data[DOMAIN] res = await request.json() _type = res.get('type', '') _url = res.get('url', '') _path = hass.config.path('./' + res.get('path', '')) try: if _type == 'get': # 获取目录和文件 data = fileExplorer.getDirectory(_path) return self.json(data) elif _type == 'get-content': # 获取文件内容 data = fileExplorer.getContent(_path) return self.json({'code': 0, 'data': data}) elif _type == 'get-cloud-list': # 获取七牛云备份列表 return self.json({'code': 0, 'data': []}) elif _type == 'delete': # 删除文件 fileExplorer.delete(_path) return self.json({'code': 0, 'msg': '删除成功'}) elif _type == 'delete-qiniu': # 删除备份文件 return self.json({'code': 0, 'msg': '删除成功'}) elif _type == 'new-file': # 新建文件 data = res.get('data', '') fileExplorer.setContent(_path, data) return self.json({'code': 0, 'msg': '保存成功'}) elif _type == 'new-dir': # 新建文件夹 fileExplorer.mkdir(_path) return self.json({'code': 0, 'msg': '新建成功'}) elif _type == 'upload-file': # 上传文件 return self.json({'code': 0, 'msg': '上传文件成功'}) elif _type == 'upload-dir': # 上传文件夹 return self.json({'code': 0, 'msg': '上传文件夹成功'}) elif _type == 'download-url': # 下载网络文件到文件夹 print(_url) down_res = None # 下载文件流 async with aiohttp.request('GET', _url) as r: down_res = await r.read() # 保存文件 with open(_path + '/' + os.path.basename(_url), "wb") as code: code.write(down_res) return self.json({'code': 0, 'msg': '下载成功'}) elif _type == 'download-tmpfile': # 下载临时文件 print(_url) down_res = None async with aiohttp.request('GET', _url) as r: down_res = await r.read() # 获取临时文件目录 _path = tempfile.gettempdir() backup_path = _path + '/ha_file_explorer_backup.zip' with open(backup_path, "wb") as code: code.write(down_res) # 解压文件 fileExplorer.unzip(backup_path, _path + '/ha_file_explorer_backup') # 删除下截的备份文件 fileExplorer.delete(backup_path) # 返回文件夹里的数据 return self.json({ 'code': 0, 'data': fileExplorer.getAllFile(_path + '/ha_file_explorer_backup'), 'msg': '下载成功' }) elif _type == 'rename': rename_path = hass.config.path(f"./{res['rename_path']}") os.rename(_path, rename_path) return self.json({'code': 0, 'msg': '重命名成功'}) ## ====================== 七牛云 ========================== elif _type == 'qn-list': if fileExplorer.q is None: return self.json({'code': 1, 'msg': '请配置七牛云相关密钥信息'}) try: res = await fileExplorer.q.get_list(None) # print('测试一下:', res) return self.json({'code': 0, 'msg': '获取备份列表', 'data': res}) except Exception as e: print(e) return self.json({ 'code': 1, 'msg': '备份列表获取异常,请检查是否正确配置七牛云密钥' }) elif _type == 'qn-upload': if fileExplorer.q is None: return self.json({'code': 1, 'msg': '请配置七牛云相关密钥信息'}) if 'path' in res: zf = fileExplorer.zipdir(res['path']) elif 'list' in res: # 压缩多个文件 zf = fileExplorer.zip(res['list']) try: await fileExplorer.q.upload(zf) # 上传成功,删除文件 fileExplorer.delete(zf) return self.json({'code': 0, 'msg': '上传成功'}) except Exception as ex: print(ex) return self.json({ 'code': 1, 'msg': '上传错误,一般是七牛云不能创建配置文件的权限问题' }) elif _type == 'qn-delete': if fileExplorer.q is None: return self.json({'code': 1, 'msg': '请配置七牛云相关密钥信息'}) await fileExplorer.q.delete(res.get('key')) return self.json({'code': 0, 'msg': '删除成功'}) ## ====================== 移动文件 ========================== elif _type == 'move-file': # 移动文件 return self.json({'code': 0, 'msg': '移动文件成功'}) elif _type == 'move-tmpfile': # 还原数据 fileExplorer.move(res['list']) await fileExplorer.notify("还原成功") return self.json({'code': 0, 'msg': '还原成功'}) elif _type == 'install-package': # 安装依赖包 package_name = _url if package.is_installed(package_name) == False: package.install_package(package_name) return self.json({'code': 0, 'msg': '正在更新依赖包'}) return self.json({'code': 0, 'msg': '已经安装成功啦'}) elif _type == 'update-package': # 更新系统依赖包 data = pip_install(_url) return self.json({'code': 0, 'data': data, 'msg': '更新依赖包完成'}) elif _type == 'update': # 拉取组件 _domain = res['domain'] _path = hass.config.path("custom_components").replace( '\\', '/') # https://github.com.cnpmjs.org/shaonianzhentan/$DOMAIN with open(_path + '/' + DOMAIN + '/update.sh', 'r', encoding='utf-8') as f: arr = _url.split('/') content = f.read().replace('$PATH', _path).replace( '$DOMAIN', _domain).replace('$URL', _url).replace('$PROJECT', arr[len(arr) - 1]) # 获取临时文件目录 tmp_path = tempfile.gettempdir() _sh = tmp_path + '/' + _domain + '.sh' with open(_sh, 'w', encoding='utf-8') as f: f.write(content) # 如果是windows则直接运行 _cmd = 'bash ' + _sh if os.name == 'nt': _cmd = _sh subprocess.Popen(_cmd, shell=True) return self.json({'code': 0, 'msg': '正在异步拉取代码,请自行查看是否成功'}) except Exception as ex: print(ex) return self.json({'code': 1, 'msg': f'出现异常:{ex}'}) return self.json(res)