Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setuptools: How to make sure file generated by packed code be deleted by pip

I have a simple code named main.py that generates a folder and a file within it:

import os
def main():
    path = os.path.join(os.path.dirname(__file__), 'folder')

    if not os.path.isdir(path):
        os.mkdir(path)

    with open(os.path.join(path, 'file.txt'), 'w+') as f:
        f.write('something')


if __name__ == '__main__':
    main()

If this script run in a folder, then the structure should be like:

.
├── main.py
└── folder
    └── file.txt

I use a setup.py file to pack the code up, where I assigned an entry point named foo. The setup.py looks like:

from setuptools import setup  
setup(
    name='mypack',
    entry_points={
        'console_scripts': ['foo=mypack.main:main'],
    },
    packages=['mypack'],
    package_data={'': '*.txt'},
    include_package_data=True
)

I packed the code with the structure below:

.
├── mypack
│   └── main.py
└── setup.py

And I install it by pip install . in the root directory. Then I run foo, it indeed generated a folder and a file. Then I uninstall it by pip uninstall mypack, the output in the terminal shows :

Uninstalling mypack-0.0.0:
  Would remove:
    d:\programdata\envs\testsetup\lib\site-packages\mypack-0.0.0-py3.6.egg-info
    d:\programdata\envs\testsetup\lib\site-packages\mypack\main.py
    d:\programdata\envs\testsetup\scripts\foo-script.py
    d:\programdata\envs\testsetup\scripts\foo.exe
Proceed (y/n)? y
  Successfully uninstalled mypack-0.0.0

The folder\file.txt didn't deleted by pip uninstall, also the output of it didn't mention anything about folder\file.txt


Then I pack the code with the structure below:

.
├── mypack
│   ├── __init__.py
│   └── main.py
└── setup.py

Where the __init__.py was an empty file. I did the same procedure, that is: pip install . -> foo -> pip uninstall mypack, then the output in the terminal is:

Uninstalling mypack-0.0.0:
  Would remove:
    d:\programdata\envs\testsetup\lib\site-packages\mypack-0.0.0-py3.6.egg-info
    d:\programdata\envs\testsetup\lib\site-packages\mypack\*
    d:\programdata\envs\testsetup\scripts\foo-script.py
    d:\programdata\envs\testsetup\scripts\foo.exe
  Would not remove (might be manually added):
    d:\programdata\envs\testsetup\lib\site-packages\mypack\folder\file.txt
Proceed (y/n)? y
  Successfully uninstalled mypack-0.0.0

This time, folder\file.txt was mentioned in the output of pip uninstall, but was not automatically removed by pip.


So my questions:

  • Why there is a difference between with and without __init__.py
  • Is there any method that can make pip automatically remove folder\file.txt like that in my example?
like image 905
Sean_Syue Avatar asked Jun 21 '18 04:06

Sean_Syue


2 Answers

At line 381 of pip/blob/master/src/pip/_internal/req/req_uninstall.py:

if not verbose:
            will_remove, will_skip = compress_for_output_listing(self.paths)
        else:
            # In verbose mode, display all the files that are going to be
            # deleted.
            will_remove = list(self.paths)
            will_skip = set()

        _display('Would remove:', will_remove)
        _display('Would not remove (might be manually added):', will_skip)
        _display('Would not remove (outside of prefix):', self._refuse)

You can see compress_for_output_listing seperating files to be deleted from files to keep, except if verbose is True, it seems to delete them all. It's a bummer though, since verbose seems to be an internal function argument, and there is no command-line argument to set it, and would require changes to the source code for it to delete all files instead.

like image 88
jackw11111 Avatar answered Oct 04 '22 01:10

jackw11111


The __init__.py file tells python that the directory it is in is a module, and to look at all the sub-modules located within. This makes pip think that it is a module, therefore mentioning everything inside it.

From the documentation:

The init.py files are required to make Python treat the directories as containing packages; this is done to prevent directories with a common name, such as string, from unintentionally hiding valid modules that occur later (deeper) on the module search path. In the simplest case, init.py can just be an empty file

For your second question, my answer is maybe. I say this because, there is no way I know for sure that this is possible.

P.S: I don't know for sure(because I have not tested it), but maybe you could change file.txt's extension to .py maybe fooling pip into removing that.

like image 43
xilpex Avatar answered Oct 04 '22 01:10

xilpex