【问题标题】:Pip install upgrade unable to remove temp filesPip install upgrade 无法删除临时文件
【发布时间】:2019-09-13 00:31:03
【问题描述】:

在维护 CLI 实用程序的过程中,我想添加一个 update 操作,该操作将从 PyPI 获取该软件包的最新版本并升级现有安装。

$ cli -V
1.0.23

$ cli update
// many lines of pip spam

$ cli -V
1.0.24  // or etc

这在所有系统范围内安装了 Python(C:\Python36 或类似)的机器上都可以正常工作,但是以用户身份安装 Python(C:\users\username\AppData\Local\Programs\Python\Python36)的机器在卸载旧版本时收到此错误:

Could not install packages due to an EnvironmentError: [WinError 5] Access is denied: 'C:\\Users\\username\\AppData\\Local\\Temp\\pip-uninstall-f5a7rk2y\\cli.exe'
Consider using the `--user` option or check the permissions.

我曾假设这是因为当 pip 尝试删除它时,错误文本中调用的 cli.exe 当前正在运行,但是这里的路径不是该 exe 所在的 %LOCALAPPDATA%\Programs\Python\Python36\Scripts,而是而不是%TEMP%。如何允许将文件移动到那里,但一旦存在就不能删除它?

按照错误消息的建议在安装参数中包含--user 是否(与先前编辑此问题的指示相反)解决了问题,但是将 cli 可执行文件移到其他地方确实可以。

我希望得到这样的答案:

  1. 解释了无法从 TEMP 目录中删除可执行文件的根本问题,以及...
  2. 提供问题的解决方案,绕过权限错误,或查询此包是否以用户身份安装,以便代码可以将 --user 添加到 args。

虽然这个问题相当笼统,但下面是一个 MCVE:

def update(piphost):
    args = ['pip', 'install',
        '--index-url', piphost,
        '-U', 'cli']
    subprocess.check_call(args)

update('https://mypypiserver:8001')

【问题讨论】:

    标签: python permissions pip


    【解决方案1】:

    正如最初推测的那样,这里的问题是试图删除正在运行的可执行文件。 Windows 不喜欢这种废话,当你尝试时会抛出PermissionErrors。不过奇怪的是,您绝对可以重命名一个正在运行的可执行文件,实际上有几个questions from different tags 使用这个事实来允许对正在运行的可执行文件进行明显的更改。

    这也解释了为什么可执行文件似乎从%LOCALAPPDATA%\Programs\Python\Python36\Scripts 运行,但未能从%TEMP% 删除。在执行期间它已被重命名(移动)到%TEMP% 文件夹(这是合法的),然后 pip 尝试删除该目录,同时删除该文件(这是非法的)。

    实现如下:

    1. 重命名当前可执行文件 (Path(sys.argv[0]).with_suffix('.exe'))
    2. pip install更新包
    3. 向入口点添加逻辑,删除重命名的可执行文件(如果存在)。
    import click  # I'm using click for my CLI, but YMMV
    from pathlib import Path
    from sys import argv
    
    def entrypoint():
        # setup.py's console_scripts points cli.exe to here
    
        tmp_exe_path = Path(argv[0]).with_suffix('.tmp')
        try:
            tmp_exe_path.unlink()
        except FileNotFoundError:
            pass
        return cli_root
    
    @click.group()
    def cli_root():
        pass
    
    def update(pip_host):
    
        exe_path = Path(argv[0])
        tmp_exe_path = exe_path.with_suffix('.tmp')
        handle_renames = False
        if exe_path.with_suffix('.exe').exists():
            # we're running on Windows, so we have to deal with this tomfoolery.
            handle_renames = True
            exe_path.rename(tmp_exe_path)
        args = ['pip', 'install',
            '--index-url', piphost,
            '-U', 'cli']
        try:
            subprocess.check_call(args)
        except Exception:  # in real code you should probably break these out to handle stuff
            if handle_renames:
                tmp_exe_path.rename(exe_path)  # undo the rename if we haven't updated
    
    @cli_root.command('update')
    @click.option("--host", default='https://mypypiserver:8001')
    def cli_update(host):
        update(host)
    

    【讨论】:

      猜你喜欢
      • 2021-06-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-11
      • 1970-01-01
      • 2012-06-21
      • 2016-04-26
      • 2016-06-30
      相关资源
      最近更新 更多