1010import io
1111import errno
1212import os
13+ import time
1314try :
1415 import ssl
1516except ImportError :
@@ -137,6 +138,9 @@ def cmd_echo(self, arg):
137138 # sends back the received string (used by the test suite)
138139 self .push (arg )
139140
141+ def cmd_noop (self , arg ):
142+ self .push ('200 noop ok' )
143+
140144 def cmd_user (self , arg ):
141145 self .push ('331 username ok' )
142146
@@ -218,6 +222,7 @@ def __init__(self, address, af=socket.AF_INET):
218222 self .active = False
219223 self .active_lock = threading .Lock ()
220224 self .host , self .port = self .socket .getsockname ()[:2 ]
225+ self .handler_instance = None
221226
222227 def start (self ):
223228 assert not self .active
@@ -241,8 +246,7 @@ def stop(self):
241246
242247 def handle_accept (self ):
243248 conn , addr = self .accept ()
244- self .handler = self .handler (conn )
245- self .close ()
249+ self .handler_instance = self .handler (conn )
246250
247251 def handle_connect (self ):
248252 self .close ()
@@ -459,12 +463,12 @@ def test_acct(self):
459463
460464 def test_rename (self ):
461465 self .client .rename ('a' , 'b' )
462- self .server .handler .next_response = '200'
466+ self .server .handler_instance .next_response = '200'
463467 self .assertRaises (ftplib .error_reply , self .client .rename , 'a' , 'b' )
464468
465469 def test_delete (self ):
466470 self .client .delete ('foo' )
467- self .server .handler .next_response = '199'
471+ self .server .handler_instance .next_response = '199'
468472 self .assertRaises (ftplib .error_reply , self .client .delete , 'foo' )
469473
470474 def test_size (self ):
@@ -512,7 +516,7 @@ def test_retrlines(self):
512516 def test_storbinary (self ):
513517 f = io .BytesIO (RETR_DATA .encode ('ascii' ))
514518 self .client .storbinary ('stor' , f )
515- self .assertEqual (self .server .handler .last_received_data , RETR_DATA )
519+ self .assertEqual (self .server .handler_instance .last_received_data , RETR_DATA )
516520 # test new callback arg
517521 flag = []
518522 f .seek (0 )
@@ -524,12 +528,12 @@ def test_storbinary_rest(self):
524528 for r in (30 , '30' ):
525529 f .seek (0 )
526530 self .client .storbinary ('stor' , f , rest = r )
527- self .assertEqual (self .server .handler .rest , str (r ))
531+ self .assertEqual (self .server .handler_instance .rest , str (r ))
528532
529533 def test_storlines (self ):
530534 f = io .BytesIO (RETR_DATA .replace ('\r \n ' , '\n ' ).encode ('ascii' ))
531535 self .client .storlines ('stor' , f )
532- self .assertEqual (self .server .handler .last_received_data , RETR_DATA )
536+ self .assertEqual (self .server .handler_instance .last_received_data , RETR_DATA )
533537 # test new callback arg
534538 flag = []
535539 f .seek (0 )
@@ -548,14 +552,59 @@ def test_dir(self):
548552 def test_makeport (self ):
549553 self .client .makeport ()
550554 # IPv4 is in use, just make sure send_eprt has not been used
551- self .assertEqual (self .server .handler .last_received_cmd , 'port' )
555+ self .assertEqual (self .server .handler_instance .last_received_cmd , 'port' )
552556
553557 def test_makepasv (self ):
554558 host , port = self .client .makepasv ()
555559 conn = socket .create_connection ((host , port ), 2 )
556560 conn .close ()
557561 # IPv4 is in use, just make sure send_epsv has not been used
558- self .assertEqual (self .server .handler .last_received_cmd , 'pasv' )
562+ self .assertEqual (self .server .handler_instance .last_received_cmd , 'pasv' )
563+
564+ def test_with_statement (self ):
565+ self .client .quit ()
566+
567+ def is_client_connected ():
568+ if self .client .sock is None :
569+ return False
570+ try :
571+ self .client .sendcmd ('noop' )
572+ except (socket .error , EOFError ):
573+ return False
574+ return True
575+
576+ # base test
577+ with ftplib .FTP (timeout = 2 ) as self .client :
578+ self .client .connect (self .server .host , self .server .port )
579+ self .client .sendcmd ('noop' )
580+ self .assertTrue (is_client_connected ())
581+ self .assertEqual (self .server .handler_instance .last_received_cmd , 'quit' )
582+ self .assertFalse (is_client_connected ())
583+
584+ # QUIT sent inside the with block
585+ with ftplib .FTP (timeout = 2 ) as self .client :
586+ self .client .connect (self .server .host , self .server .port )
587+ self .client .sendcmd ('noop' )
588+ self .client .quit ()
589+ self .assertEqual (self .server .handler_instance .last_received_cmd , 'quit' )
590+ self .assertFalse (is_client_connected ())
591+
592+ # force a wrong response code to be sent on QUIT: error_perm
593+ # is expected and the connection is supposed to be closed
594+ try :
595+ with ftplib .FTP (timeout = 2 ) as self .client :
596+ self .client .connect (self .server .host , self .server .port )
597+ self .client .sendcmd ('noop' )
598+ self .server .handler_instance .next_response = '550 error on quit'
599+ except ftplib .error_perm as err :
600+ self .assertEqual (str (err ), '550 error on quit' )
601+ else :
602+ self .fail ('Exception not raised' )
603+ # needed to give the threaded server some time to set the attribute
604+ # which otherwise would still be == 'noop'
605+ time .sleep (0.1 )
606+ self .assertEqual (self .server .handler_instance .last_received_cmd , 'quit' )
607+ self .assertFalse (is_client_connected ())
559608
560609
561610class TestIPv6Environment (TestCase ):
@@ -575,13 +624,13 @@ def test_af(self):
575624
576625 def test_makeport (self ):
577626 self .client .makeport ()
578- self .assertEqual (self .server .handler .last_received_cmd , 'eprt' )
627+ self .assertEqual (self .server .handler_instance .last_received_cmd , 'eprt' )
579628
580629 def test_makepasv (self ):
581630 host , port = self .client .makepasv ()
582631 conn = socket .create_connection ((host , port ), 2 )
583632 conn .close ()
584- self .assertEqual (self .server .handler .last_received_cmd , 'epsv' )
633+ self .assertEqual (self .server .handler_instance .last_received_cmd , 'epsv' )
585634
586635 def test_transfer (self ):
587636 def retr ():
0 commit comments