Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions docs/tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,25 @@ and a range of `passive ports <api.html#pyftpdlib.handlers.FTPHandler.passive_po
main()


Use with context
^^^^^^^^^^^^^^^^

.. code-block:: python

authorizer = DummyAuthorizer()
authorizer.add_anonymous(".")
handler = FTPHandler
handler.authorizer = authorizer
server = FTPServer(("localhost", port), handler)
with FTPServerContext(server):
with FTP() as ftp:
ftp.connect(host='localhost', port=port)
ftp.login()
print(ftp.dir())
print("get the directory successfully first time")
print("now the ftpserver has been closed automatically")


Logging management
==================

Expand Down
34 changes: 34 additions & 0 deletions pyftpdlib/servers.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,40 @@ def close_all(self):
return self.ioloop.close()


class FTPServerContext():
"""
use the FTPServerContext

with FTPServerContext(FTPServer):
do_something
# auto close the FTPServer
"""

def __init__(self, server):
self.server = server
self.thread = None

def __enter__(self):
logger.debug("you are in FTPServerContext now!")
self.start_thread()
return

def __exit__(self, exc_type, exc_value, traceback):
logger.debug("you are existing FTPServerContext")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: existing -> exiting

self.server.close_all()
self.thread.join()
return

def start_thread(self):
logger.debug("the FTPServer is running")
self.thread = threading.Thread(
group=None,
target=self.server.serve_forever,
)
self.thread.start()
return


# ===================================================================
# --- extra implementations
# ===================================================================
Expand Down
58 changes: 58 additions & 0 deletions pyftpdlib/test/test_context_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""
test run a ftpserver using with statement
"""

import random

from ftplib import FTP

from pyftpdlib.servers import FTPServer, FTPServerContext
from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler

from pyftpdlib.test import PyftpdlibTestCase
from pyftpdlib.test import unittest


# logging.basicConfig(level=logging.INFO)


class ContextServerTest(PyftpdlibTestCase):

def test_context(self):
port = random.randint(10000, 20000)
authorizer = DummyAuthorizer()
authorizer.add_anonymous(".")
handler = FTPHandler
handler.authorizer = authorizer
server = FTPServer(("localhost", port), handler)
with FTPServerContext(server):
with FTP() as ftp:
ftp.connect(host='localhost', port=port)
ftp.login()
# time.sleep(1)
print(ftp.dir())
print("get the directory successfully first time")

def test_auto_close(self):
port = random.randint(10000, 20000)
authorizer = DummyAuthorizer()
authorizer.add_anonymous(".")
handler = FTPHandler
handler.authorizer = authorizer
server = FTPServer(("localhost", port), handler)
with FTPServerContext(server):
with FTP() as ftp:
ftp.connect(host='localhost', port=port)
ftp.login()
# time.sleep(1)
print(ftp.dir())
print("get the directory successfully first time")
with self.assertRaises(ConnectionRefusedError):
with FTP() as ftp:
ftp.connect(host='localhost', port=port)
ftp.login()


if __name__ == "__main__":
unittest.main()