Commit bbf954c9 authored by Jim Fulton's avatar Jim Fulton

Read-only files can't be removed on Windows. :(

Added APIs for removing committed blob data that makes files writable
before removing them and updated code that removes committed blobs or
blob directories to use them.
parent 144499dc
...@@ -30,6 +30,7 @@ import shutil ...@@ -30,6 +30,7 @@ import shutil
# ZODB test support # ZODB test support
import ZODB import ZODB
import ZODB.blob
import ZODB.tests.util import ZODB.tests.util
from ZODB.tests.MinPO import MinPO from ZODB.tests.MinPO import MinPO
from ZODB.tests.StorageTestBase import zodb_unpickle from ZODB.tests.StorageTestBase import zodb_unpickle
...@@ -164,7 +165,7 @@ class GenericTests( ...@@ -164,7 +165,7 @@ class GenericTests(
def tearDown(self): def tearDown(self):
self._storage.close() self._storage.close()
os.remove(self._conf_path) os.remove(self._conf_path)
shutil.rmtree(self.blob_cache_dir) ZODB.blob.remove_committed_dir(self.blob_cache_dir)
for server in self._servers: for server in self._servers:
forker.shutdown_zeo_server(server) forker.shutdown_zeo_server(server)
if hasattr(os, 'waitpid'): if hasattr(os, 'waitpid'):
...@@ -616,7 +617,7 @@ class BlobAdaptedFileStorageTests(GenericTests, CommonBlobTests): ...@@ -616,7 +617,7 @@ class BlobAdaptedFileStorageTests(GenericTests, CommonBlobTests):
calls.append((oid, serial)) calls.append((oid, serial))
sendBlob_org(self, oid, serial) sendBlob_org(self, oid, serial)
os.remove(filename) ZODB.blob.remove_committed(filename)
returns = [] returns = []
threads = [ threads = [
threading.Thread( threading.Thread(
......
...@@ -1245,7 +1245,7 @@ class TmpStore: ...@@ -1245,7 +1245,7 @@ class TmpStore:
os.makedirs(targetpath, 0700) os.makedirs(targetpath, 0700)
targetname = self._getCleanFilename(oid, serial) targetname = self._getCleanFilename(oid, serial)
rename_or_copy_blob(blobfilename, targetname) rename_or_copy_blob(blobfilename, targetname, chmod=False)
def loadBlob(self, oid, serial): def loadBlob(self, oid, serial):
"""Return the filename where the blob file can be found. """Return the filename where the blob file can be found.
......
...@@ -189,7 +189,7 @@ class Blob(persistent.Persistent): ...@@ -189,7 +189,7 @@ class Blob(persistent.Persistent):
target = self._create_uncommitted_file() target = self._create_uncommitted_file()
# We need to unlink the freshly created target again # We need to unlink the freshly created target again
# to allow link() to do its job # to allow link() to do its job
os.unlink(target) os.remove(target)
try: try:
rename_or_copy_blob(filename, target, chmod=False) rename_or_copy_blob(filename, target, chmod=False)
...@@ -198,7 +198,7 @@ class Blob(persistent.Persistent): ...@@ -198,7 +198,7 @@ class Blob(persistent.Persistent):
# might exist and mark the pointer to the uncommitted file. # might exist and mark the pointer to the uncommitted file.
self._p_blob_uncommitted = None self._p_blob_uncommitted = None
if os.path.exists(target): if os.path.exists(target):
os.unlink(target) os.remove(target)
# If there was a file moved aside, bring it back including the # If there was a file moved aside, bring it back including the
# pointer to the uncommitted file. # pointer to the uncommitted file.
...@@ -212,7 +212,7 @@ class Blob(persistent.Persistent): ...@@ -212,7 +212,7 @@ class Blob(persistent.Persistent):
if previous_uncommitted: if previous_uncommitted:
# The relinking worked so we can remove the data that we had # The relinking worked so we can remove the data that we had
# set aside. # set aside.
os.unlink(target_aside) os.remove(target_aside)
# We changed the blob state and have to make sure we join the # We changed the blob state and have to make sure we join the
# transaction. # transaction.
...@@ -448,7 +448,7 @@ class BlobStorage(SpecificationDecoratorBase): ...@@ -448,7 +448,7 @@ class BlobStorage(SpecificationDecoratorBase):
oid, serial = self.dirty_oids.pop() oid, serial = self.dirty_oids.pop()
clean = self.fshelper.getBlobFilename(oid, serial) clean = self.fshelper.getBlobFilename(oid, serial)
if os.exists(clean): if os.exists(clean):
os.unlink(clean) remove_committed(clean)
@non_overridable @non_overridable
def loadBlob(self, oid, serial): def loadBlob(self, oid, serial):
...@@ -487,7 +487,7 @@ class BlobStorage(SpecificationDecoratorBase): ...@@ -487,7 +487,7 @@ class BlobStorage(SpecificationDecoratorBase):
fn = self.fshelper.getBlobFilename(oid, serial) fn = self.fshelper.getBlobFilename(oid, serial)
self.loadSerial(oid, serial) self.loadSerial(oid, serial)
except POSKeyError: except POSKeyError:
os.unlink(filepath) remove_committed(filepath)
if not os.listdir(oid_path): if not os.listdir(oid_path):
shutil.rmtree(oid_path) shutil.rmtree(oid_path)
...@@ -511,9 +511,9 @@ class BlobStorage(SpecificationDecoratorBase): ...@@ -511,9 +511,9 @@ class BlobStorage(SpecificationDecoratorBase):
latest = files[-1] # depends on ever-increasing tids latest = files[-1] # depends on ever-increasing tids
files.remove(latest) files.remove(latest)
for file in files: for file in files:
os.unlink(os.path.join(oid_path, file)) remove_committed(os.path.join(oid_path, file))
else: else:
shutil.rmtree(oid_path) remove_committed_dir(oid_path)
continue continue
if not os.listdir(oid_path): if not os.listdir(oid_path):
...@@ -630,6 +630,24 @@ def rename_or_copy_blob(f1, f2, chmod=True): ...@@ -630,6 +630,24 @@ def rename_or_copy_blob(f1, f2, chmod=True):
finally: finally:
file1.close() file1.close()
file2.close() file2.close()
os.unlink(f1) remove_committed(f1)
if chmod: if chmod:
os.chmod(f2, stat.S_IREAD) os.chmod(f2, stat.S_IREAD)
if sys.platform == 'win32':
# On Windows, you can't remove read-only files, so make the
# file writable first.
def remove_committed(filename):
os.chmod(filename, stat.S_IWRITE)
os.remove(filename)
def remove_committed_dir(path):
for (dirpath, dirnames, filenames) in os.walk(path):
for filename in filenames:
filename = os.path.join(dirpath, filename)
remove_committed(filename)
shutil.rmtree(path)
else:
remove_committed = os.remove
remove_committed_dir = shutil.rmtree
...@@ -76,8 +76,6 @@ You can't put blobs into a database that has uses a Non-Blob-Storage, though: ...@@ -76,8 +76,6 @@ You can't put blobs into a database that has uses a Non-Blob-Storage, though:
While we are testing this, we don't need the storage directory and While we are testing this, we don't need the storage directory and
databases anymore: databases anymore:
>>> import shutil
>>> shutil.rmtree(blob_dir)
>>> transaction.abort() >>> transaction.abort()
>>> database.close() >>> database.close()
>>> database2.close() >>> database2.close()
...@@ -90,8 +90,9 @@ Clean up our blob directory: ...@@ -90,8 +90,9 @@ Clean up our blob directory:
>>> base_storage1.close() >>> base_storage1.close()
>>> base_storage2.close() >>> base_storage2.close()
>>> shutil.rmtree(blob_dir1) >>> import ZODB.blob
>>> shutil.rmtree(blob_dir2) >>> ZODB.blob.remove_committed_dir(blob_dir1)
>>> ZODB.blob.remove_committed_dir(blob_dir2)
>>> os.unlink(exportfile) >>> os.unlink(exportfile)
>>> os.unlink(storagefile1) >>> os.unlink(storagefile1)
>>> os.unlink(storagefile1+".index") >>> os.unlink(storagefile1+".index")
......
...@@ -316,8 +316,6 @@ Teardown ...@@ -316,8 +316,6 @@ Teardown
We don't need the storage directory and databases anymore:: We don't need the storage directory and databases anymore::
>>> import shutil
>>> shutil.rmtree(blob_dir)
>>> tm1.abort() >>> tm1.abort()
>>> tm2.abort() >>> tm2.abort()
>>> database.close() >>> database.close()
...@@ -19,6 +19,7 @@ import ZODB.tests.util ...@@ -19,6 +19,7 @@ import ZODB.tests.util
from ZODB import utils from ZODB import utils
from ZODB.FileStorage import FileStorage from ZODB.FileStorage import FileStorage
from ZODB.blob import Blob, BlobStorage from ZODB.blob import Blob, BlobStorage
import ZODB.blob
from ZODB.DB import DB from ZODB.DB import DB
import transaction import transaction
...@@ -92,7 +93,7 @@ class BlobUndoTests(unittest.TestCase): ...@@ -92,7 +93,7 @@ class BlobUndoTests(unittest.TestCase):
def tearDown(self): def tearDown(self):
os.chdir(self.here) os.chdir(self.here)
shutil.rmtree(self.test_dir) ZODB.blob.remove_committed_dir(self.test_dir)
def testUndoWithoutPreviousVersion(self): def testUndoWithoutPreviousVersion(self):
base_storage = FileStorage(self.storagefile) base_storage = FileStorage(self.storagefile)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment