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
53e8bd02
Commit
53e8bd02
authored
Dec 20, 2010
by
willmcgugan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
test fixes for windoze
parent
e24d7a4a
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
107 additions
and
88 deletions
+107
-88
fs/commands/fsserve.py
+1
-1
fs/commands/fstree.py
+1
-1
fs/commands/runner.py
+3
-0
fs/ftpfs.py
+34
-35
fs/opener.py
+13
-11
fs/osfs/__init__.py
+18
-18
fs/path.py
+4
-6
fs/sftpfs.py
+3
-0
fs/tests/__init__.py
+2
-1
fs/tests/test_expose.py
+16
-7
fs/tests/test_ftpfs.py
+10
-6
fs/utils.py
+1
-1
setup.py
+1
-1
No files found.
fs/commands/fsserve.py
View file @
53e8bd02
...
...
@@ -15,7 +15,7 @@ Serves the contents of PATH with one of a number of methods"""
optparse
=
super
(
FSServe
,
self
)
.
get_optparse
()
optparse
.
add_option
(
'-t'
,
'--type'
,
dest
=
'type'
,
type
=
"string"
,
default
=
"http"
,
help
=
"Server type to create (http, rpc, sftp)"
,
metavar
=
"TYPE"
)
optparse
.
add_option
(
'-a'
,
'--addr'
,
dest
=
'addr'
,
type
=
"string"
,
default
=
"
localhost
"
,
optparse
.
add_option
(
'-a'
,
'--addr'
,
dest
=
'addr'
,
type
=
"string"
,
default
=
"
127.0.0.1
"
,
help
=
"Server address"
,
metavar
=
"ADDR"
)
optparse
.
add_option
(
'-p'
,
'--port'
,
dest
=
'port'
,
type
=
"int"
,
help
=
"Port number"
,
metavar
=
""
)
...
...
fs/commands/fstree.py
View file @
53e8bd02
...
...
@@ -42,7 +42,7 @@ Recursively display the contents of PATH in an ascii tree"""
print_fs
(
fs
,
path
or
''
,
file_out
=
self
.
output_file
,
max_levels
=
options
.
depth
,
terminal_colors
=
self
.
is_terminal
()
,
terminal_colors
=
self
.
terminal_colors
,
hide_dotfiles
=
not
options
.
all
,
dirs_first
=
options
.
dirsfirst
)
...
...
fs/commands/runner.py
View file @
53e8bd02
import
warnings
warnings
.
filterwarnings
(
"ignore"
)
import
sys
from
optparse
import
OptionParser
from
fs.opener
import
opener
,
OpenerError
,
Opener
...
...
fs/ftpfs.py
View file @
53e8bd02
...
...
@@ -549,7 +549,7 @@ class _FTPFile(object):
self
.
_lock
=
threading
.
RLock
()
self
.
ftpfs
=
ftpfs
self
.
ftp
=
ftp
self
.
path
=
path
self
.
path
=
normpath
(
path
)
self
.
mode
=
mode
self
.
read_pos
=
0
self
.
write_pos
=
0
...
...
@@ -559,9 +559,7 @@ class _FTPFile(object):
self
.
file_size
=
ftpfs
.
getsize
(
path
)
self
.
conn
=
None
path
=
_encode
(
abspath
(
path
))
#self._lock = ftpfs._lock
self
.
_start_file
(
mode
,
path
)
self
.
_start_file
(
mode
,
_encode
(
self
.
path
))
@fileftperrors
def
_start_file
(
self
,
mode
,
path
):
...
...
@@ -569,15 +567,15 @@ class _FTPFile(object):
self
.
write_pos
=
0
if
'r'
in
mode
:
self
.
ftp
.
voidcmd
(
'TYPE I'
)
self
.
conn
=
self
.
ftp
.
transfercmd
(
'RETR '
+
path
,
None
)
self
.
conn
=
self
.
ftp
.
transfercmd
(
'RETR '
+
path
,
None
)
else
:
#if 'w' in mode or 'a' in mode:
self
.
ftp
.
voidcmd
(
'TYPE I'
)
if
'a'
in
mode
:
self
.
write_pos
=
self
.
file_size
self
.
conn
=
self
.
ftp
.
transfercmd
(
'APPE '
+
path
)
self
.
conn
=
self
.
ftp
.
transfercmd
(
'APPE '
+
path
)
else
:
self
.
conn
=
self
.
ftp
.
transfercmd
(
'STOR '
+
path
)
self
.
conn
=
self
.
ftp
.
transfercmd
(
'STOR '
+
path
)
@fileftperrors
def
read
(
self
,
size
=
None
):
...
...
@@ -765,8 +763,6 @@ class _FTPFile(object):
yield
line
append
(
c
)
def
ftperrors
(
f
):
@wraps
(
f
)
def
deco
(
self
,
*
args
,
**
kwargs
):
...
...
@@ -786,7 +782,7 @@ def ftperrors(f):
return
deco
def
_encode
(
s
):
def
_encode
(
s
):
if
isinstance
(
s
,
unicode
):
return
s
.
encode
(
'utf-8'
)
return
s
...
...
@@ -883,8 +879,8 @@ class FTPFS(FS):
self
.
refresh_dircache
(
dirname
(
path
))
@synchronize
def
_readdir
(
self
,
path
):
path
=
normpath
(
path
)
def
_readdir
(
self
,
path
):
path
=
abspath
(
normpath
(
path
)
)
if
self
.
dircache
.
count
:
cached_dirlist
=
self
.
dircache
.
get
(
path
)
if
cached_dirlist
is
not
None
:
...
...
@@ -893,15 +889,14 @@ class FTPFS(FS):
parser
=
FTPListDataParser
()
def
on_line
(
line
):
def
on_line
(
line
):
if
not
isinstance
(
line
,
unicode
):
line
=
line
.
decode
(
'utf-8'
)
line
=
line
.
decode
(
'utf-8'
)
info
=
parser
.
parse_line
(
line
)
if
info
:
info
=
info
.
__dict__
if
info
[
'name'
]
not
in
(
'.'
,
'..'
):
dirlist
[
info
[
'name'
]]
=
info
try
:
self
.
ftp
.
dir
(
_encode
(
path
),
on_line
)
except
error_reply
:
...
...
@@ -925,7 +920,7 @@ class FTPFS(FS):
else
:
remove_paths
=
[]
dircache
=
self
.
dircache
paths
=
[
normpath
(
path
)
for
path
in
paths
]
paths
=
[
normpath
(
abspath
(
path
)
)
for
path
in
paths
]
for
cached_path
in
dircache
.
keys
():
for
path
in
paths
:
if
isbase
(
cached_path
,
path
):
...
...
@@ -1046,6 +1041,7 @@ class FTPFS(FS):
@ftperrors
def
open
(
self
,
path
,
mode
=
'r'
):
path
=
normpath
(
path
)
mode
=
mode
.
lower
()
if
self
.
isdir
(
path
):
raise
ResourceInvalidError
(
path
)
...
...
@@ -1067,16 +1063,17 @@ class FTPFS(FS):
self
.
ftp
.
storbinary
(
'STOR
%
s'
%
_encode
(
path
),
data
,
blocksize
=
chunk_size
)
@ftperrors
def
getcontents
(
self
,
path
):
def
getcontents
(
self
,
path
):
path
=
normpath
(
path
)
contents
=
StringIO
()
self
.
ftp
.
retrbinary
(
'RETR
%
s'
%
_encode
(
normpath
(
path
)
),
contents
.
write
,
blocksize
=
1024
*
64
)
self
.
ftp
.
retrbinary
(
'RETR
%
s'
%
_encode
(
path
),
contents
.
write
,
blocksize
=
1024
*
64
)
return
contents
.
getvalue
()
@ftperrors
def
exists
(
self
,
path
):
path
=
normpath
(
path
)
if
path
in
(
''
,
'/'
):
return
True
return
True
dirlist
,
fname
=
self
.
_get_dirlist
(
path
)
return
fname
in
dirlist
...
...
@@ -1141,21 +1138,23 @@ class FTPFS(FS):
@ftperrors
def
makedir
(
self
,
path
,
recursive
=
False
,
allow_recreate
=
False
):
path
=
normpath
(
path
)
if
path
in
(
''
,
'/'
):
return
def
checkdir
(
path
):
self
.
clear_dircache
(
dirname
(
path
))
try
:
self
.
ftp
.
mkd
(
_encode
(
path
))
except
error_reply
:
return
except
error_perm
,
e
:
if
recursive
or
allow_recreate
:
if
not
self
.
isdir
(
path
):
self
.
clear_dircache
(
dirname
(
path
))
try
:
self
.
ftp
.
mkd
(
_encode
(
path
))
except
error_reply
:
return
if
str
(
e
)
.
split
(
' '
,
1
)[
0
]
==
'550'
:
raise
DestinationExistsError
(
path
)
else
:
raise
except
error_perm
,
e
:
if
recursive
or
allow_recreate
:
return
if
str
(
e
)
.
split
(
' '
,
1
)[
0
]
==
'550'
:
raise
DestinationExistsError
(
path
)
else
:
raise
if
recursive
:
for
p
in
recursepath
(
path
):
checkdir
(
p
)
...
...
@@ -1182,7 +1181,7 @@ class FTPFS(FS):
@ftperrors
def
removedir
(
self
,
path
,
recursive
=
False
,
force
=
False
):
path
=
abspath
(
normpath
(
path
))
if
not
self
.
exists
(
path
):
raise
ResourceNotFoundError
(
path
)
if
self
.
isfile
(
path
):
...
...
@@ -1258,6 +1257,7 @@ class FTPFS(FS):
@ftperrors
def
desc
(
self
,
path
):
path
=
normpath
(
path
)
url
=
self
.
getpathurl
(
path
,
allow_none
=
True
)
if
url
:
return
url
...
...
@@ -1268,7 +1268,6 @@ class FTPFS(FS):
@ftperrors
def
move
(
self
,
src
,
dst
,
overwrite
=
False
,
chunk_size
=
16384
):
if
not
overwrite
and
self
.
exists
(
dst
):
raise
DestinationExistsError
(
dst
)
#self.refresh_dircache(dirname(src), dirname(dst))
...
...
@@ -1281,8 +1280,7 @@ class FTPFS(FS):
self
.
refresh_dircache
(
src
,
dirname
(
src
),
dst
,
dirname
(
dst
))
@ftperrors
def
copy
(
self
,
src
,
dst
,
overwrite
=
False
,
chunk_size
=
1024
*
64
):
def
copy
(
self
,
src
,
dst
,
overwrite
=
False
,
chunk_size
=
1024
*
64
):
if
not
self
.
isfile
(
src
):
if
self
.
isdir
(
src
):
raise
ResourceInvalidError
(
src
,
msg
=
"Source is not a file:
%(path)
s"
)
...
...
@@ -1290,12 +1288,13 @@ class FTPFS(FS):
if
not
overwrite
and
self
.
exists
(
dst
):
raise
DestinationExistsError
(
dst
)
dst
=
normpath
(
dst
)
src_file
=
None
try
:
src_file
=
self
.
open
(
src
,
"rb"
)
ftp
=
self
.
_open_ftp
()
ftp
.
voidcmd
(
'TYPE I'
)
ftp
.
storbinary
(
'STOR
%
s'
%
_encode
(
(
normpath
(
dst
)
)),
src_file
,
blocksize
=
chunk_size
)
ftp
.
storbinary
(
'STOR
%
s'
%
_encode
(
normpath
(
dst
)),
src_file
,
blocksize
=
chunk_size
)
finally
:
self
.
refresh_dircache
(
dirname
(
dst
))
if
src_file
is
not
None
:
...
...
fs/opener.py
View file @
53e8bd02
...
...
@@ -10,7 +10,7 @@ __all__ = ['OpenerError',
import
sys
from
fs.osfs
import
OSFS
from
fs.path
import
pathsplit
,
basename
,
join
,
iswildcard
from
fs.path
import
pathsplit
,
basename
,
join
,
iswildcard
,
normpath
import
os
import
os.path
import
re
...
...
@@ -24,15 +24,17 @@ class NoOpenerError(OpenerError):
def
_expand_syspath
(
path
):
if
path
is
None
:
return
path
path
=
os
.
path
.
expanduser
(
os
.
path
.
expandvars
(
path
))
path
=
os
.
path
.
normpath
(
os
.
path
.
abspath
(
path
))
if
sys
.
platform
==
"win32"
:
if
not
path
.
startswith
(
"
\\\\
?
\\
"
):
path
=
u"
\\\\
?
\\
"
+
root_path
# If it points at the root of a drive, it needs a trailing slash.
if
len
(
path
)
==
6
:
path
=
path
+
"
\\
"
return
path
path
=
os
.
path
.
expanduser
(
os
.
path
.
expandvars
(
path
))
path
=
normpath
(
path
)
#path = os.path.normpath(os.path.abspath(path))
#if sys.platform == "win32":
# if not path.startswith("\\\\?\\"):
# path = u"\\\\?\\" + path
# # If it points at the root of a drive, it needs a trailing slash.
# if len(path) == 6:
# path = path + "\\"
return
path
...
...
@@ -279,7 +281,7 @@ class OSFSOpener(Opener):
def
get_fs
(
cls
,
registry
,
fs_name
,
fs_name_params
,
fs_path
,
writeable
,
create_dir
):
from
fs.osfs
import
OSFS
path
=
_expand_sys
path
(
fs_path
)
path
=
norm
path
(
fs_path
)
if
create_dir
and
not
os
.
path
.
exists
(
path
):
from
fs.osfs
import
_os_makedirs
_os_makedirs
(
path
)
...
...
fs/osfs/__init__.py
View file @
53e8bd02
...
...
@@ -320,23 +320,23 @@ class OSFS(OSFSXAttrMixin, OSFSWatchMixin, FS):
def
getsize
(
self
,
path
):
return
self
.
_stat
(
path
)
.
st_size
@convert_os_errors
def
opendir
(
self
,
path
):
"""A specialised opendir that returns another OSFS rather than a SubDir
This is more optimal than a SubDir because no path delegation is required.
"""
if
path
in
(
''
,
'/'
):
return
self
path
=
normpath
(
path
)
if
not
self
.
exists
(
path
):
raise
ResourceNotFoundError
(
path
)
sub_path
=
pathjoin
(
self
.
root_path
,
path
)
return
OSFS
(
sub_path
,
thread_synchronize
=
self
.
thread_synchronize
,
encoding
=
self
.
encoding
,
create
=
False
,
dir_mode
=
self
.
dir_mode
)
#
@convert_os_errors
#
def opendir(self, path):
#
"""A specialised opendir that returns another OSFS rather than a SubDir
#
#
This is more optimal than a SubDir because no path delegation is required.
#
#
"""
#
if path in ('', '/'):
#
return self
#
path = normpath(path)
#
if not self.exists(path):
#
raise ResourceNotFoundError(path)
#
sub_path = pathjoin(self.root_path, path)
#
return OSFS(sub_path,
#
thread_synchronize=self.thread_synchronize,
#
encoding=self.encoding,
#
create=False,
#
dir_mode=self.dir_mode)
fs/path.py
View file @
53e8bd02
...
...
@@ -119,9 +119,7 @@ def relpath(path):
'a/b'
"""
while
path
and
path
[
0
]
==
"/"
:
path
=
path
[
1
:]
return
path
return
path
.
lstrip
(
'/'
)
def
pathjoin
(
*
paths
):
...
...
@@ -148,9 +146,9 @@ def pathjoin(*paths):
absolute
=
True
relpaths
.
append
(
p
)
path
=
normpath
(
"/"
.
join
(
relpaths
))
if
absolute
and
not
path
.
startswith
(
"/"
)
:
path
=
u"/"
+
path
path
=
normpath
(
u
"/"
.
join
(
relpaths
))
if
absolute
:
path
=
abspath
(
path
)
return
path
# Allow pathjoin() to be used as fs.path.join()
...
...
fs/sftpfs.py
View file @
53e8bd02
...
...
@@ -453,6 +453,8 @@ class SFTPFS(FS):
self
.
remove
(
path2
)
except
ResourceInvalidError
:
self
.
removedir
(
path2
,
force
=
True
)
if
not
self
.
exists
(
path
):
raise
ResourceNotFoundError
(
path
)
try
:
self
.
client
.
rmdir
(
npath
)
except
IOError
,
e
:
...
...
@@ -460,6 +462,7 @@ class SFTPFS(FS):
if
self
.
isfile
(
path
):
raise
ResourceInvalidError
(
path
,
msg
=
"Can't use removedir() on a file:
%(path)
s"
)
raise
ResourceNotFoundError
(
path
)
elif
self
.
listdir
(
path
):
raise
DirectoryNotEmptyError
(
path
)
raise
...
...
fs/tests/__init__.py
View file @
53e8bd02
...
...
@@ -697,8 +697,9 @@ class FSTestCases(object):
def
chunk_stream
():
"""Generate predictable-but-randomy binary content."""
r
=
random
.
Random
(
0
)
randint
=
r
.
randint
for
i
in
xrange
(
num_chunks
):
c
=
""
.
join
(
chr
(
r
.
r
andint
(
0
,
255
))
for
j
in
xrange
(
chunk_size
/
8
))
c
=
""
.
join
(
chr
(
randint
(
0
,
255
))
for
j
in
xrange
(
chunk_size
/
8
))
yield
c
*
8
f
=
self
.
fs
.
open
(
"bigfile"
,
"wb"
)
try
:
...
...
fs/tests/test_expose.py
View file @
53e8bd02
...
...
@@ -14,29 +14,30 @@ import time
from
fs.tests
import
FSTestCases
,
ThreadingTestCases
from
fs.tempfs
import
TempFS
from
fs.osfs
import
OSFS
from
fs.memoryfs
import
MemoryFS
from
fs.path
import
*
from
fs.errors
import
*
from
fs
import
rpcfs
from
fs.expose.xmlrpc
import
RPCFSServer
class
TestRPCFS
(
unittest
.
TestCase
,
FSTestCases
,
ThreadingTestCases
):
class
TestRPCFS
(
unittest
.
TestCase
,
FSTestCases
,
ThreadingTestCases
):
def
makeServer
(
self
,
fs
,
addr
):
return
RPCFSServer
(
fs
,
addr
,
logRequests
=
False
)
def
startServer
(
self
):
port
=
8
000
def
startServer
(
self
):
port
=
3
000
self
.
temp_fs
=
TempFS
()
self
.
server
=
None
while
not
self
.
server
:
try
:
self
.
server
=
self
.
makeServer
(
self
.
temp_fs
,(
"
localhost
"
,
port
))
self
.
server
=
self
.
makeServer
(
self
.
temp_fs
,(
"
127.0.0.1
"
,
port
))
except
socket
.
error
,
e
:
if
e
.
args
[
1
]
==
"Address already in use"
:
port
+=
1
else
:
raise
self
.
server_addr
=
(
"
localhost
"
,
port
)
self
.
server_addr
=
(
"
127.0.0.1
"
,
port
)
self
.
serve_more_requests
=
True
self
.
server_thread
=
threading
.
Thread
(
target
=
self
.
runServer
)
self
.
server_thread
.
daemon
=
True
...
...
@@ -76,7 +77,7 @@ class TestRPCFS(unittest.TestCase,FSTestCases,ThreadingTestCases):
sock
=
None
try
:
sock
=
socket
.
socket
(
af
,
socktype
,
proto
)
sock
.
settimeout
(
1
)
sock
.
settimeout
(
.
1
)
sock
.
connect
(
sa
)
sock
.
send
(
"
\n
"
)
except
socket
.
error
,
e
:
...
...
@@ -96,6 +97,14 @@ class TestSFTPFS(TestRPCFS):
def
setUp
(
self
):
self
.
startServer
()
self
.
fs
=
sftpfs
.
SFTPFS
(
self
.
server_addr
,
no_auth
=
True
)
#def runServer(self):
# self.server.serve_forever()
#
#def tearDown(self):
# self.server.shutdown()
# self.server_thread.join()
# self.temp_fs.close()
def
bump
(
self
):
# paramiko doesn't like being bumped, just wait for it to timeout.
...
...
fs/tests/test_ftpfs.py
View file @
53e8bd02
#!/usr/bin/env python
from
fs.tests
import
FSTestCases
,
ThreadingTestCases
import
unittest
...
...
@@ -25,12 +25,15 @@ class TestFTPFS(unittest.TestCase, FSTestCases, ThreadingTestCases):
def
setUp
(
self
):
global
ftp_port
#
ftp_port += 1
ftp_port
+=
1
use_port
=
str
(
ftp_port
)
#ftp_port = 10000
self
.
temp_dir
=
tempfile
.
mkdtemp
(
u"ftpfstests"
)
self
.
ftp_server
=
subprocess
.
Popen
([
sys
.
executable
,
abspath
(
__file__
),
self
.
temp_dir
,
str
(
use_port
)])
file_path
=
__file__
if
':'
not
in
file_path
:
file_path
=
abspath
(
file_path
)
self
.
ftp_server
=
subprocess
.
Popen
([
sys
.
executable
,
file_path
,
self
.
temp_dir
,
str
(
use_port
)])
# Need to sleep to allow ftp server to start
time
.
sleep
(
.
2
)
self
.
fs
=
ftpfs
.
FTPFS
(
'127.0.0.1'
,
'user'
,
'12345'
,
dircache
=
True
,
port
=
use_port
,
timeout
=
5.0
)
...
...
@@ -40,21 +43,22 @@ class TestFTPFS(unittest.TestCase, FSTestCases, ThreadingTestCases):
def
tearDown
(
self
):
if
sys
.
platform
==
'win32'
:
import
win32api
win32api
.
TerminateProcess
(
int
(
process
.
_handle
),
-
1
)
os
.
popen
(
'TASKKILL /PID '
+
str
(
self
.
ftp_server
.
pid
)
+
' /F'
)
else
:
os
.
system
(
'kill '
+
str
(
self
.
ftp_server
.
pid
))
shutil
.
rmtree
(
self
.
temp_dir
)
self
.
fs
.
close
()
def
check
(
self
,
p
):
return
os
.
path
.
exists
(
os
.
path
.
join
(
self
.
temp_dir
,
relpath
(
p
)))
check_path
=
self
.
temp_dir
.
rstrip
(
os
.
sep
)
+
os
.
sep
+
p
return
os
.
path
.
exists
(
check_path
.
encode
(
'utf-8'
))
if
__name__
==
"__main__"
:
# Run an ftp server that exposes a given directory
import
sys
authorizer
=
ftpserver
.
DummyAuthorizer
()
authorizer
=
ftpserver
.
DummyAuthorizer
()
authorizer
.
add_user
(
"user"
,
"12345"
,
sys
.
argv
[
1
],
perm
=
"elradfmw"
)
authorizer
.
add_anonymous
(
sys
.
argv
[
1
])
...
...
fs/utils.py
View file @
53e8bd02
...
...
@@ -448,7 +448,7 @@ def print_fs(fs, path='/', max_levels=5, file_out=None, terminal_colors=None, hi
terminal_colors
=
True
def
write
(
line
):
file_out
.
write
(
line
.
encode
(
file_encoding
)
+
'
\n
'
)
file_out
.
write
(
line
.
encode
(
file_encoding
,
'replace'
)
+
'
\n
'
)
def
wrap_prefix
(
prefix
):
if
not
terminal_colors
:
...
...
setup.py
View file @
53e8bd02
...
...
@@ -38,7 +38,7 @@ setup(name='fs',
packages
=
[
'fs'
,
'fs.expose'
,
'fs.expose.fuse'
,
'fs.tests'
,
'fs.wrapfs'
,
'fs.osfs'
,
'fs.contrib'
,
'fs.contrib.bigfs'
,
'fs.contrib.davfs'
,
'fs.expose.dokan'
,
'fs.commands'
],
scripts
=
[
'fs/commands/
%
s'
%
command
for
command
in
COMMANDS
],
scripts
=
[
'fs/commands/
%
s
.py
'
%
command
for
command
in
COMMANDS
],
classifiers
=
classifiers
,
)
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