Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
d78def94
Commit
d78def94
authored
May 06, 2011
by
Giampaolo Rodola
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Issue #11072: added MLSD command (RFC-3659) support to ftplib.
parent
0872816d
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
139 additions
and
8 deletions
+139
-8
Doc/library/ftplib.rst
Doc/library/ftplib.rst
+23
-6
Lib/ftplib.py
Lib/ftplib.py
+29
-1
Lib/test/test_ftplib.py
Lib/test/test_ftplib.py
+85
-1
Misc/NEWS
Misc/NEWS
+2
-0
No files found.
Doc/library/ftplib.rst
View file @
d78def94
...
...
@@ -254,13 +254,12 @@ followed by ``lines`` for the text version or ``binary`` for the binary version.
Retrieve a file or directory listing in ASCII transfer mode. *cmd* should be
an appropriate ``RETR`` command (see :meth:`retrbinary`) or a command such as
``LIST``
, ``NLST`` or ``MLSD
`` (usually just the string ``'LIST'``).
``LIST``
or ``NLST
`` (usually just the string ``'LIST'``).
``LIST`` retrieves a list of files and information about those files.
``NLST`` retrieves a list of file names. On some servers, ``MLSD`` retrieves
a machine readable list of files and information about those files. The
*callback* function is called for each line with a string argument containing
the line with the trailing CRLF stripped. The default *callback* prints the
line to ``sys.stdout``.
``NLST`` retrieves a list of file names.
The *callback* function is called for each line with a string argument
containing the line with the trailing CRLF stripped. The default *callback*
prints the line to ``sys.stdout``.
.. method:: FTP.set_pasv(boolean)
...
...
@@ -320,6 +319,20 @@ followed by ``lines`` for the text version or ``binary`` for the binary version.
in :meth:`transfercmd`.
.. method:: FTP.mlsd(path="", facts=[])
List a directory in a standardized format by using MLSD command
(:rfc:`3659`). If *path* is omitted the current directory is assumed.
*facts* is a list of strings representing the type of information desired
(e.g. *["type", "size", "perm"]*). Return a generator object yielding a
tuple of two elements for every file found in path. First element is the
file name, the second one is a dictionary including a variable number of
"facts" depending on the server and whether *facts* argument has been
provided.
.. versionadded:: 3.3
.. method:: FTP.nlst(argument[, ...])
Return a list of file names as returned by the ``NLST`` command. The
...
...
@@ -327,6 +340,8 @@ followed by ``lines`` for the text version or ``binary`` for the binary version.
directory). Multiple arguments can be used to pass non-standard options to
the ``NLST`` command.
.. deprecated:: 3.3 use :meth:`mlsd` instead
.. method:: FTP.dir(argument[, ...])
...
...
@@ -337,6 +352,8 @@ followed by ``lines`` for the text version or ``binary`` for the binary version.
as a *callback* function as for :meth:`retrlines`; the default prints to
``sys.stdout``. This method returns ``None``.
.. deprecated:: 3.3 use :meth:`mlsd` instead
.. method:: FTP.rename(fromname, toname)
...
...
Lib/ftplib.py
View file @
d78def94
...
...
@@ -426,7 +426,7 @@ class FTP:
"""Retrieve data in line mode. A new port is created for you.
Args:
cmd: A RETR, LIST,
NLST, or MLSD
command.
cmd: A RETR, LIST,
or NLST
command.
callback: An optional single parameter callable that is called
for each line with the trailing CRLF stripped.
[default: print_line()]
...
...
@@ -527,6 +527,34 @@ class FTP:
cmd
=
cmd
+
(
' '
+
arg
)
self
.
retrlines
(
cmd
,
func
)
def
mlsd
(
self
,
path
=
""
,
facts
=
[]):
'''List a directory in a standardized format by using MLSD
command (RFC-3659). If path is omitted the current directory
is assumed. "facts" is a list of strings representing the type
of information desired (e.g. ["type", "size", "perm"]).
Return a generator object yielding a tuple of two elements
for every file found in path.
First element is the file name, the second one is a dictionary
including a variable number of "facts" depending on the server
and whether "facts" argument has been provided.
'''
if
facts
:
self
.
sendcmd
(
"OPTS MLST "
+
";"
.
join
(
facts
)
+
";"
)
if
path
:
cmd
=
"MLSD %s"
%
path
else
:
cmd
=
"MLSD"
lines
=
[]
self
.
retrlines
(
cmd
,
lines
.
append
)
for
line
in
lines
:
facts_found
,
_
,
name
=
line
.
rstrip
(
CRLF
).
partition
(
' '
)
entry
=
{}
for
fact
in
facts_found
[:
-
1
].
split
(
";"
):
key
,
_
,
value
=
fact
.
partition
(
"="
)
entry
[
key
.
lower
()]
=
value
yield
(
name
,
entry
)
def
rename
(
self
,
fromname
,
toname
):
'''Rename a file.'''
resp
=
self
.
sendcmd
(
'RNFR '
+
fromname
)
...
...
Lib/test/test_ftplib.py
View file @
d78def94
...
...
@@ -22,10 +22,25 @@ from test.support import HOST
threading
=
support
.
import_module
(
'threading'
)
# the dummy data returned by server over the data channel when
# RETR, LIST
and NLST
commands are issued
# RETR, LIST
, NLST, MLSD
commands are issued
RETR_DATA
=
'abcde12345
\
r
\
n
'
*
1000
LIST_DATA
=
'foo
\
r
\
n
bar
\
r
\
n
'
NLST_DATA
=
'foo
\
r
\
n
bar
\
r
\
n
'
MLSD_DATA
=
(
"type=cdir;perm=el;unique==keVO1+ZF4; test
\
r
\
n
"
"type=pdir;perm=e;unique==keVO1+d?3; ..
\
r
\
n
"
"type=OS.unix=slink:/foobar;perm=;unique==keVO1+4G4; foobar
\
r
\
n
"
"type=OS.unix=chr-13/29;perm=;unique==keVO1+5G4; device
\
r
\
n
"
"type=OS.unix=blk-11/108;perm=;unique==keVO1+6G4; block
\
r
\
n
"
"type=file;perm=awr;unique==keVO1+8G4; writable
\
r
\
n
"
"type=dir;perm=cpmel;unique==keVO1+7G4; promiscuous
\
r
\
n
"
"type=dir;perm=;unique==keVO1+1t2; no-exec
\
r
\
n
"
"type=file;perm=r;unique==keVO1+EG4; two words
\
r
\
n
"
"type=file;perm=r;unique==keVO1+IH4; leading space
\
r
\
n
"
"type=file;perm=r;unique==keVO1+1G4; file1
\
r
\
n
"
"type=dir;perm=cpmel;unique==keVO1+7G4; incoming
\
r
\
n
"
"type=file;perm=r;unique==keVO1+1G4; file2
\
r
\
n
"
"type=file;perm=r;unique==keVO1+1G4; file3
\
r
\
n
"
"type=file;perm=r;unique==keVO1+1G4; file4
\
r
\
n
"
)
class
DummyDTPHandler
(
asynchat
.
async_chat
):
...
...
@@ -49,6 +64,11 @@ class DummyDTPHandler(asynchat.async_chat):
self
.
dtp_conn_closed
=
True
def
push
(
self
,
what
):
if
self
.
baseclass
.
next_data
is
not
None
:
what
=
self
.
baseclass
.
next_data
self
.
baseclass
.
next_data
=
None
if
not
what
:
return
self
.
close_when_done
()
super
(
DummyDTPHandler
,
self
).
push
(
what
.
encode
(
'ascii'
))
def
handle_error
(
self
):
...
...
@@ -67,6 +87,7 @@ class DummyFTPHandler(asynchat.async_chat):
self
.
last_received_cmd
=
None
self
.
last_received_data
=
''
self
.
next_response
=
''
self
.
next_data
=
None
self
.
rest
=
None
self
.
push
(
'220 welcome'
)
...
...
@@ -208,6 +229,14 @@ class DummyFTPHandler(asynchat.async_chat):
self
.
dtp
.
push
(
NLST_DATA
)
self
.
dtp
.
close_when_done
()
def
cmd_opts
(
self
,
arg
):
self
.
push
(
'200 opts ok'
)
def
cmd_mlsd
(
self
,
arg
):
self
.
push
(
'125 mlsd ok'
)
self
.
dtp
.
push
(
MLSD_DATA
)
self
.
dtp
.
close_when_done
()
class
DummyFTPServer
(
asyncore
.
dispatcher
,
threading
.
Thread
):
...
...
@@ -550,6 +579,61 @@ class TestFTPClass(TestCase):
self
.
client
.
dir
(
lambda
x
:
l
.
append
(
x
))
self
.
assertEqual
(
''
.
join
(
l
),
LIST_DATA
.
replace
(
'
\
r
\
n
'
,
''
))
def
test_mlsd
(
self
):
list
(
self
.
client
.
mlsd
())
list
(
self
.
client
.
mlsd
(
path
=
'/'
))
list
(
self
.
client
.
mlsd
(
path
=
'/'
,
facts
=
[
'size'
,
'type'
]))
ls
=
list
(
self
.
client
.
mlsd
())
for
name
,
facts
in
ls
:
self
.
assertTrue
(
name
)
self
.
assertTrue
(
'type'
in
facts
)
self
.
assertTrue
(
'perm'
in
facts
)
self
.
assertTrue
(
'unique'
in
facts
)
def
set_data
(
data
):
self
.
server
.
handler_instance
.
next_data
=
data
def
test_entry
(
line
,
type
=
None
,
perm
=
None
,
unique
=
None
,
name
=
None
):
type
=
'type'
if
type
is
None
else
type
perm
=
'perm'
if
perm
is
None
else
perm
unique
=
'unique'
if
unique
is
None
else
unique
name
=
'name'
if
name
is
None
else
name
set_data
(
line
)
_name
,
facts
=
next
(
self
.
client
.
mlsd
())
self
.
assertEqual
(
_name
,
name
)
self
.
assertEqual
(
facts
[
'type'
],
type
)
self
.
assertEqual
(
facts
[
'perm'
],
perm
)
self
.
assertEqual
(
facts
[
'unique'
],
unique
)
# plain
test_entry
(
'type=type;perm=perm;unique=unique; name
\
r
\
n
'
)
# "=" in fact value
test_entry
(
'type=ty=pe;perm=perm;unique=unique; name
\
r
\
n
'
,
type
=
"ty=pe"
)
test_entry
(
'type==type;perm=perm;unique=unique; name
\
r
\
n
'
,
type
=
"=type"
)
test_entry
(
'type=t=y=pe;perm=perm;unique=unique; name
\
r
\
n
'
,
type
=
"t=y=pe"
)
test_entry
(
'type=====;perm=perm;unique=unique; name
\
r
\
n
'
,
type
=
"===="
)
# spaces in name
test_entry
(
'type=type;perm=perm;unique=unique; na me
\
r
\
n
'
,
name
=
"na me"
)
test_entry
(
'type=type;perm=perm;unique=unique; name
\
r
\
n
'
,
name
=
"name "
)
test_entry
(
'type=type;perm=perm;unique=unique; name
\
r
\
n
'
,
name
=
" name"
)
test_entry
(
'type=type;perm=perm;unique=unique; n am e
\
r
\
n
'
,
name
=
"n am e"
)
# ";" in name
test_entry
(
'type=type;perm=perm;unique=unique; na;me
\
r
\
n
'
,
name
=
"na;me"
)
test_entry
(
'type=type;perm=perm;unique=unique; ;name
\
r
\
n
'
,
name
=
";name"
)
test_entry
(
'type=type;perm=perm;unique=unique; ;name;
\
r
\
n
'
,
name
=
";name;"
)
test_entry
(
'type=type;perm=perm;unique=unique; ;;;;
\
r
\
n
'
,
name
=
";;;;"
)
# case sensitiveness
set_data
(
'Type=type;TyPe=perm;UNIQUE=unique; name
\
r
\
n
'
)
_name
,
facts
=
next
(
self
.
client
.
mlsd
())
[
self
.
assertTrue
(
x
.
islower
())
for
x
in
facts
.
keys
()]
# no data (directory empty)
set_data
(
''
)
self
.
assertRaises
(
StopIteration
,
next
,
self
.
client
.
mlsd
())
set_data
(
''
)
for
x
in
self
.
client
.
mlsd
():
self
.
fail
(
"unexpected data %s"
%
data
)
def
test_makeport
(
self
):
with
self
.
client
.
makeport
():
# IPv4 is in use, just make sure send_eprt has not been used
...
...
Misc/NEWS
View file @
d78def94
...
...
@@ -140,6 +140,8 @@ Core and Builtins
Library
-------
- Issue #11072: added MLSD command (RFC-3659) support to ftplib.
- Issue #8808: The IMAP4_SSL constructor now allows passing an SSLContext
parameter to control parameters of the secure channel. Patch by Sijin
Joseph.
...
...
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