pixelb / crudini

A utility for manipulating ini files
GNU General Public License v2.0
443 stars 60 forks source link

Exception AttributeError: AttributeError("'NoneType' object has no attribute 'unlink'",) in <bound method LockedFile.__del__ of <__main__.LockedFile object at 0x26b8d50>> ignored #12

Closed gashev closed 10 years ago

gashev commented 10 years ago
% tox
GLOB sdist-make: /tmp/crudini/setup.py
py26 create: /tmp/crudini/.tox/py26
py26 installdeps: iniparse
py26 inst: /tmp/crudini/.tox/dist/crudini-0.4.zip
py26 runtests: PYTHONHASHSEED='3324528470'
py26 runtests: commands[0] | /bin/bash -c cd tests && ./test.sh
Test 1 OK (line 32)
Exception AttributeError: AttributeError("'NoneType' object has no attribute 'unlink'",) in <bound method LockedFile.__del__ of <__main__.LockedFile object at 0x26b8d50>> ignored
Test 2 OK (line 39)

tox stopped on test2 and process run:

% strace -p 13320
^[[6~Process 13320 attached - interrupt to quit
select(0, NULL, NULL, NULL, {0, 677151}) = 0 (Timeout)
open(".test.ini.crudini.lck", O_RDONLY|O_CREAT|O_EXCL, 0) = -1 EEXIST (File exists)
select(0, NULL, NULL, NULL, {1, 0})     = 0 (Timeout)
open(".test.ini.crudini.lck", O_RDONLY|O_CREAT|O_EXCL, 0) = -1 EEXIST (File exists)
select(0, NULL, NULL, NULL, {1, 0})     = 0 (Timeout)
open(".test.ini.crudini.lck", O_RDONLY|O_CREAT|O_EXCL, 0) = -1 EEXIST (File exists)
select(0, NULL, NULL, NULL, {1, 0})     = 0 (Timeout)
open(".test.ini.crudini.lck", O_RDONLY|O_CREAT|O_EXCL, 0) = -1 EEXIST (File exists)
select(0, NULL, NULL, NULL, {1, 0})     = 0 (Timeout)
open(".test.ini.crudini.lck", O_RDONLY|O_CREAT|O_EXCL, 0) = -1 EEXIST (File exists)
select(0, NULL, NULL, NULL, {1, 0})     = 0 (Timeout)
open(".test.ini.crudini.lck", O_RDONLY|O_CREAT|O_EXCL, 0) = -1 EEXIST (File exists)
select(0, NULL, NULL, NULL, {1, 0})     = 0 (Timeout)
open(".test.ini.crudini.lck", O_RDONLY|O_CREAT|O_EXCL, 0) = -1 EEXIST (File exists)
% uname -a
Linux hp 3.15.1 #2 SMP Sun Jun 22 18:33:35 UTC 2014 x86_64 GNU/Linux
% cat /etc/issue
Debian GNU/Linux 7 \n \l
pixelb commented 10 years ago

Weird. Can't repro here on 2.7 or python 2.6. Also I can't correlate the code to the exception message.

You'll need to remove the .test.ini.crudini.lck file manually

gashev commented 10 years ago

I need remove .test.ini.crudini.lck file after every test:

% tox
GLOB sdist-make: /tmp/crudini/setup.py
py26 create: /tmp/crudini/.tox/py26
py26 installdeps: iniparse
py26 inst: /tmp/crudini/.tox/dist/crudini-0.4.zip
py26 runtests: PYTHONHASHSEED='4056784320'
py26 runtests: commands[0] | /bin/bash -c cd tests && ./test.sh
Test 1 OK (line 32)
Exception AttributeError: AttributeError("'NoneType' object has no attribute 'unlink'",) in <bound method LockedFile.__del__ of <__main__.LockedFile object at 0x296cd50>> ignored
Test 2 OK (line 39)
Exception AttributeError: AttributeError("'NoneType' object has no attribute 'unlink'",) in <bound method LockedFile.__del__ of <__main__.LockedFile object at 0x1fb8d50>> ignored
Test 3 OK (line 44)
Exception AttributeError: AttributeError("'NoneType' object has no attribute 'unlink'",) in <bound method LockedFile.__del__ of <__main__.LockedFile object at 0x16cbd50>> ignored
Test 4 OK (line 50)
Exception AttributeError: AttributeError("'NoneType' object has no attribute 'unlink'",) in <bound method LockedFile.__del__ of <__main__.LockedFile object at 0x2024d50>> ignored
Test 5 OK (line 55)
pixelb commented 10 years ago

Right because the unlink isn't happening, But the code only does "os.unlink(...)", so it thinks that "os" is None ??

gashev commented 10 years ago

Yes, os is NoneType. You call os.unlink to del call. In this time os already destroyed?

pixelb commented 10 years ago

Drats so it's racy. It's surprising I didn't hit this in all the times I ran the test. Does the following address it for you?

diff --git a/crudini b/crudini
index 3daeb6c..ebe7a0e 100755
--- a/crudini
+++ b/crudini
@@ -8,6 +8,7 @@
 # under the terms of the GPLv2, the GNU General Public License version 2, as
 # published by the Free Software Foundation. http://gnu.org/licenses/gpl.html

+import atexit
 import os
 import sys
 import errno
@@ -323,6 +324,8 @@ class LockedFile(FileLock):
         self.filename = filename
         self.operation = operation

+        atexit.register(self.delete)
+
         FileLock.__init__(self, operation != "--get", not inplace)

         if inplace:
@@ -350,9 +353,10 @@ class LockedFile(FileLock):
             error(str(e))
             sys.exit(1)

-    def __del__(self):
+    def delete(self):
         # explicit close so closed in correct order
-        # if taking lock multiple times
+        # if taking lock multiple times or also avoiding
+        # os module being destroyed first etc.
         self.unlock()
         if self.fp:
             self.fp.close()
gashev commented 10 years ago

It's ok now. I ran tox some times, issue is not reproduced.

pixelb commented 10 years ago

Fixed in commit 7108dd7

Thanks for the hint and testing!