Failure to uninstall a python package from a pypy virtualenv on windows in develop mode

Viewed 298

TL;DR: Running python setup.py develop --uninstall from a pypy environment created using tox results in an exception: error: [Error 32] The process cannot access the file because it is being used by another process: c:\users\shach\code\pydocstyle\.tox\pypy\site-packages\funniest.egg-link.

Hello folks,

I have a set of integration tests for my python package that do the following:

  1. Call python setup.py develop (using subprocess.check_call)
  2. Run all the tests for the package
  3. Call python setup.py develop --uninstall (again, using subprocess.check_call)

The tests are being run by tox. Everything works fine on python versions 27, 33, 34, 35 and 36, but the code fails on pypy.

I'm not going to include the setup.py and project file here, you can assume they're fine. I've reproduced this using the minimal package shown here and it works (fails?) all the same.

To reproduce, I created a python script that runs the following:

import shlex
import subprocess

subprocess.check_call(shlex.split('python setup.py develop'))
print('----------')
subprocess.check_call(shlex.split('python setup.py develop --uninstall'))

Running the file using a regular pypy installed on the system works just fine:

C:\Users\shach\code\bla\funniest>pypy test.py
running develop
running egg_info
writing funniest.egg-info\PKG-INFO
writing dependency_links to funniest.egg-info\dependency_links.txt
writing top-level names to funniest.egg-info\top_level.txt
reading manifest file 'funniest.egg-info\SOURCES.txt'
writing manifest file 'funniest.egg-info\SOURCES.txt'
running build_ext
Creating c:\python\pypy\site-packages\funniest.egg-link (link to .)
Adding funniest 0.1 to easy-install.pth file

Installed c:\users\shach\code\bla\funniest
Processing dependencies for funniest==0.1
Finished processing dependencies for funniest==0.1
----------
running develop
Removing c:\python\pypy\site-packages\funniest.egg-link (link to .)
Removing funniest 0.1 from easy-install.pth file

C:\Users\shach\code\bla\funniest>

But when I run it from a tox environment for pypy:

C:\Users\shach\code\bla\funniest>tox
GLOB sdist-make: C:\Users\shach\code\bla\funniest\setup.py
pypy inst-nodeps: C:\Users\shach\code\bla\funniest\.tox\dist\funniest-0.1.zip
pypy installed: cffi==1.10.1,funniest==0.1,greenlet==0.4.12,readline==6.2.4.1
pypy runtests: PYTHONHASHSEED='122'
pypy runtests: commands[0] | python test.py
running develop
running egg_info
writing funniest.egg-info\PKG-INFO
writing dependency_links to funniest.egg-info\dependency_links.txt
writing top-level names to funniest.egg-info\top_level.txt
reading manifest file 'funniest.egg-info\SOURCES.txt'
writing manifest file 'funniest.egg-info\SOURCES.txt'
running build_ext
Creating c:\users\shach\code\bla\funniest\.tox\pypy\site-packages\funniest.egg-link (link to .)
funniest 0.1 is already the active version in easy-install.pth

Installed c:\users\shach\code\bla\funniest
Processing dependencies for funniest==0.1
Finished processing dependencies for funniest==0.1
----------
running develop
Removing c:\users\shach\code\bla\funniest\.tox\pypy\site-packages\funniest.egg-link (link to .)
error: [Error 32] The process cannot access the file because it is being used by another process: c:\users\shach\code\bla\funniest\.tox\pypy\site-packages\funniest.egg-link
Traceback (most recent call last):
  File "test.py", line 6, in <module>
    subprocess.check_call(shlex.split('python setup.py develop --uninstall'))
  File "C:\Python\pypy\lib-python\2.7\subprocess.py", line 186, in check_call
    raise CalledProcessError(retcode, cmd)
CalledProcessError: Command '['python', 'setup.py', 'develop', '--uninstall']' returned non-zero exit status 1
ERROR: InvocationError: 'C:\\Users\\shach\\code\\bla\\funniest\\.tox\\pypy\\bin\\python.EXE test.py'
___________________________________ summary ___________________________________
ERROR:   pypy: commands failed

C:\Users\shach\code\bla\funniest>

Here's the tox.ini I'm using to reproduce:

[tox]
envlist = pypy
[testenv]
commands=python test.py

I made sure that I have read/write permissions to the directory, and am kinda losing my mind here.

It works just fine on linux. Probably because you can remove a file that's being used (inodes, etc.) :^)

UPDATE 1:

I thought maybe the problem lies in that both develop and develop --uninstall are being run from the same python file, and that some resource is not being cleaned properly (perhaps an open file that locks the egg-link), so i ran it manually:

C:\Users\shach\code\bla\funniest>.tox\pypy\bin\activate.bat

(pypy) C:\Users\shach\code\bla\funniest>pypy setup.py develop
running develop
running egg_info
writing funniest.egg-info\PKG-INFO
writing dependency_links to funniest.egg-info\dependency_links.txt
writing top-level names to funniest.egg-info\top_level.txt
reading manifest file 'funniest.egg-info\SOURCES.txt'
writing manifest file 'funniest.egg-info\SOURCES.txt'
running build_ext
Creating c:\users\shach\code\bla\funniest\.tox\pypy\site-packages\funniest.egg-link (link to .)
funniest 0.1 is already the active version in easy-install.pth

Installed c:\users\shach\code\bla\funniest
Processing dependencies for funniest==0.1
Finished processing dependencies for funniest==0.1

(pypy) C:\Users\shach\code\bla\funniest>pypy setup.py develop --uninstall
running develop
Removing c:\users\shach\code\bla\funniest\.tox\pypy\site-packages\funniest.egg-link (link to .)
error: [Error 32] The process cannot access the file because it is being used by another process: c:\users\shach\code\bla\funniest\.tox\pypy\site-packages\funniest.egg-link

(pypy) C:\Users\shach\code\bla\funniest>

Still fails :(

Please advise!

Shachar.

UPDATE 2:

I've tried to check if there's a process holding that file using Sysinternal's Process Explorer, and nada. I manually ran pypy setup.py develop, then checked in Process Explorer and made sure the file is not opened in any process, and then ran pypy setup.py develop --uninstall and the same error was raised.

UPDATE 3:

Still happens after closing CMD and opening a new one.

UPDATE 4:

REBOOTING THE MACHINE DOES NOT SOLVE THIS! Wha-----?

2 Answers

From the description it would seem that somewhere in the running of pypy setup.py develop --uninstall, a file or directory is being held open, which prevents its deletion on windows. You can try to run pypy with "-X track-resources" argument (pypy -X track-resources setup.py develop --uninstall) which should notify you if an object holding resources is deallocated before the resource is closed. Sometimes scanning for code like s - open(path).read() may be enough to find the problem, that code will leak the file descriptor until the garbage collector runs, notices the ghost object, and deletes it. The code should use a context manager; with open(path) as fid: s = fid.read()

TL;DR: Don't use setup.py directly, use pip install -e ..

It seems that the problem is in setuptools on windows. What happens is that the egg-link file is opened&closed just a couple of lines before it's being removed - and windows can't handle that.

Somehow pip does handle this case, I'll update when I have more info.

I've opened an issue in setuptools.

Related