<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On Wed, Dec 28, 2016 at 3:57 PM, Ivan Pozdeev <span dir="ltr"><<a href="mailto:vano@mail.mipt.ru" target="_blank">vano@mail.mipt.ru</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
  
    
  
  <div bgcolor="#FFFFFF"><span class="gmail-">
    
    On 25.12.2016 17:21, Giampaolo Rodola' wrote:<br>
    <blockquote type="cite">
      <div dir="ltr">From what I understand the problem should be fixed
        in urllib so that it always closes the FTP connection. I
        understand this is what happens in recent 3.x versions but not
        on 2.7.</div>
    </blockquote></span>
    urllib in 2.7 should indeed be fixed to close FTP connections rather
    than leave them dangling, no question about that.<br>
    </div></blockquote><div><br></div><div>To me it looks like this is the only issue (currently tracked in <a href="http://bugs.python.org/issue28931">http://bugs.python.org/issue28931</a>).</div><div> <br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div bgcolor="#FFFFFF">I tried to fix urllib so that it's guaranteed to call voidresp(),
    and failed. Too complicated, effectively reimplementing a sizable
    part of FTP client logic is required, perhaps even monkey-patching
    ftplib to be able to set flags in the right places.</div></blockquote><div><br></div><div>Why did you fail? Why urllib can't close() -> voidresp() the FTP session once it's done with it? </div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div bgcolor="#FFFFFF">
    With simple commands (<code class="gmail-m_-1450766179390561399descname">voidcmd</code>) and
    self-contained transfer commands (retrlines/retrbinary), ftplib does
    incapsulate statefulness, by handling them atomically. But with
    transfercmd(), it returns control to the user halfway through.<br></div></blockquote><div><br></div><div>That's by design. ftplib's transfercmd() just works like that: it's a low level method returning a "data" socket and it's just up to you to cleanly close the session (close() -> voidresp()) once you're done with it. Most of the times you don't even want to use transfercmd() in the first place, as you just use stor* and retr* methods.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div bgcolor="#FFFFFF">
    At this point, if ftplib doesn't itself keep track that the control
    connection is currently in invalid state for the next command, the
    user is forced to do that themselves instead. </div></blockquote><div><br></div><div>Hence why I suggested a docfix.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div bgcolor="#FFFFFF">And guess what -
    urllib has to use ftplib through a wrapper class that does exactly
    that. That's definitely a "stock logic part" 'cuz it's an integral
    part of FTP rather than something specific to user logic.<br></div></blockquote><div><br></div><div>That wrapper should just cleanly close the session. </div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div bgcolor="#FFFFFF">
    The other "stock logic part" currently missing is error handling
    during transfer. If the error's cause is local (like a local
    exception that results in closing the data socket prematurely, or
    the user closing it by hand, or an ABOR they sent midway), the error
    code from the end-of-transfer response should be ignored, otherwise,
    it shouldn't.<br></div></blockquote><div><br></div><div><div>Absolutely not. Base ftplib should NOT take any deliberate decision such as ignoring an error.</div><div>I can even come up with use cases: use ftplib to test FTP servers so that they *do* reply with an error code in certain conditions. Here's a couple examples in pyftpdlib:</div><div><br></div><div><a href="https://github.com/giampaolo/pyftpdlib/blob/17bebe3714752b01e43ab60d2771308f4594bd99/pyftpdlib/test/test_functional.py#L1354-L1369">https://github.com/giampaolo/pyftpdlib/blob/17bebe3714752b01e43ab60d2771308f4594bd99/pyftpdlib/test/test_functional.py#L1354-L1369</a></div></div><div><br></div><div><a href="https://github.com/giampaolo/pyftpdlib/blob/17bebe3714752b01e43ab60d2771308f4594bd99/pyftpdlib/test/test_functional.py#L1973-L1980">https://github.com/giampaolo/pyftpdlib/blob/17bebe3714752b01e43ab60d2771308f4594bd99/pyftpdlib/test/test_functional.py#L1973-L1980</a></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div bgcolor="#FFFFFF">
    Other than making ftplib keep track of the session's internal state
    while the user has control and providing the custom error handling
    logic to them, another solution is to never release control to the
    user midway, i.e. ban transfercmd() entirely and only provide
    self-contained retr*()/dir()/whatever, but let the callback signal
    them to abort the transfer. That way, the state would be kept
    implicitly - in the execution pointer.</div></blockquote><div><br></div><div>Banning transfercmd() means renaming it to _transfercmd() which is a no-no for backward compatibility. Also, as I've shown above, transfercmd() does have a use case which relies on it behaving like that (return control midway). </div><div> </div></div><div><br></div>-- <br><div class="gmail_signature"><div dir="ltr"><div>Giampaolo - <a href="http://grodola.blogspot.com" target="_blank">http://grodola.blogspot.com</a></div><div><br></div></div></div>
</div></div>