Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
P
pyfs
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
OpenEdx
pyfs
Commits
bc30657b
Commit
bc30657b
authored
Nov 24, 2012
by
willmcgugan@gmail.com
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fixes for backslashes on Linux issue, see Issue #139
parent
2fbb136c
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
50 additions
and
27 deletions
+50
-27
fs/commands/runner.py
+0
-0
fs/errors.py
+5
-0
fs/osfs/__init__.py
+14
-15
fs/path.py
+28
-9
fs/tests/test_path.py
+3
-3
No files found.
fs/commands/runner.py
View file @
bc30657b
fs/errors.py
View file @
bc30657b
...
@@ -42,6 +42,10 @@ from fs.path import *
...
@@ -42,6 +42,10 @@ from fs.path import *
from
fs.local_functools
import
wraps
from
fs.local_functools
import
wraps
class
InvalidPathError
(
Exception
):
pass
class
FSError
(
Exception
):
class
FSError
(
Exception
):
"""Base exception class for the FS module."""
"""Base exception class for the FS module."""
default_message
=
"Unspecified error"
default_message
=
"Unspecified error"
...
@@ -184,6 +188,7 @@ class ResourceLockedError(ResourceError):
...
@@ -184,6 +188,7 @@ class ResourceLockedError(ResourceError):
"""Exception raised when a resource can't be used because it is locked."""
"""Exception raised when a resource can't be used because it is locked."""
default_message
=
"Resource is locked:
%(path)
s"
default_message
=
"Resource is locked:
%(path)
s"
class
NoMMapError
(
ResourceError
):
class
NoMMapError
(
ResourceError
):
"""Exception raise when getmmap fails to create a mmap"""
"""Exception raise when getmmap fails to create a mmap"""
default_message
=
"Can't get mmap for
%(path)
s"
default_message
=
"Can't get mmap for
%(path)
s"
...
...
fs/osfs/__init__.py
View file @
bc30657b
...
@@ -35,10 +35,12 @@ def _os_stat(path):
...
@@ -35,10 +35,12 @@ def _os_stat(path):
"""Replacement for os.stat that raises FSError subclasses."""
"""Replacement for os.stat that raises FSError subclasses."""
return
os
.
stat
(
path
)
return
os
.
stat
(
path
)
@convert_os_errors
@convert_os_errors
def
_os_mkdir
(
name
,
mode
=
0777
):
def
_os_mkdir
(
name
,
mode
=
0777
):
"""Replacement for os.mkdir that raises FSError subclasses."""
"""Replacement for os.mkdir that raises FSError subclasses."""
return
os
.
mkdir
(
name
,
mode
)
return
os
.
mkdir
(
name
,
mode
)
@convert_os_errors
@convert_os_errors
def
_os_makedirs
(
name
,
mode
=
0777
):
def
_os_makedirs
(
name
,
mode
=
0777
):
...
@@ -66,7 +68,6 @@ def _os_makedirs(name, mode=0777):
...
@@ -66,7 +68,6 @@ def _os_makedirs(name, mode=0777):
os
.
mkdir
(
name
,
mode
)
os
.
mkdir
(
name
,
mode
)
class
OSFS
(
OSFSXAttrMixin
,
OSFSWatchMixin
,
FS
):
class
OSFS
(
OSFSXAttrMixin
,
OSFSWatchMixin
,
FS
):
"""Expose the underlying operating-system filesystem as an FS object.
"""Expose the underlying operating-system filesystem as an FS object.
...
@@ -126,9 +127,9 @@ class OSFS(OSFSXAttrMixin, OSFSWatchMixin, FS):
...
@@ -126,9 +127,9 @@ class OSFS(OSFSXAttrMixin, OSFSWatchMixin, FS):
pass
pass
if
not
os
.
path
.
exists
(
root_path
):
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
):
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
.
root_path
=
root_path
self
.
dir_mode
=
dir_mode
self
.
dir_mode
=
dir_mode
...
@@ -147,10 +148,10 @@ class OSFS(OSFSXAttrMixin, OSFSWatchMixin, FS):
...
@@ -147,10 +148,10 @@ class OSFS(OSFSXAttrMixin, OSFSWatchMixin, FS):
return
p
.
decode
(
self
.
encoding
,
'replace'
)
return
p
.
decode
(
self
.
encoding
,
'replace'
)
def
getsyspath
(
self
,
path
,
allow_none
=
False
):
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
)
path
=
os
.
path
.
join
(
self
.
root_path
,
path
)
if
not
path
.
startswith
(
self
.
root_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
)
path
=
self
.
_decode_path
(
path
)
return
path
return
path
...
@@ -173,7 +174,7 @@ class OSFS(OSFSXAttrMixin, OSFSWatchMixin, FS):
...
@@ -173,7 +174,7 @@ class OSFS(OSFSXAttrMixin, OSFSWatchMixin, FS):
if
not
prefix
.
endswith
(
os
.
path
.
sep
):
if
not
prefix
.
endswith
(
os
.
path
.
sep
):
prefix
+=
os
.
path
.
sep
prefix
+=
os
.
path
.
sep
if
not
os
.
path
.
normcase
(
path
)
.
startswith
(
prefix
):
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
):])
return
normpath
(
path
[
len
(
self
.
root_path
):])
def
getmeta
(
self
,
meta_name
,
default
=
NoDefaultMeta
):
def
getmeta
(
self
,
meta_name
,
default
=
NoDefaultMeta
):
...
@@ -221,8 +222,8 @@ class OSFS(OSFSXAttrMixin, OSFSWatchMixin, FS):
...
@@ -221,8 +222,8 @@ class OSFS(OSFSXAttrMixin, OSFSWatchMixin, FS):
raise
raise
@convert_os_errors
@convert_os_errors
def
setcontents
(
self
,
path
,
contents
,
chunk_size
=
64
*
1024
):
def
setcontents
(
self
,
path
,
contents
,
chunk_size
=
64
*
1024
):
return
super
(
OSFS
,
self
)
.
setcontents
(
path
,
contents
,
chunk_size
)
return
super
(
OSFS
,
self
)
.
setcontents
(
path
,
contents
,
chunk_size
)
@convert_os_errors
@convert_os_errors
def
exists
(
self
,
path
):
def
exists
(
self
,
path
):
...
@@ -252,9 +253,9 @@ class OSFS(OSFSXAttrMixin, OSFSWatchMixin, FS):
...
@@ -252,9 +253,9 @@ class OSFS(OSFSXAttrMixin, OSFSWatchMixin, FS):
_os_mkdir
(
sys_path
,
self
.
dir_mode
)
_os_mkdir
(
sys_path
,
self
.
dir_mode
)
except
DestinationExistsError
:
except
DestinationExistsError
:
if
self
.
isfile
(
path
):
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
:
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
:
except
ResourceNotFoundError
:
raise
ParentDirectoryMissingError
(
path
)
raise
ParentDirectoryMissingError
(
path
)
...
@@ -297,7 +298,7 @@ class OSFS(OSFSXAttrMixin, OSFSWatchMixin, FS):
...
@@ -297,7 +298,7 @@ class OSFS(OSFSXAttrMixin, OSFSWatchMixin, FS):
if
recursive
:
if
recursive
:
try
:
try
:
if
dirname
(
path
)
not
in
(
''
,
'/'
):
if
dirname
(
path
)
not
in
(
''
,
'/'
):
self
.
removedir
(
dirname
(
path
),
recursive
=
True
)
self
.
removedir
(
dirname
(
path
),
recursive
=
True
)
except
DirectoryNotEmptyError
:
except
DirectoryNotEmptyError
:
pass
pass
...
@@ -322,7 +323,7 @@ class OSFS(OSFSXAttrMixin, OSFSWatchMixin, FS):
...
@@ -322,7 +323,7 @@ class OSFS(OSFSXAttrMixin, OSFSWatchMixin, FS):
raise
ParentDirectoryMissingError
(
dst
)
raise
ParentDirectoryMissingError
(
dst
)
raise
raise
def
_stat
(
self
,
path
):
def
_stat
(
self
,
path
):
"""Stat the given path, normalising error codes."""
"""Stat the given path, normalising error codes."""
sys_path
=
self
.
getsyspath
(
path
)
sys_path
=
self
.
getsyspath
(
path
)
try
:
try
:
...
@@ -350,5 +351,3 @@ class OSFS(OSFSXAttrMixin, OSFSWatchMixin, FS):
...
@@ -350,5 +351,3 @@ class OSFS(OSFSXAttrMixin, OSFSWatchMixin, FS):
@convert_os_errors
@convert_os_errors
def
getsize
(
self
,
path
):
def
getsize
(
self
,
path
):
return
self
.
_stat
(
path
)
.
st_size
return
self
.
_stat
(
path
)
.
st_size
fs/path.py
View file @
bc30657b
from
__future__
import
unicode_literals
"""
"""
fs.path
fs.path
=======
=======
...
@@ -11,22 +13,23 @@ by forward slashes and with an optional leading slash).
...
@@ -11,22 +13,23 @@ by forward slashes and with an optional leading slash).
"""
"""
import
re
import
re
import
os
_requires_normalization
=
re
.
compile
(
r'/\.\.|\./|\.|//'
)
.
search
_requires_normalization
=
re
.
compile
(
r'/\.\.|\./|\.|//|\\'
)
.
search
def
normpath
(
path
):
def
normpath
(
path
):
"""Normalizes a path to be in the format expected by FS objects.
"""Normalizes a path to be in the format expected by FS objects.
This function remove any leading or trailing slashes, collapses
This function remove any leading or trailing slashes, collapses
duplicate slashes,
replaces backward with forward slashes, and generally
duplicate slashes,
and generally tries very hard to return a new path
tries very hard to return a new path string
the canonical FS format.
in
the canonical FS format.
If the path is invalid, ValueError will be raised.
If the path is invalid, ValueError will be raised.
:param path: path to normalize
:param path: path to normalize
:returns: a valid FS path
:returns: a valid FS path
>>> normpath(r"foo
\\
bar
\\
baz")
'foo/bar/baz'
>>> normpath("/foo//bar/frob/../baz")
>>> normpath("/foo//bar/frob/../baz")
'/foo/bar/baz'
'/foo/bar/baz'
...
@@ -40,8 +43,6 @@ def normpath(path):
...
@@ -40,8 +43,6 @@ def normpath(path):
if
path
in
(
''
,
'/'
):
if
path
in
(
''
,
'/'
):
return
path
return
path
path
=
path
.
replace
(
'
\\
'
,
'/'
)
# An early out if there is no need to normalize this path
# An early out if there is no need to normalize this path
if
not
_requires_normalization
(
path
):
if
not
_requires_normalization
(
path
):
return
path
.
rstrip
(
'/'
)
return
path
.
rstrip
(
'/'
)
...
@@ -66,6 +67,21 @@ def normpath(path):
...
@@ -66,6 +67,21 @@ def normpath(path):
return
'/'
.
join
(
components
)
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
):
def
iteratepath
(
path
,
numsplits
=
None
):
"""Iterate over the individual components of a path.
"""Iterate over the individual components of a path.
...
@@ -117,6 +133,7 @@ def isabs(path):
...
@@ -117,6 +133,7 @@ def isabs(path):
"""Return True if path is an absolute path."""
"""Return True if path is an absolute path."""
return
path
.
startswith
(
'/'
)
return
path
.
startswith
(
'/'
)
def
abspath
(
path
):
def
abspath
(
path
):
"""Convert the given path to an absolute path.
"""Convert the given path to an absolute path.
...
@@ -163,7 +180,7 @@ def pathjoin(*paths):
...
@@ -163,7 +180,7 @@ def pathjoin(*paths):
relpaths
=
[]
relpaths
=
[]
for
p
in
paths
:
for
p
in
paths
:
if
p
:
if
p
:
if
p
[
0
]
in
'
\\
/'
:
if
p
[
0
]
==
'
/'
:
del
relpaths
[:]
del
relpaths
[:]
absolute
=
True
absolute
=
True
relpaths
.
append
(
p
)
relpaths
.
append
(
p
)
...
@@ -173,6 +190,7 @@ def pathjoin(*paths):
...
@@ -173,6 +190,7 @@ def pathjoin(*paths):
path
=
abspath
(
path
)
path
=
abspath
(
path
)
return
path
return
path
def
pathcombine
(
path1
,
path2
):
def
pathcombine
(
path1
,
path2
):
"""Joins two paths together.
"""Joins two paths together.
...
@@ -185,6 +203,7 @@ def pathcombine(path1, path2):
...
@@ -185,6 +203,7 @@ def pathcombine(path1, path2):
"""
"""
return
"
%
s/
%
s"
%
(
path1
.
rstrip
(
'/'
),
path2
.
lstrip
(
'/'
))
return
"
%
s/
%
s"
%
(
path1
.
rstrip
(
'/'
),
path2
.
lstrip
(
'/'
))
def
join
(
*
paths
):
def
join
(
*
paths
):
"""Joins any number of paths together, returning a new path string.
"""Joins any number of paths together, returning a new path string.
...
...
fs/tests/test_path.py
View file @
bc30657b
...
@@ -14,7 +14,7 @@ class TestPathFunctions(unittest.TestCase):
...
@@ -14,7 +14,7 @@ class TestPathFunctions(unittest.TestCase):
"""Testcases for FS path functions."""
"""Testcases for FS path functions."""
def
test_normpath
(
self
):
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):
...
@@ -22,7 +22,7 @@ class TestPathFunctions(unittest.TestCase):
(
"a/b/c"
,
"a/b/c"
),
(
"a/b/c"
,
"a/b/c"
),
(
"a/b/../c/"
,
"a/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
:
for
path
,
result
in
tests
:
self
.
assertEqual
(
normpath
(
path
),
result
)
self
.
assertEqual
(
normpath
(
path
),
result
)
...
@@ -38,7 +38,7 @@ class TestPathFunctions(unittest.TestCase):
...
@@ -38,7 +38,7 @@ class TestPathFunctions(unittest.TestCase):
(
"a/b/c"
,
"../d"
,
"c"
,
"a/b/d/c"
),
(
"a/b/c"
,
"../d"
,
"c"
,
"a/b/d/c"
),
(
"a/b/c"
,
"../d"
,
"/a"
,
"/a"
),
(
"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/bbb
\\
ccc"
),
(
"aaa"
,
"bbb"
,
"ccc"
,
"/aaa"
,
"eee"
,
"/aaa/eee"
),
(
"aaa"
,
"bbb"
,
"ccc"
,
"/aaa"
,
"eee"
,
"/aaa/eee"
),
(
"a/b"
,
"./d"
,
"e"
,
"a/b/d/e"
),
(
"a/b"
,
"./d"
,
"e"
,
"a/b/d/e"
),
(
"/"
,
"/"
,
"/"
),
(
"/"
,
"/"
,
"/"
),
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment