Commit bc30657b by willmcgugan@gmail.com

Fixes for backslashes on Linux issue, see Issue #139

parent 2fbb136c
......@@ -42,6 +42,10 @@ from fs.path import *
from fs.local_functools import wraps
class InvalidPathError(Exception):
pass
class FSError(Exception):
"""Base exception class for the FS module."""
default_message = "Unspecified error"
......@@ -184,6 +188,7 @@ class ResourceLockedError(ResourceError):
"""Exception raised when a resource can't be used because it is locked."""
default_message = "Resource is locked: %(path)s"
class NoMMapError(ResourceError):
"""Exception raise when getmmap fails to create a mmap"""
default_message = "Can't get mmap for %(path)s"
......
......@@ -35,10 +35,12 @@ def _os_stat(path):
"""Replacement for os.stat that raises FSError subclasses."""
return os.stat(path)
@convert_os_errors
def _os_mkdir(name, mode=0777):
"""Replacement for os.mkdir that raises FSError subclasses."""
return os.mkdir(name,mode)
return os.mkdir(name, mode)
@convert_os_errors
def _os_makedirs(name, mode=0777):
......@@ -66,7 +68,6 @@ def _os_makedirs(name, mode=0777):
os.mkdir(name, mode)
class OSFS(OSFSXAttrMixin, OSFSWatchMixin, FS):
"""Expose the underlying operating-system filesystem as an FS object.
......@@ -126,9 +127,9 @@ class OSFS(OSFSXAttrMixin, OSFSWatchMixin, FS):
pass
if not os.path.exists(root_path):
raise ResourceNotFoundError(root_path,msg="Root directory does not exist: %(path)s")
raise ResourceNotFoundError(root_path, msg="Root directory does not exist: %(path)s")
if not os.path.isdir(root_path):
raise ResourceInvalidError(root_path,msg="Root path is not a directory: %(path)s")
raise ResourceInvalidError(root_path, msg="Root path is not a directory: %(path)s")
self.root_path = root_path
self.dir_mode = dir_mode
......@@ -147,10 +148,10 @@ class OSFS(OSFSXAttrMixin, OSFSWatchMixin, FS):
return p.decode(self.encoding, 'replace')
def getsyspath(self, path, allow_none=False):
path = relpath(normpath(path)).replace("/",os.sep)
path = relpath(normpath(path)).replace("/", os.sep)
path = os.path.join(self.root_path, path)
if not path.startswith(self.root_path):
raise PathError(path,msg="OSFS given path outside root: %(path)s")
raise PathError(path, msg="OSFS given path outside root: %(path)s")
path = self._decode_path(path)
return path
......@@ -173,7 +174,7 @@ class OSFS(OSFSXAttrMixin, OSFSWatchMixin, FS):
if not prefix.endswith(os.path.sep):
prefix += os.path.sep
if not os.path.normcase(path).startswith(prefix):
raise ValueError("path not within this FS: %s (%s)" % (os.path.normcase(path),prefix))
raise ValueError("path not within this FS: %s (%s)" % (os.path.normcase(path), prefix))
return normpath(path[len(self.root_path):])
def getmeta(self, meta_name, default=NoDefaultMeta):
......@@ -221,8 +222,8 @@ class OSFS(OSFSXAttrMixin, OSFSWatchMixin, FS):
raise
@convert_os_errors
def setcontents(self, path, contents, chunk_size=64*1024):
return super(OSFS,self).setcontents(path, contents, chunk_size)
def setcontents(self, path, contents, chunk_size=64 * 1024):
return super(OSFS, self).setcontents(path, contents, chunk_size)
@convert_os_errors
def exists(self, path):
......@@ -252,9 +253,9 @@ class OSFS(OSFSXAttrMixin, OSFSWatchMixin, FS):
_os_mkdir(sys_path, self.dir_mode)
except DestinationExistsError:
if self.isfile(path):
raise ResourceInvalidError(path,msg="Cannot create directory, there's already a file of that name: %(path)s")
raise ResourceInvalidError(path, msg="Cannot create directory, there's already a file of that name: %(path)s")
if not allow_recreate:
raise DestinationExistsError(path,msg="Can not create a directory that already exists (try allow_recreate=True): %(path)s")
raise DestinationExistsError(path, msg="Can not create a directory that already exists (try allow_recreate=True): %(path)s")
except ResourceNotFoundError:
raise ParentDirectoryMissingError(path)
......@@ -297,7 +298,7 @@ class OSFS(OSFSXAttrMixin, OSFSWatchMixin, FS):
if recursive:
try:
if dirname(path) not in ('', '/'):
self.removedir(dirname(path),recursive=True)
self.removedir(dirname(path), recursive=True)
except DirectoryNotEmptyError:
pass
......@@ -322,7 +323,7 @@ class OSFS(OSFSXAttrMixin, OSFSWatchMixin, FS):
raise ParentDirectoryMissingError(dst)
raise
def _stat(self,path):
def _stat(self, path):
"""Stat the given path, normalising error codes."""
sys_path = self.getsyspath(path)
try:
......@@ -350,5 +351,3 @@ class OSFS(OSFSXAttrMixin, OSFSWatchMixin, FS):
@convert_os_errors
def getsize(self, path):
return self._stat(path).st_size
from __future__ import unicode_literals
"""
fs.path
=======
......@@ -11,22 +13,23 @@ by forward slashes and with an optional leading slash).
"""
import re
import os
_requires_normalization = re.compile(r'/\.\.|\./|\.|//').search
_requires_normalization = re.compile(r'/\.\.|\./|\.|//|\\').search
def normpath(path):
"""Normalizes a path to be in the format expected by FS objects.
This function remove any leading or trailing slashes, collapses
duplicate slashes, replaces backward with forward slashes, and generally
tries very hard to return a new path string the canonical FS format.
duplicate slashes, and generally tries very hard to return a new path
in the canonical FS format.
If the path is invalid, ValueError will be raised.
:param path: path to normalize
:returns: a valid FS path
>>> normpath(r"foo\\bar\\baz")
'foo/bar/baz'
>>> normpath("/foo//bar/frob/../baz")
'/foo/bar/baz'
......@@ -40,8 +43,6 @@ def normpath(path):
if path in ('', '/'):
return path
path = path.replace('\\', '/')
# An early out if there is no need to normalize this path
if not _requires_normalization(path):
return path.rstrip('/')
......@@ -66,6 +67,21 @@ def normpath(path):
return '/'.join(components)
if os.sep != '/':
def ospath(path):
"""Replace path separators in an OS path if required"""
return path.replace(os.sep, '/')
else:
def ospath(path):
"""Replace path separators in an OS path if required"""
return path
def normospath(path):
"""Normalizes a path with os separators"""
return normpath(ospath)
def iteratepath(path, numsplits=None):
"""Iterate over the individual components of a path.
......@@ -117,6 +133,7 @@ def isabs(path):
"""Return True if path is an absolute path."""
return path.startswith('/')
def abspath(path):
"""Convert the given path to an absolute path.
......@@ -163,7 +180,7 @@ def pathjoin(*paths):
relpaths = []
for p in paths:
if p:
if p[0] in '\\/':
if p[0] == '/':
del relpaths[:]
absolute = True
relpaths.append(p)
......@@ -173,6 +190,7 @@ def pathjoin(*paths):
path = abspath(path)
return path
def pathcombine(path1, path2):
"""Joins two paths together.
......@@ -185,6 +203,7 @@ def pathcombine(path1, path2):
"""
return "%s/%s" % (path1.rstrip('/'), path2.lstrip('/'))
def join(*paths):
"""Joins any number of paths together, returning a new path string.
......
......@@ -14,7 +14,7 @@ class TestPathFunctions(unittest.TestCase):
"""Testcases for FS path functions."""
def test_normpath(self):
tests = [ ("\\a\\b\\c", "/a/b/c"),
tests = [ ("\\a\\b\\c", "\\a\\b\\c"),
(".", ""),
("./", ""),
("", ""),
......@@ -22,7 +22,7 @@ class TestPathFunctions(unittest.TestCase):
("a/b/c", "a/b/c"),
("a/b/../c/", "a/c"),
("/","/"),
(u"a/\N{GREEK SMALL LETTER BETA}\\c",u"a/\N{GREEK SMALL LETTER BETA}/c"),
(u"a/\N{GREEK SMALL LETTER BETA}/c",u"a/\N{GREEK SMALL LETTER BETA}/c"),
]
for path, result in tests:
self.assertEqual(normpath(path), result)
......@@ -38,7 +38,7 @@ class TestPathFunctions(unittest.TestCase):
("a/b/c", "../d", "c", "a/b/d/c"),
("a/b/c", "../d", "/a", "/a"),
("aaa", "bbb/ccc", "aaa/bbb/ccc"),
("aaa", "bbb\ccc", "aaa/bbb/ccc"),
("aaa", "bbb\\ccc", "aaa/bbb\\ccc"),
("aaa", "bbb", "ccc", "/aaa", "eee", "/aaa/eee"),
("a/b", "./d", "e", "a/b/d/e"),
("/", "/", "/"),
......
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