[ftputil] Problem using FTPHost.makedirs()
Roger Demetrescu
roger.demetrescu at gmail.com
Thu Nov 6 13:52:44 CET 2014
Hi Stefan
On Thu, Nov 6, 2014 at 10:46 AM, Roger Demetrescu
<roger.demetrescu at gmail.com> wrote:
> Hi Stefan,
>
> On Wed, Nov 5, 2014 at 5:27 AM, Stefan Schwarzer
> <sschwarzer at sschwarzer.net> wrote:
>> Hi Roger :-)
>>
>> On 2014-11-04 18:04, Roger Demetrescu wrote:
>>> Ok... I did some more testes and found out that I am not able to see
>>> any files/directories inside:
>>>
>>> /aaa
>>> /aaa/bbb
>>
>> I'm always surprised about the anomalies of FTP servers,
>> although having seen a bunch in bug reports. ;-)
>
> So do I... :)
>
>
>>
>>> I had to change FTPHost's makedirs implementation to this code:
>>
>>> def makedirs(self, path, mode=None):
>>> ...
>>> path = ftputil.tool.as_unicode(path)
>>> path = self.path.abspath(path)
>>> directories = path.split(self.sep)
>>> old_dir = self.getcwd()
>>>
>>> try:
>>> # Try to build the directory chain from the "uppermost" to
>>> # the "lowermost" directory.
>>> for index in range(1, len(directories)):
>>> # Re-insert the separator which got lost by using `path.split`.
>>> next_directory = self.sep + self.path.join(*directories[:index+1])
>>> try:
>>> self.chdir(next_directory)
>>> except ftputil.error.PermanentError:
>>> self.mkdir(next_directory)
>>> finally:
>>> self.chdir(old_dir)
>>>
>>> It works like a charm... :)
>>
>> I'm glad you could make it work - and saved me some brooding
>> over the problem. ;-)
>>
>> When seeing your solution I was thinking about whether it
>> was a complete substitute (and hence could go into ftputil)
>> or if it was failing for some other cases the current
>> `makedirs` implementation covers. Particularly, could there
>> be reasons for `chdir` to fail, apart from the non-existence
>> of the directory? What about other problems?
>>
>> I can think of these issues:
>>
>> - The directory `next_directory` exists, but you can't
>> change to it because you don't have execute permission (or
>> some equivalent on the server).
>>
>> In this case `mkdir` would try to create it and fail and
>> thus raise an exception (as in the old implementation). If
>> the directory existed and was the last in the chain of
>> directories to create, `makedirs` should have succeeded
>> though. So this is a case that the old implementation
>> covers but not the new one.
>>
>> If the directory is not the last in the chain, `chdir`
>> would fail and `mkdir` as well and raise a
>> `PermanentError`. The same would have happened with the
>> old implementation and is in my opinion the expected
>> behavior.
>>
>> - The "directory" `next_directory` exists, but is a file.
>>
>> Here, `chdir` would fail and then the `mkdir` as well,
>> which would be the expected behavior.
>>
>> - You aren't allowed to change to `/aaa` though you might be
>> allowed to change to `/aaa/bbb`.
>>
>> On Unix, you can't change to `/aaa/bbb` if you can't
>> change to `/aaa` if I'm not mistaken. I don't know if the
>> same is true for FTP servers. It might be but due to some
>> anomaly it might not be. If this anomaly applies, the old
>> implementation might work but not the new one.
>>
>> - What cases might I have forgotten? ...
>>
>> Can you think of a way to counter the above problems with
>> the new approach but still keeping the advantages of the old
>> and new one? Ok, the last bullet item seems obscure to me,
>> but the problem from the first one not so much. But even if
>> we were able to solve the problem from the first item, it
>> would mean we make `makedirs` work for one obscure case, but
>> fail for another obscure case. ;-/
>
>
> We could mix the two approachs:
>
>
>
> try:
> self.chdir(next_directory)
> except ftputil.error.PermanentError:
> try:
> self.mkdir(next_directory)
> except ftputil.error.PermanentError:
> if not self.path.isdir(next_directory):
> raise
>
>
> Does it solve all (existing for now) concerns ? :)
Better put the full code: :)
# Ignore unused argument `mode`
# pylint: disable=unused-argument
def makedirs(self, path, mode=None):
"""
Make the directory `path`, but also make not yet existing
intermediate directories, like `os.makedirs`. The value
of `mode` is only accepted for compatibility with
`os.makedirs` but otherwise ignored.
"""
path = ftputil.tool.as_unicode(path)
path = self.path.abspath(path)
directories = path.split(self.sep)
old_dir = self.getcwd()
try:
# Try to build the directory chain from the "uppermost" to
# the "lowermost" directory.
for index in range(1, len(directories)):
# Re-insert the separator which got lost by using `path.split`.
next_directory = self.sep +
self.path.join(*directories[:index+1])
try:
self.chdir(next_directory)
except ftputil.error.PermanentError:
try:
self.mkdir(next_directory)
except ftputil.error.PermanentError:
# Find out the cause of the error. Re-raise the
# exception only if the directory didn't exist already,
# else something went _really_ wrong, e. g. there's a
# regular file with the name of the directory.
if not self.path.isdir(next_directory):
raise
except:
self.chdir(old_dir)
Best regards,
Roger
More information about the ftputil
mailing list