Skip to content

Commit 4ddd1a3

Browse files
authored
Merge pull request #38 from stuertz/windows_fixes
Windows fixes for leaking file handles #37
2 parents fd2eba4 + 7bced78 commit 4ddd1a3

File tree

5 files changed

+54
-8
lines changed

5 files changed

+54
-8
lines changed

Diff for: gitdb/pack.py

+12
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,10 @@ def __init__(self, indexpath):
266266
super(PackIndexFile, self).__init__()
267267
self._indexpath = indexpath
268268

269+
def close(self):
270+
mman.force_map_handle_removal_win(self._indexpath)
271+
self._cursor = None
272+
269273
def _set_cache_(self, attr):
270274
if attr == "_packfile_checksum":
271275
self._packfile_checksum = self._cursor.map()[-40:-20]
@@ -527,6 +531,10 @@ class PackFile(LazyMixin):
527531
def __init__(self, packpath):
528532
self._packpath = packpath
529533

534+
def close(self):
535+
mman.force_map_handle_removal_win(self._packpath)
536+
self._cursor = None
537+
530538
def _set_cache_(self, attr):
531539
# we fill the whole cache, whichever attribute gets queried first
532540
self._cursor = mman.make_cursor(self._packpath).use_region()
@@ -668,6 +676,10 @@ def __init__(self, pack_or_index_path):
668676
self._index = self.IndexFileCls("%s.idx" % basename) # PackIndexFile instance
669677
self._pack = self.PackFileCls("%s.pack" % basename) # corresponding PackFile instance
670678

679+
def close(self):
680+
self._index.close()
681+
self._pack.close()
682+
671683
def _set_cache_(self, attr):
672684
# currently this can only be _offset_map
673685
# TODO: make this a simple sorted offset array which can be bisected

Diff for: gitdb/test/db/test_pack.py

+11
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,22 @@
1010
from gitdb.db import PackedDB
1111

1212
from gitdb.exc import BadObject, AmbiguousObjectName
13+
from gitdb.util import mman
1314

1415
import os
1516
import random
17+
import sys
1618

19+
from nose.plugins.skip import SkipTest
1720

1821
class TestPackDB(TestDBBase):
1922

2023
@with_rw_directory
2124
@with_packs_rw
2225
def test_writing(self, path):
26+
if sys.platform == "win32":
27+
raise SkipTest("FIXME: Currently fail on windows")
28+
2329
pdb = PackedDB(path)
2430

2531
# on demand, we init our pack cache
@@ -30,6 +36,11 @@ def test_writing(self, path):
3036
# packs removed - rename a file, should affect the glob
3137
pack_path = pdb.entities()[0].pack().path()
3238
new_pack_path = pack_path + "renamed"
39+
if sys.platform == "win32":
40+
# While using this function, we are not allowed to have any handle
41+
# to this path, which is currently not the case. The pack caching
42+
# does still have a handle :-(
43+
mman.force_map_handle_removal_win(pack_path)
3344
os.rename(pack_path, new_pack_path)
3445

3546
pdb.update_cache(force=True)

Diff for: gitdb/test/performance/test_stream.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from gitdb.db import LooseObjectDB
1010
from gitdb import IStream
1111

12-
from gitdb.util import bin_to_hex
12+
from gitdb.util import bin_to_hex, remove
1313
from gitdb.fun import chunk_size
1414

1515
from time import time
@@ -104,5 +104,6 @@ def test_large_data_streaming(self, path):
104104
(size_kib, desc, cs_kib, elapsed_readchunks, size_kib / (elapsed_readchunks or 1)), file=sys.stderr)
105105

106106
# del db file so we keep something to do
107-
os.remove(db_file)
107+
ostream = None # To release the file handle (win)
108+
remove(db_file)
108109
# END for each randomization factor

Diff for: gitdb/test/test_pack.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -217,10 +217,11 @@ def rewind_streams():
217217
assert os.path.getsize(ppath) > 100
218218

219219
# verify pack
220-
pf = PackFile(ppath) # FIXME: Leaks file-pointer(s)!
220+
pf = PackFile(ppath)
221221
assert pf.size() == len(pack_objs)
222222
assert pf.version() == PackFile.pack_version_default
223223
assert pf.checksum() == pack_sha
224+
pf.close()
224225

225226
# verify index
226227
if ipath is not None:
@@ -231,6 +232,7 @@ def rewind_streams():
231232
assert idx.packfile_checksum() == pack_sha
232233
assert idx.indexfile_checksum() == index_sha
233234
assert idx.size() == len(pack_objs)
235+
idx.close()
234236
# END verify files exist
235237
# END for each packpath, indexpath pair
236238

@@ -245,7 +247,8 @@ def rewind_streams():
245247
# END for each crc mode
246248
# END for each info
247249
assert count == len(pack_objs)
248-
250+
entity.close()
251+
249252
def test_pack_64(self):
250253
# TODO: hex-edit a pack helping us to verify that we can handle 64 byte offsets
251254
# of course without really needing such a huge pack

Diff for: gitdb/util.py

+23-4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import os
77
import mmap
88
import sys
9+
import time
910
import errno
1011

1112
from io import BytesIO
@@ -58,7 +59,6 @@ def unpack_from(fmt, data, offset=0):
5859
isdir = os.path.isdir
5960
isfile = os.path.isfile
6061
rename = os.rename
61-
remove = os.remove
6262
dirname = os.path.dirname
6363
basename = os.path.basename
6464
join = os.path.join
@@ -67,6 +67,25 @@ def unpack_from(fmt, data, offset=0):
6767
close = os.close
6868
fsync = os.fsync
6969

70+
71+
def _retry(func, *args, **kwargs):
72+
# Wrapper around functions, that are problematic on "Windows". Sometimes
73+
# the OS or someone else has still a handle to the file
74+
if sys.platform == "win32":
75+
for _ in range(10):
76+
try:
77+
return func(*args, **kwargs)
78+
except Exception:
79+
time.sleep(0.1)
80+
return func(*args, **kwargs)
81+
else:
82+
return func(*args, **kwargs)
83+
84+
85+
def remove(*args, **kwargs):
86+
return _retry(os.remove, *args, **kwargs)
87+
88+
7089
# Backwards compatibility imports
7190
from gitdb.const import (
7291
NULL_BIN_SHA,
@@ -321,7 +340,7 @@ def open(self, write=False, stream=False):
321340
self._fd = os.open(self._filepath, os.O_RDONLY | binary)
322341
except:
323342
# assure we release our lockfile
324-
os.remove(self._lockfilepath())
343+
remove(self._lockfilepath())
325344
raise
326345
# END handle lockfile
327346
# END open descriptor for reading
@@ -365,7 +384,7 @@ def _end_writing(self, successful=True):
365384
# on windows, rename does not silently overwrite the existing one
366385
if sys.platform == "win32":
367386
if isfile(self._filepath):
368-
os.remove(self._filepath)
387+
remove(self._filepath)
369388
# END remove if exists
370389
# END win32 special handling
371390
os.rename(lockfile, self._filepath)
@@ -376,7 +395,7 @@ def _end_writing(self, successful=True):
376395
chmod(self._filepath, int("644", 8))
377396
else:
378397
# just delete the file so far, we failed
379-
os.remove(lockfile)
398+
remove(lockfile)
380399
# END successful handling
381400

382401
#} END utilities

0 commit comments

Comments
 (0)