【问题标题】:pip install misses some generated files when writing installed-files.txtpip install 在写入 installed-files.txt 时遗漏了一些生成的文件
【发布时间】:2014-09-05 13:21:44
【问题描述】:

pip installing 具有自定义 build_py 命令的项目时,该命令会在构建目录中生成一个附加文件,安装时 pip 生成的 installed-files.txt 文件不会列出生成的文件。结果,当我卸载发行版时,它会留下我生成的文件。

我想我无法以某种方式注册生成的文件,但我找不到任何有关如何执行此操作的文档。

我必须进行哪些更改才能使 pip 的 installed-files.txt 列出我生成的文件?

复制步骤

创建以下文件系统条目。

已安装的文件缺失项目 ├── install-entry-missing │   └── __init__.py └── setup.py

将以下内容放入setup.py。

import os

from setuptools import setup
from setuptools.command.build_py import build_py


def touch(fname, times=None):
    with open(fname, 'a'):
        os.utime(fname, times)


class my_build_py(build_py):
    def run(self):
        if not self.dry_run:
            target_dir = os.path.join(self.build_lib, "install-entry-missing")
            self.mkpath(target_dir)
            touch(os.path.join(target_dir, "my_file.txt"))
            # TODO: missing registration of "my_file.txt"?
        build_py.run(self)


setup_args = dict(
    name='install-entry-missing',
    version='1.0.0',
    description='',
    author='author',
    author_email='author@example.com',
    packages = ["install-entry-missing"],
    cmdclass={'build_py': my_build_py}
)


if __name__ == '__main__':
    setup(**setup_args)

install-entry-missing-project 目录,运行pip install .

安装的目录将包含 my_file.txt 和 __init__.py。但是,检查 egg-info 目录的 installed-files.txt 将显示 my_file.txt 未列出。因此,pip uninstall install-entry-missing 将删除 __init__.py 而不是 my_file.txt。

【问题讨论】:

    标签: python python-2.7 pip


    【解决方案1】:

    可以通过覆盖build_pyget_outputs 方法而不是使用install_egg_info 来获得比Eric's answer 中建议的更简洁的代码:

    import os
    
    from setuptools import setup
    from setuptools.command.build_py import build_py
    
    
    def touch(fname, times=None):
        with open(fname, 'a'):
            os.utime(fname, times)
    
    
    class my_build_py(build_py):
        def run(self):
            self.my_outputs = []
            if not self.dry_run:
                target_dir = os.path.join(self.build_lib, "install-entry-missing")
                self.mkpath(target_dir)
                output = os.path.join(target_dir, "my_file.txt")
                touch(output)
                self.my_outputs.append(output)
            build_py.run(self)
    
        def get_outputs(self):
            outputs = build_py.get_outputs(self)
            outputs.extend(self.my_outputs)
            return outputs
    
    
    setup_args = dict(
        name='install-entry-missing',
        version='1.0.0',
        description='',
        author='author',
        author_email='author@example.com',
        packages = ["install-entry-missing"],
        cmdclass={'build_py': my_build_py}
    )
    
    
    if __name__ == '__main__':
        setup(**setup_args)
    

    虽然Eric's answer 中使用的方法有效,但覆盖install_egg_info 以更新installed-files.txt 在语义上是可疑的,因为您正在更新与已安装内容相关的文件列表在 egg-info 目录中。覆盖 build_py 中的 get_outputs 可使更改文件列表更接近它们的生成位置,并且更容易处理生成的文件仅在运行时确定的情况。

    【讨论】:

      【解决方案2】:

      有必要覆盖install_egg_info 命令并将一个条目附加到其self.outputs 列表中。 self.outputs 列表中的每个条目都会在最终的 installed-files.txt 中生成一个条目。

      修改上面的代码,正确的解决方法是:

      import os
      
      from setuptools import setup
      from setuptools.command.build_py import build_py
      from setuptools.command.install_egg_info import install_egg_info
      
      
      def touch(fname, times=None):
          with open(fname, 'a'):
              os.utime(fname, times)
      
      
      class my_build_py(build_py):
          def run(self):
              if not self.dry_run:
                  target_dir = os.path.join(self.build_lib, "install-entry-missing")
                  self.mkpath(target_dir)
                  touch(os.path.join(target_dir, "my_file.txt"))
                  # this file will be registered in my_install_egg_info
              build_py.run(self)
      
      
      class my_install_egg_info(install_egg_info):
          def run(self):
              install_egg_info.run(self)
              target_path = os.path.join(self.install_dir, "my_file.txt")
              self.outputs.append(target_path)
      
      
      setup_args = dict(
          name='install-entry-missing',
          version='1.0.0',
          description='',
          author='author',
          author_email='author@example.com',
          packages = ["install-entry-missing"],
          cmdclass={'build_py': my_build_py,
                    'install_egg_info': my_install_egg_info}
      )
      
      
      if __name__ == '__main__':
          setup(**setup_args)
      

      【讨论】:

        【解决方案3】:

        我遇到了与本文所述的问题类似的问题。上述解决方案均无效。我创建一个符号链接作为 setup.py 的安装后步骤。

        我按照 pip 源代码中的代码读取installed-files.txt 以查看发生了什么。事实证明,由于我的文件是一个符号链接,它被忽略了。在 pip/req/req_uninstall.py 的第 50 行,对 normalise_path 的调用遵循符号链接并将链接文件添加到要删除的路径列表中。链接本身不会添加到要删除的文件列表中,因此会被安装丢弃并留下。

        作为一种解决方法,我将符号链接更改为硬链接,pip 现在完全卸载了我的包。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-02-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多