You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
os.listdir() returns unusable bytes result on Windows #60860
This critical bug is one of the reasons that non-English speaking communities doesn't adopt Python as broadly as it happens in English world compared to other technologies (PHP etc.).
The problem exhibited is not coming from the os.walk() implementation, but from the use of a byte-string as the argument to it.
The directories are created with unicode literals and therefore the argument must also be a unicode literal (u'.') for them to be shown. See the note in the listdir() documentation.
As it stands, I suggest that this is closed as invalid, or at the minimum that it could be a documentation bug for walk() not also having a similar note as listdir().
Works for me without the u'.', too, though less usefully:
>>> for r, dirs, files in os.walk('.'):
... print dirs
...
['\xd0\xa0\xd1\x83\xd1\x81\xd1\x81\xd0\xba\xd0\xbe\xd0\xb5 \xd0\xb8\xd0\xbc\xd1\x8f']
Maybe that doesn't work on Windows, though. I am, of course, assuming that python3 does the right thing on Windows, but I can't imagine Victor would have overlooked that.
techtonik commented on Dec 11, 2012
techtonikmannequin
MannequinAuthor
More actions
In Python 3 it fails with UnicodeEncodeError in "C:\Python33\lib\encodings\cp437.py", while Vista's 'dir' command shows everything correctly in the same console, so somebody definitely overlooked that aspect.
This bug is clearly an issue for developers who write products for international markets. It is neither out of date, nor it is invalid. Note in documentation in red is a must have, also a warning should be issued in warning mode when os.walk() ignores international dirs. I doubt there are many people who aware of this racist behavior and want it be default.
Oh, I remember Victor complaining about that behavior of Windows. I'm pretty sure it is the windows API and not python that is doing that mangling. But Victor would know for sure.
I'm a little confused. FindFirstFile is an ANSI API, so we get a narrow string back. We call PyBytes_FromString(), which expects a narrow string and returns a bytes object. Who's trying (and failing) to encode the filename?
On Windows with Python 2, unencodable characters are replaced with "?". It is the default behaviour of WideCharToMultiByte() and so all ANSI functions have this behaviour. Python doesn't try to behave differently, it just exposes system function as Python functions.
So for example, os.listdir(bytes) returns filename with "?" if some characters are not encodable to the ANSI codepage. It's a choice in the design of Windows.
This critical bug is one of the reasons that non-English speaking
communities doesn't adopt Python as broadly as it happens in
English world compared to other technologies (PHP etc.).
I don't understand this point.
PHP doesn't have a Unicode type, I'm quite sure that PHP have exactly the same issue. And this issue is only solved in Python 3... except if you explicitly uses a bytes filename (for os.listdir/os.walk), but the bytes filename API has been deprecated in Python 3.3.
In Python 2, you can use Unicode filenames to workaround this issue. But it doesn't work as well as Python 3: on UNIX, you will get a similar issue with undecodable filenames (which is the opposite of unencodable filenames).
About listdir_unicode-2.7.patch: Python chose to work as Windows with unencodable characters. If you want to change the behaviour, you must change *all* calls to the Windows ANSI API (which is not trivial). Anyway, as I wrote, the bytes API is deprecated for filenames in Python 3.3. I prefer to not change anything in Python 2, because it may break existing applications. For example, os.listdir(bytes) doesn't fail in Python 2.7 with unencodable names, whereas it fails with your patch.
Nothing interesting in this issue, I'm closing it. If your consider the redirection issue important, please open a new issue.
For example, os.listdir(bytes) doesn't fail in Python 2.7 with unencodable names, whereas it fails with your patch.
No. The purpose of this patch is that it doesn't fail and should return
a usable result.
techtonik commented on Dec 15, 2012
techtonikmannequin
MannequinAuthor
More actions
haypo didn't understood the main user story for this ticket and closed it, so I reopen it with simplified user story.
"""As a developer, I want Python os.listdir('.') function return all directories in current directory on Windows, not matter how international they are, in the same way as other Windows applications return them.
I want returned name to be normal string, but reuseable in subsequent functions, so that I can query stats for this dir or CD to it. It doesn't matter how this name is binary represented. It can be quoted, in UTF-8 byte code or whatever - I don't care as long as I can access the same name from the same session."""
Looks as this is a duplicate of bpo-13247. And Victor submitted a patch with approach similar to me (except that my patch does not raises an exception, but returns an unicode for unencodable names).
It looks as longliving design bug and perhaps really be closed as "won't fix".
Indeed, os.listdir() should always be called with a unicode argument under Windows (which is the natural thing to do in 3.x, anyway). The other issue (with print() unable to display some symbols depending on the codepage) is unrelated.
Activity
techtonik commentedon Dec 10, 2012
This critical bug is one of the reasons that non-English speaking communities doesn't adopt Python as broadly as it happens in English world compared to other technologies (PHP etc.).
# -- coding: utf-8 --
This gives:
['English name']
[]
Windows Vista.
serhiy-storchaka commentedon Dec 10, 2012
Is it reproduced on 3.x?
bitdancer commentedon Dec 10, 2012
No.
bitdancer commentedon Dec 10, 2012
Oops, clicked submit too soon.
It isn't likely to get fixed in 2.7, because 2.7's unicode support problems is the major reason python3 was developed.
bitdancer commentedon Dec 10, 2012
For that matter, it isn't reproduced in python2.7, either:
jkloth commentedon Dec 10, 2012
The problem exhibited is not coming from the os.walk() implementation, but from the use of a byte-string as the argument to it.
The directories are created with unicode literals and therefore the argument must also be a unicode literal (u'.') for them to be shown. See the note in the listdir() documentation.
As it stands, I suggest that this is closed as invalid, or at the minimum that it could be a documentation bug for walk() not also having a similar note as listdir().
bitdancer commentedon Dec 10, 2012
Works for me without the u'.', too, though less usefully:
Maybe that doesn't work on Windows, though. I am, of course, assuming that python3 does the right thing on Windows, but I can't imagine Victor would have overlooked that.
techtonik commentedon Dec 11, 2012
In Python 3 it fails with UnicodeEncodeError in "C:\Python33\lib\encodings\cp437.py", while Vista's 'dir' command shows everything correctly in the same console, so somebody definitely overlooked that aspect.
This bug is clearly an issue for developers who write products for international markets. It is neither out of date, nor it is invalid. Note in documentation in red is a must have, also a warning should be issued in warning mode when os.walk() ignores international dirs. I doubt there are many people who aware of this racist behavior and want it be default.
amauryfa commentedon Dec 11, 2012
22 remaining items
bitdancer commentedon Dec 14, 2012
Oh, I remember Victor complaining about that behavior of Windows. I'm pretty sure it is the windows API and not python that is doing that mangling. But Victor would know for sure.
larryhastings commentedon Dec 14, 2012
I'm a little confused. FindFirstFile is an ANSI API, so we get a narrow string back. We call PyBytes_FromString(), which expects a narrow string and returns a bytes object. Who's trying (and failing) to encode the filename?
serhiy-storchaka commentedon Dec 14, 2012
Windows. File created using Unicode API and stored UTF-16 encoded in
NTFS. Windows fails to represent this filename using ANSI API.
Here is a patch against 2.7 which always uses Unicode API in listdir()
and tries to encode filenames to str if str argument used.
serhiy-storchaka commentedon Dec 14, 2012
I can't test (and even compile) the patch as I don't have a Windows,
please test it for me.
vstinner commentedon Dec 14, 2012
On Windows with Python 2, unencodable characters are replaced with "?". It is the default behaviour of WideCharToMultiByte() and so all ANSI functions have this behaviour. Python doesn't try to behave differently, it just exposes system function as Python functions.
So for example, os.listdir(bytes) returns filename with "?" if some characters are not encodable to the ANSI codepage. It's a choice in the design of Windows.
I don't understand this point.
PHP doesn't have a Unicode type, I'm quite sure that PHP have exactly the same issue. And this issue is only solved in Python 3... except if you explicitly uses a bytes filename (for os.listdir/os.walk), but the bytes filename API has been deprecated in Python 3.3.
In Python 2, you can use Unicode filenames to workaround this issue. But it doesn't work as well as Python 3: on UNIX, you will get a similar issue with undecodable filenames (which is the opposite of unencodable filenames).
Read my book for more information: https://github.com/haypo/unicode_book/wiki
--
About listdir_unicode-2.7.patch: Python chose to work as Windows with unencodable characters. If you want to change the behaviour, you must change *all* calls to the Windows ANSI API (which is not trivial). Anyway, as I wrote, the bytes API is deprecated for filenames in Python 3.3. I prefer to not change anything in Python 2, because it may break existing applications. For example, os.listdir(bytes) doesn't fail in Python 2.7 with unencodable names, whereas it fails with your patch.
Nothing interesting in this issue, I'm closing it. If your consider the redirection issue important, please open a new issue.
vstinner commentedon Dec 14, 2012
Ooops, I mean: this issue is *already* solved in Python 3
serhiy-storchaka commentedon Dec 14, 2012
No. The purpose of this patch is that it doesn't fail and should return
a usable result.
techtonik commentedon Dec 15, 2012
haypo didn't understood the main user story for this ticket and closed it, so I reopen it with simplified user story.
"""As a developer, I want Python os.listdir('.') function return all directories in current directory on Windows, not matter how international they are, in the same way as other Windows applications return them.
I want returned name to be normal string, but reuseable in subsequent functions, so that I can query stats for this dir or CD to it. It doesn't matter how this name is binary represented. It can be quoted, in UTF-8 byte code or whatever - I don't care as long as I can access the same name from the same session."""
amauryfa commentedon Dec 15, 2012
Anatoly,
print repr(os.listdir(u'.'))
print(ascii(os.listdir('.')))
Do the commands above work correctly?
serhiy-storchaka commentedon Dec 16, 2012
Looks as this is a duplicate of bpo-13247. And Victor submitted a patch with approach similar to me (except that my patch does not raises an exception, but returns an unicode for unencodable names).
It looks as longliving design bug and perhaps really be closed as "won't fix".
pitrou commentedon Dec 16, 2012
Indeed, os.listdir() should always be called with a unicode argument under Windows (which is the natural thing to do in 3.x, anyway). The other issue (with print() unable to display some symbols depending on the codepage) is unrelated.