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
838d94c3
Commit
838d94c3
authored
Nov 20, 2010
by
willmcgugan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fixed getmeta/hasmeta, added documentation and tests
parent
e4fe27c2
Show whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
139 additions
and
29 deletions
+139
-29
fs/base.py
+48
-12
fs/contrib/bigfs/__init__.py
+1
-0
fs/contrib/davfs/__init__.py
+1
-0
fs/contrib/tahoefs/__init__.py
+1
-1
fs/ftpfs.py
+5
-2
fs/memoryfs.py
+6
-2
fs/mountfs.py
+1
-1
fs/osfs/__init__.py
+26
-1
fs/rpcfs.py
+11
-4
fs/s3fs.py
+4
-1
fs/sftpfs.py
+4
-1
fs/tempfs.py
+4
-0
fs/tests/__init__.py
+20
-0
fs/wrapfs/__init__.py
+2
-2
fs/wrapfs/readonlyfs.py
+2
-1
fs/zipfs.py
+3
-1
No files found.
fs/base.py
View file @
838d94c3
...
...
@@ -15,7 +15,8 @@ __all__ = ['DummyLock',
'NullFile'
,
'synchronize'
,
'FS'
,
'flags_to_mode'
]
'flags_to_mode'
,
'NoDefaultMeta'
]
import
os
,
os
.
path
import
sys
...
...
@@ -33,9 +34,6 @@ from fs.path import *
from
fs.errors
import
*
from
fs.local_functools
import
wraps
SubFS
=
None
# this is lazily imported from fs.wrapfs.subfs
class
DummyLock
(
object
):
"""A dummy lock object that doesn't do anything.
...
...
@@ -68,6 +66,11 @@ def silence_fserrors(f, *args, **kwargs):
return
None
class
NoDefaultMeta
(
object
):
"""A singleton used to signify that there is no default for getmeta"""
pass
class
NullFile
(
object
):
"""A NullFile is a file object that has no functionality.
...
...
@@ -188,8 +191,33 @@ class FS(object):
else
:
self
.
_lock
=
DummyLock
()
def
getmeta
(
self
,
meta_name
,
default
=
Ellipsis
):
"""Retrieve a meta value associated with the FS object
def
getmeta
(
self
,
meta_name
,
default
=
NoDefaultMeta
):
"""Retrieve a meta value associated with an FS object. Meta values are
a way of an FS implementation to report potentially useful information
associated with the file system.
A meta key is a lower case string with no spaces. Meta keys may also
be grouped in namespaces in a dotted notation, e.g. 'atomic.namespaces'.
FS implementations aren't obliged to return any meta values, but the
following are common:
* *read_only* True if the file system can not be modified
* *network* True if the file system requires network access
* *unicode_paths* True if the file system can use unicode paths
* *case_insensitive_paths* True if the file system ignores the case of paths
* *atomic.makedir* True if making a directory is an atomic operation
* *atomic.rename" True if rename is an atomic operation, (and not implemented as a copy followed by a delete)
* *atomic.setcontents" True if the implementation supports setting the contents of a file as an atomic operation (without opening a file)
The following are less common:
* *free_space* The free space (in bytes) available on the file system
FS implementations may expose non-generic meta data through a self-named namespace. e.g. 'somefs.some_meta'
Since no meta value is guaranteed to exist, it is advisable to always supply a
default value to `getmeta`.
:param meta_name: The name of the meta value to retrieve
:param default: An option default to return, if the meta value isn't present
...
...
@@ -197,7 +225,7 @@ class FS(object):
"""
if
meta_name
not
in
self
.
_meta
:
if
default
is
not
Ellipsis
:
if
default
is
not
NoDefaultMeta
:
return
default
raise
NoMetaError
(
meta_name
=
meta_name
)
return
self
.
_meta
[
meta_name
]
...
...
@@ -210,7 +238,7 @@ class FS(object):
"""
try
:
self
.
getmeta
(
'meta_name'
)
self
.
getmeta
(
meta_name
)
except
NoMetaError
:
return
False
return
True
...
...
@@ -512,7 +540,16 @@ class FS(object):
def
getinfo
(
self
,
path
):
"""Returns information for a path as a dictionary. The exact content of
this dictionary will vary depending on the implementation, but will
likely include a few common values.
likely include a few common values. The following values will be found
in info dictionaries for most implementations:
* "size" - Number of bytes used to store the file or directory
* "created_time" - A datetime object containing the time the resource
was created
* "accessed_time" - A datetime object containing the time the resource
was last accessed
* "modified_time" - A datetime object containing the time the resource
was modified
:param path: a path to retrieve information for
:rtype: dict
...
...
@@ -521,7 +558,7 @@ class FS(object):
def
desc
(
self
,
path
):
"""Returns short descriptive text regarding a path. Intended mainly as
a debugging aid
a debugging aid
.
:param path: A path to describe
:rtype: str
...
...
@@ -583,8 +620,7 @@ class FS(object):
:param path: path to directory to open
:rtype: An FS object
"""
global
SubFS
if
SubFS
is
None
:
from
fs.wrapfs.subfs
import
SubFS
if
not
self
.
exists
(
path
):
raise
ResourceNotFoundError
(
path
)
...
...
fs/contrib/bigfs/__init__.py
View file @
838d94c3
...
...
@@ -161,6 +161,7 @@ class BigFS(FS):
'read_only'
:
True
,
'unicode_paths'
:
True
,
'case_insensitive_paths'
:
False
,
'network'
:
False
,
}
def
__init__
(
self
,
filename
,
mode
=
"r"
,
thread_synchronize
=
True
):
...
...
fs/contrib/davfs/__init__.py
View file @
838d94c3
...
...
@@ -77,6 +77,7 @@ class DAVFS(FS):
'read_only'
:
False
,
'unicode_paths'
:
True
,
'case_insensitive_paths'
:
False
,
'network'
:
True
}
def
__init__
(
self
,
url
,
credentials
=
None
,
get_credentials
=
None
,
thread_synchronize
=
True
,
connection_classes
=
None
,
timeout
=
None
):
...
...
fs/contrib/tahoefs/__init__.py
View file @
838d94c3
...
...
@@ -80,7 +80,7 @@ class TahoeFS(CacheFS):
'read_only'
:
False
,
'unicode_paths'
:
True
,
'case_insensitive_paths'
:
False
,
'
may_bloc
k'
:
True
'
networ
k'
:
True
}
def
__init__
(
self
,
dircap
,
timeout
=
60
,
autorun
=
True
,
largefilesize
=
10
*
1024
*
1024
,
webapi
=
'http://127.0.0.1:3456'
):
...
...
fs/ftpfs.py
View file @
838d94c3
...
...
@@ -737,11 +737,14 @@ class FTPFS(FS):
_locals
=
threading
.
local
()
_meta
=
{
'virtual'
:
False
,
_meta
=
{
'network'
:
True
,
'virtual'
:
False
,
'read_only'
:
False
,
'unicode_paths'
:
True
,
'case_insensitive_paths'
:
False
,
'may_block'
:
True
'atomic.makedir'
:
True
,
'atomic.rename'
:
True
,
'atomic.setcontents'
:
False
,
}
def
__init__
(
self
,
host
=
''
,
user
=
''
,
passwd
=
''
,
acct
=
''
,
timeout
=
_GLOBAL_DEFAULT_TIMEOUT
,
...
...
fs/memoryfs.py
View file @
838d94c3
...
...
@@ -184,10 +184,14 @@ class MemoryFS(FS):
"""
_meta
=
{
'virtual'
:
False
,
_meta
=
{
'network'
:
False
,
'virtual'
:
False
,
'read_only'
:
False
,
'unicode_paths'
:
True
,
'case_insensitive_paths'
:
False
'case_insensitive_paths'
:
False
,
'atomic.makedir'
:
True
,
'atomic.rename'
:
True
,
'atomic.setcontents'
:
False
,
}
def
_make_dir_entry
(
self
,
*
args
,
**
kwargs
):
...
...
fs/mountfs.py
View file @
838d94c3
...
...
@@ -72,7 +72,7 @@ class MountFS(FS):
_meta
=
{
'virtual'
:
True
,
'read_only'
:
False
,
'unicode_paths'
:
True
,
'case_insensitive_paths'
:
False
'case_insensitive_paths'
:
False
,
}
DirMount
=
DirMount
...
...
fs/osfs/__init__.py
View file @
838d94c3
...
...
@@ -18,6 +18,7 @@ import os.path
import
sys
import
errno
import
datetime
import
platform
from
fs.base
import
*
from
fs.errors
import
*
...
...
@@ -73,10 +74,14 @@ class OSFS(OSFSXAttrMixin, OSFSWatchMixin, FS):
methods in the os and os.path modules.
"""
_meta
=
{
'virtual'
:
False
,
_meta
=
{
'network'
:
False
,
'virtual'
:
False
,
'read_only'
:
False
,
'unicode_paths'
:
os
.
path
.
supports_unicode_filenames
,
'case_insensitive_paths'
:
os
.
path
.
normcase
(
'Aa'
)
==
'aa'
,
'atomic.makedir'
:
True
,
'atomic.rename'
:
True
,
'atomic.setcontents'
:
False
,
}
def
__init__
(
self
,
root_path
,
thread_synchronize
=
_thread_synchronize_default
,
encoding
=
None
,
create
=
False
,
dir_mode
=
0700
):
...
...
@@ -155,6 +160,26 @@ class OSFS(OSFSXAttrMixin, OSFSWatchMixin, FS):
raise
ValueError
(
"path not within this FS:
%
s (
%
s)"
%
(
os
.
path
.
normcase
(
path
),
prefix
))
return
path
[
len
(
self
.
root_path
):]
def
getmeta
(
self
,
meta_name
,
default
=
NoDefaultMeta
):
if
meta_name
==
'free_space'
:
if
platform
.
system
()
==
'Windows'
:
try
:
import
ctypes
free_bytes
=
ctypes
.
ulonglong
(
0
)
ctypes
.
windll
.
kernel32
.
GetDiskFreeSpaceExW
(
ctypes
.
c_wchar_p
(
self
.
root_path
),
None
,
None
,
ctypes
.
pointer
(
free_bytes
))
return
free_bytes
.
value
except
ImportError
:
# Fall through to call the base class
pass
else
:
stat
=
os
.
statvfs
(
self
.
root_path
)
return
stat
.
f_bfree
*
stat
.
f_bsize
return
super
(
OSFS
,
self
)
.
getmeta
(
meta_name
,
default
)
@convert_os_errors
def
open
(
self
,
path
,
mode
=
"r"
,
**
kwargs
):
mode
=
filter
(
lambda
c
:
c
in
"rwabt+"
,
mode
)
...
...
fs/rpcfs.py
View file @
838d94c3
...
...
@@ -89,10 +89,7 @@ class RPCFS(FS):
"""
_meta
=
{
'virtual'
:
False
,
'read_only'
:
False
,
'unicode_paths'
:
True
,
'case_insensitive_paths'
:
False
,
'may_block'
:
True
,
'network'
:
True
,
}
def
__init__
(
self
,
uri
,
transport
=
None
):
...
...
@@ -147,6 +144,16 @@ class RPCFS(FS):
"""Decode paths arriving over the wire."""
return
path
.
decode
(
"base64"
)
.
decode
(
"utf8"
)
def
getmeta
(
self
,
meta_name
,
default
=
NoDefaultMeta
):
if
meta_name
in
self
.
_meta
:
return
self
.
_meta
[
meta_name
]
return
self
.
proxy
.
getmeta
(
meta_name
,
default
)
def
hasmeta
(
self
,
meta_name
):
if
meta_name
in
self
.
_meta
:
return
True
return
self
.
proxy
.
hasmeta
(
meta_name
)
def
open
(
self
,
path
,
mode
=
"r"
):
# TODO: chunked transport of large files
path
=
self
.
encode_path
(
path
)
...
...
fs/s3fs.py
View file @
838d94c3
...
...
@@ -60,7 +60,10 @@ class S3FS(FS):
'read_only'
:
False
,
'unicode_paths'
:
True
,
'case_insensitive_paths'
:
False
,
'may_block'
:
True
,
'network'
:
True
,
'atomic.makedir'
:
True
,
'atomic.rename'
:
False
,
'atomic.setconetns'
:
True
}
class
meta
:
...
...
fs/sftpfs.py
View file @
838d94c3
...
...
@@ -50,7 +50,10 @@ class SFTPFS(FS):
'read_only'
:
False
,
'unicode_paths'
:
True
,
'case_insensitive_paths'
:
False
,
'may_block'
:
True
,
'network'
:
True
,
'atomic.makedir'
:
True
,
'atomic.rename'
:
True
,
'atomic.setcontents'
:
False
}
def
__init__
(
self
,
connection
,
root_path
=
"/"
,
encoding
=
None
,
**
credentials
):
...
...
fs/tempfs.py
View file @
838d94c3
...
...
@@ -24,6 +24,10 @@ class TempFS(OSFS):
'read_only'
:
False
,
'unicode_paths'
:
os
.
path
.
supports_unicode_filenames
,
'case_insensitive_paths'
:
os
.
path
.
normcase
(
'Aa'
)
==
'aa'
,
'network'
:
False
,
'atomic.makedir'
:
True
,
'atomic.rename'
:
True
,
'atomic.setcontents'
:
False
}
def
__init__
(
self
,
identifier
=
None
,
temp_dir
=
None
,
dir_mode
=
0700
,
thread_synchronize
=
_thread_synchronize_default
):
...
...
fs/tests/__init__.py
View file @
838d94c3
...
...
@@ -48,6 +48,26 @@ class FSTestCases(object):
"""Check that a file exists within self.fs"""
return
self
.
fs
.
exists
(
p
)
def
test_meta
(
self
):
"""Checks getmeta / hasmeta are functioning"""
# getmeta / hasmeta are hard to test, since there is no way to validate
# the implementations response
meta_names
=
[
"read_only"
,
"network"
,
"unicode_paths"
]
stupid_meta
=
'thismetashouldnotexist!"r$$
%
^&&*()_+'
self
.
assertRaises
(
NoMetaError
,
self
.
fs
.
getmeta
,
stupid_meta
)
self
.
assertFalse
(
self
.
fs
.
hasmeta
(
stupid_meta
))
self
.
assertEquals
(
None
,
self
.
fs
.
getmeta
(
stupid_meta
,
None
))
self
.
assertEquals
(
3.14
,
self
.
fs
.
getmeta
(
stupid_meta
,
3.14
))
for
meta_name
in
meta_names
:
try
:
meta
=
self
.
fs
.
getmeta
(
meta_name
)
self
.
assertTrue
(
self
.
fs
.
hasmeta
(
meta_name
))
except
NoMetaError
:
self
.
assertFalse
(
self
.
fs
.
hasmeta
(
meta_name
))
def
test_root_dir
(
self
):
self
.
assertTrue
(
self
.
fs
.
isdir
(
""
))
self
.
assertTrue
(
self
.
fs
.
isdir
(
"/"
))
...
...
fs/wrapfs/__init__.py
View file @
838d94c3
...
...
@@ -19,7 +19,7 @@ import re
import
sys
import
fnmatch
from
fs.base
import
FS
,
threading
,
synchronize
from
fs.base
import
FS
,
threading
,
synchronize
,
NoDefaultMeta
from
fs.errors
import
*
from
fs.path
import
*
from
fs.local_functools
import
wraps
...
...
@@ -118,7 +118,7 @@ class WrapFS(FS):
return
(
mode
,
mode
)
@rewrite_errors
def
getmeta
(
self
,
meta_name
,
default
=
Ellipsis
):
def
getmeta
(
self
,
meta_name
,
default
=
NoDefaultMeta
):
return
self
.
wrapped_fs
.
getmeta
(
meta_name
,
default
)
@rewrite_errors
...
...
fs/wrapfs/readonlyfs.py
View file @
838d94c3
...
...
@@ -6,6 +6,7 @@ An FS wrapper class for blocking operations that would modify the FS.
"""
from
fs.base
import
NoDefaultMeta
from
fs.wrapfs
import
WrapFS
from
fs.errors
import
UnsupportedError
,
NoSysPathError
...
...
@@ -20,7 +21,7 @@ class ReadOnlyFS(WrapFS):
"""
def
getmeta
(
self
,
meta_name
,
default
=
Ellipsis
):
def
getmeta
(
self
,
meta_name
,
default
=
NoDefaultMeta
):
if
meta_name
==
'read_only'
:
return
True
return
self
.
wrapped_fs
(
meta_name
,
default
)
...
...
fs/zipfs.py
View file @
838d94c3
...
...
@@ -76,6 +76,8 @@ class ZipFS(FS):
'read_only'
:
False
,
'unicode_paths'
:
True
,
'case_insensitive_paths'
:
False
,
'network'
:
False
,
'atomic.setcontents'
:
False
}
def
__init__
(
self
,
zip_file
,
mode
=
"r"
,
compression
=
"deflated"
,
allow_zip_64
=
False
,
encoding
=
"CP437"
,
thread_synchronize
=
True
):
...
...
@@ -149,7 +151,7 @@ class ZipFS(FS):
f
=
self
.
_path_fs
.
open
(
path
,
'w'
)
f
.
close
()
def
getmeta
(
self
,
meta_name
,
default
=
Ellipsis
):
def
getmeta
(
self
,
meta_name
,
default
=
NoDefaultMeta
):
if
meta_name
==
'read_only'
:
return
self
.
read_only
return
super
(
ZipFS
,
self
)
.
getmeta
(
meta_name
,
default
)
...
...
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