Commit 0bd14855 by willmcgugan

fs.path optimizations since these functions are called so frequently

parent 53e8bd02
...@@ -32,13 +32,13 @@ class MemoryFile(object): ...@@ -32,13 +32,13 @@ class MemoryFile(object):
def seek_and_lock(f): def seek_and_lock(f):
def deco(self, *args, **kwargs): def deco(self, *args, **kwargs):
try: try:
self.lock.acquire() self._lock.acquire()
self.mem_file.seek(self.pos) self.mem_file.seek(self.pos)
ret = f(self, *args, **kwargs) ret = f(self, *args, **kwargs)
self.pos = self.mem_file.tell() self.pos = self.mem_file.tell()
return ret return ret
finally: finally:
self.lock.release() self._lock.release()
return deco return deco
def __init__(self, path, memory_fs, mem_file, mode, lock): def __init__(self, path, memory_fs, mem_file, mode, lock):
...@@ -47,7 +47,7 @@ class MemoryFile(object): ...@@ -47,7 +47,7 @@ class MemoryFile(object):
self.memory_fs = memory_fs self.memory_fs = memory_fs
self.mem_file = mem_file self.mem_file = mem_file
self.mode = mode self.mode = mode
self.lock = lock self._lock = lock
self.pos = 0 self.pos = 0
...@@ -59,7 +59,7 @@ class MemoryFile(object): ...@@ -59,7 +59,7 @@ class MemoryFile(object):
finally: finally:
lock.release() lock.release()
if _check_mode(mode, 'w'): elif _check_mode(mode, 'w'):
lock.acquire() lock.acquire()
try: try:
self.mem_file.seek(0) self.mem_file.seek(0)
...@@ -96,12 +96,17 @@ class MemoryFile(object): ...@@ -96,12 +96,17 @@ class MemoryFile(object):
def readline(self, *args, **kwargs): def readline(self, *args, **kwargs):
return self.mem_file.readline(*args, **kwargs) return self.mem_file.readline(*args, **kwargs)
#@seek_and_lock
def close(self): def close(self):
if not self.closed and self.mem_file is not None: do_close = False
self.memory_fs._on_close_memory_file(self, self.path) self._lock.acquire()
try:
do_close = not self.closed and self.mem_file is not None
if do_close:
self.closed = True self.closed = True
finally:
self._lock.release()
if do_close:
self.memory_fs._on_close_memory_file(self, self.path)
@seek_and_lock @seek_and_lock
def read(self, size=None): def read(self, size=None):
...@@ -124,13 +129,13 @@ class MemoryFile(object): ...@@ -124,13 +129,13 @@ class MemoryFile(object):
#@seek_and_lock #@seek_and_lock
def write(self, data): def write(self, data):
self.memory_fs._on_modify_memory_file(self.path) self.memory_fs._on_modify_memory_file(self.path)
self.lock.acquire() self._lock.acquire()
try: try:
self.mem_file.seek(self.pos) self.mem_file.seek(self.pos)
self.mem_file.write(data) self.mem_file.write(data)
self.pos = self.mem_file.tell() self.pos = self.mem_file.tell()
finally: finally:
self.lock.release() self._lock.release()
@seek_and_lock @seek_and_lock
def writelines(self, *args, **kwargs): def writelines(self, *args, **kwargs):
...@@ -385,6 +390,7 @@ class MemoryFS(FS): ...@@ -385,6 +390,7 @@ class MemoryFS(FS):
@synchronize @synchronize
def open(self, path, mode="r", **kwargs): def open(self, path, mode="r", **kwargs):
path = normpath(path)
filepath, filename = pathsplit(path) filepath, filename = pathsplit(path)
parent_dir_entry = self._get_dir_entry(filepath) parent_dir_entry = self._get_dir_entry(filepath)
...@@ -432,13 +438,12 @@ class MemoryFS(FS): ...@@ -432,13 +438,12 @@ class MemoryFS(FS):
raise ResourceInvalidError(path,msg="That's a directory, not a file: %(path)s") raise ResourceInvalidError(path,msg="That's a directory, not a file: %(path)s")
pathname, dirname = pathsplit(path) pathname, dirname = pathsplit(path)
parent_dir = self._get_dir_entry(pathname) parent_dir = self._get_dir_entry(pathname)
del parent_dir.contents[dirname] del parent_dir.contents[dirname]
@synchronize @synchronize
def removedir(self, path, recursive=False, force=False): def removedir(self, path, recursive=False, force=False):
path = normpath(path)
dir_entry = self._get_dir_entry(path) dir_entry = self._get_dir_entry(path)
if dir_entry is None: if dir_entry is None:
...@@ -462,7 +467,9 @@ class MemoryFS(FS): ...@@ -462,7 +467,9 @@ class MemoryFS(FS):
@synchronize @synchronize
def rename(self, src, dst): def rename(self, src, dst):
src_dir,src_name = pathsplit(src) src = normpath(src)
dst = normpath(dst)
src_dir, src_name = pathsplit(src)
src_entry = self._get_dir_entry(src) src_entry = self._get_dir_entry(src)
if src_entry is None: if src_entry is None:
raise ResourceNotFoundError(src) raise ResourceNotFoundError(src)
...@@ -504,12 +511,14 @@ class MemoryFS(FS): ...@@ -504,12 +511,14 @@ class MemoryFS(FS):
@synchronize @synchronize
def _on_close_memory_file(self, open_file, path): def _on_close_memory_file(self, open_file, path):
dir_entry = self._get_dir_entry(path) dir_entry = self._get_dir_entry(path)
if dir_entry is not None:
dir_entry.open_files.remove(open_file) dir_entry.open_files.remove(open_file)
@synchronize @synchronize
def _on_modify_memory_file(self, path): def _on_modify_memory_file(self, path):
dir_entry = self._get_dir_entry(path) dir_entry = self._get_dir_entry(path)
if dir_entry is not None:
dir_entry.modified_time = datetime.datetime.now() dir_entry.modified_time = datetime.datetime.now()
@synchronize @synchronize
......
...@@ -37,25 +37,21 @@ def normpath(path): ...@@ -37,25 +37,21 @@ def normpath(path):
if not path: if not path:
return path return path
components = [] components = []
for comp in path.replace('\\','/').split("/"): append = components.append
if not comp or comp == ".": for comp in [c for c in path.replace('\\','/').split("/") if c not in ('', '.')]:
pass if comp == "..":
elif comp == "..":
try: try:
components.pop() components.pop()
except IndexError: except IndexError:
err = "too many backrefs in path '%s'" % (path,) err = "too many backrefs in path '%s'" % (path,)
raise ValueError(err) raise ValueError(err)
else: else:
components.append(comp) append(comp)
if path[0] in "\\/": if path[0] in '\\/':
if not components: if not components:
components = [""] append("")
components.insert(0, "") components.insert(0, "")
if isinstance(path, unicode): return "/".join(components)
return u"/".join(components)
else:
return '/'.join(components)
def iteratepath(path, numsplits=None): def iteratepath(path, numsplits=None):
...@@ -69,9 +65,9 @@ def iteratepath(path, numsplits=None): ...@@ -69,9 +65,9 @@ def iteratepath(path, numsplits=None):
if not path: if not path:
return [] return []
if numsplits == None: if numsplits == None:
return map(None, path.split('/')) return path.split('/')
else: else:
return map(None, path.split('/', numsplits)) return path.split('/', numsplits)
def recursepath(path, reverse=False): def recursepath(path, reverse=False):
"""Returns intermediate paths from the root to the given path """Returns intermediate paths from the root to the given path
...@@ -84,11 +80,13 @@ def recursepath(path, reverse=False): ...@@ -84,11 +80,13 @@ def recursepath(path, reverse=False):
""" """
if reverse: if reverse:
paths = [] paths = []
append = paths.append
path = abspath(normpath(path)).rstrip("/") path = abspath(normpath(path)).rstrip("/")
while path: while path:
paths.append(path) append(path)
path = dirname(path).rstrip("/") path = dirname(path).rstrip("/")
return paths + [u"/"] paths.append(u"/")
return paths
else: else:
paths = [u""] + list(iteratepath(path)) paths = [u""] + list(iteratepath(path))
return [u"/"] + [u'/'.join(paths[:i+1]) for i in xrange(1,len(paths))] return [u"/"] + [u'/'.join(paths[:i+1]) for i in xrange(1,len(paths))]
...@@ -100,8 +98,6 @@ def abspath(path): ...@@ -100,8 +98,6 @@ def abspath(path):
adds a leading '/' character if the path doesn't already have one. adds a leading '/' character if the path doesn't already have one.
""" """
if not path:
return u'/'
if not path.startswith('/'): if not path.startswith('/'):
return u'/' + path return u'/' + path
return path return path
...@@ -173,10 +169,10 @@ def pathsplit(path): ...@@ -173,10 +169,10 @@ def pathsplit(path):
('/foo/bar', 'baz') ('/foo/bar', 'baz')
""" """
split = normpath(path).rsplit('/', 1) if '/' not in path:
if len(split) == 1: return ('', path)
return (u'', split[0]) split = path.rsplit('/', 1)
return split[0] or '/', split[1] return (split[0] or '/', split[1])
# Allow pathsplit() to be used as fs.path.split() # Allow pathsplit() to be used as fs.path.split()
split = pathsplit split = pathsplit
...@@ -211,14 +207,14 @@ def isdotfile(path): ...@@ -211,14 +207,14 @@ def isdotfile(path):
>>> isdotfile('.baz') >>> isdotfile('.baz')
True True
>>> isdotfile('foo/bar/.baz') >>> isdotfile('foo/bar/baz')
True True
>>> isdotfile('foo/bar.baz') >>> isdotfile('foo/bar.baz').
False False
""" """
return pathsplit(path)[-1].startswith('.') return basename(path).startswith('.')
def dirname(path): def dirname(path):
"""Returns the parent directory of a path. """Returns the parent directory of a path.
...@@ -232,7 +228,9 @@ def dirname(path): ...@@ -232,7 +228,9 @@ def dirname(path):
'foo/bar' 'foo/bar'
""" """
return pathsplit(path)[0] if '/' not in path:
return ''
return path.rsplit('/', 1)[0]
def basename(path): def basename(path):
...@@ -247,7 +245,9 @@ def basename(path): ...@@ -247,7 +245,9 @@ def basename(path):
'baz' 'baz'
""" """
return pathsplit(path)[1] if '/' not in path:
return path
return path.rsplit('/', 1)[-1]
def issamedir(path1, path2): def issamedir(path1, path2):
...@@ -262,7 +262,7 @@ def issamedir(path1, path2): ...@@ -262,7 +262,7 @@ def issamedir(path1, path2):
False False
""" """
return pathsplit(normpath(path1))[0] == pathsplit(normpath(path2))[0] return dirname(normpath(path1)) == dirname(normpath(path2))
def isbase(path1, path2): def isbase(path1, path2):
p1 = forcedir(abspath(path1)) p1 = forcedir(abspath(path1))
......
...@@ -103,6 +103,37 @@ class TestPathFunctions(unittest.TestCase): ...@@ -103,6 +103,37 @@ class TestPathFunctions(unittest.TestCase):
self.assertEquals(recursepath("hello",reverse=True),["/hello","/"]) self.assertEquals(recursepath("hello",reverse=True),["/hello","/"])
self.assertEquals(recursepath("",reverse=True),["/"]) self.assertEquals(recursepath("",reverse=True),["/"])
def test_isdotfile(self):
for path in ['.foo',
'.svn',
'foo/.svn',
'foo/bar/.svn',
'/foo/.bar']:
self.assert_(isdotfile(path))
for path in ['asfoo',
'df.svn',
'foo/er.svn',
'foo/bar/test.txt',
'/foo/bar']:
self.assertFalse(isdotfile(path))
def test_dirname(self):
tests = [('foo', ''),
('foo/bar', 'foo'),
('foo/bar/baz', 'foo/bar'),
('/', '')]
for path, test_dirname in tests:
self.assertEqual(dirname(path), test_dirname)
def test_basename(self):
tests = [('foo', 'foo'),
('foo/bar', 'bar'),
('foo/bar/baz', 'baz'),
('/', '')]
for path, test_basename in tests:
self.assertEqual(basename(path), test_basename)
class Test_PathMap(unittest.TestCase): class Test_PathMap(unittest.TestCase):
......
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