GeezerVisonThe World Wide Web, restored to 800 pixels of glory. |
| Home|About|Gaming cabinet|Archives|How it works|Starter sites |
GeezerVison is showing peps.python.org — PEP 529 – Change Windows filesystem encoding to UTF-8 | peps.python.org
PEP 529 – Change Windows filesystem encoding to UTF-8
PEP 529 – Change Windows filesystem encoding to UTF-8
Table of ContentsAbstractHistorically, Python uses the ANSI APIs for interacting with the Windows operating system, often via C Runtime functions. However, these have been long discouraged in favor of the UTF-16 APIs. Within the operating system, all text is represented as UTF-16, and the ANSI APIs perform encoding and decoding using the active code page. See Naming Files, Paths, and Namespaces for more details. This PEP proposes changing the default filesystem encoding on Windows to utf-8, and changing all filesystem functions to use the Unicode APIs for filesystem paths. This will not affect code that uses strings to represent paths, however those that use bytes for paths will now be able to correctly round-trip all valid paths in Windows filesystems. Currently, the conversions between Unicode (in the OS) and bytes (in Python) were lossy and would fail to round-trip characters outside of the user’s active code page. Notably, this does not impact the encoding of the contents of files. These will
continue to default to BackgroundFile system paths are almost universally represented as text with an encoding
determined by the file system. In Python, we expose these paths via a number of
interfaces, such as the When paths are passed between the filesystem and the application, they are
either passed through as a bytes blob or converted to/from str using
On Windows, the native format for the filesystem is utf-16-le. The recommended platform APIs for accessing the filesystem all accept and return text encoded in this format. However, prior to Windows NT (and possibly further back), the native format was a configurable machine option and a separate set of APIs existed to accept this format. The option (the “active code page”) and these APIs (the “*A functions”) still exist in recent versions of Windows for backwards compatibility, though new functionality often only has a utf-16-le API (the “*W functions”). In Python, str is recommended because it can correctly round-trip all characters used in paths (on POSIX with surrogateescape handling; on Windows because str maps to the native representation). On Windows bytes cannot round-trip all characters used in paths, as Python internally uses the *A functions and hence the encoding is “whatever the active code page is”. Since the active code page cannot represent all Unicode characters, the conversion of a path into bytes can lose information without warning or any available indication. As a demonstration of this: >>> open('test\uAB00.txt', 'wb').close() >>> import glob >>> glob.glob('test*') ['test\uab00.txt'] >>> glob.glob(b'test*') [b'test?.txt'] The Unicode character in the second call to glob has been replaced by a ‘?’,
which means passing the path back into the filesystem will result in a
While one user-accessible fix is to use str everywhere, POSIX systems generally
do not suffer from data loss when using bytes exclusively as the bytes are the
canonical representation. Even if the encoding is “incorrect” by some standard,
the file system will still map the bytes back to the file. Making use of this
avoids the cost of decoding and reencoding, such that (theoretically, and only
on POSIX), code such as this may be faster because of the use of >>> for f in os.listdir(b'.'): ... os.stat(f) ... As a result, POSIX-focused library authors prefer to use bytes to represent paths. For some authors it is also a convenience, as their code may receive bytes already known to be encoded correctly, while others are attempting to simplify porting their code from Python 2. However, the correctness assumptions do not carry over to Windows where Unicode is the canonical representation, and errors may result. This potential data loss is why the use of bytes paths on Windows was deprecated in Python 3.3 - all of the above code snippets produce deprecation warnings on Windows. ProposalCurrently the default filesystem encoding is ‘mbcs’, which is a meta-encoder that uses the active code page. However, when bytes are passed to the filesystem they go through the *A APIs and the operating system handles encoding. In this case, paths are always encoded using the equivalent of ‘mbcs:replace’ with no opportunity for Python to override or change this. This proposal would remove all use of the *A APIs and only ever call the *W
APIs. When Windows returns paths to Python as The use of utf-8 will not be configurable, except for the provision of a “legacy mode” flag to revert to the previous behaviour. The The choice of utf-8 bytes (as opposed to utf-16-le bytes) is to ensure the
ability to round-trip path names and allow basic manipulation (for example,
using the This change would also undeprecate the use of bytes paths on Windows. No change
to the semantics of using bytes as a path is required - as before, they must be
encoded with the encoding specified by Specific ChangesUpdate sys.getfilesystemencodingRemove the default value for Update the implementations of Add sys.getfilesystemencodeerrorsAs the error mode may now change between Add a public Add a public Update the implementations of Update the implementations of Update path_converterUpdate the path converter to always decode bytes or buffer objects into text
using Change the Remove unused ANSI codeRemove all code paths using the Add legacy modeAdd a legacy mode flag, enabled by the environment variable
When this flag is set, the default filesystem encoding is set to mbcs rather
than utf-8, and the error mode is set to Undeprecate bytes paths on WindowsUsing bytes as paths on Windows is currently deprecated. We would announce that
this is no longer the case, and that paths when encoded as bytes should use
whatever is returned from Beta experimentTo assist with determining the impact of this change, we propose applying it to 3.6.0b1 provisionally with the intent being to make a final decision before 3.6.0b4. During the experiment period, decoding and encoding exception messages will be expanded to include a link to an active online discussion and encourage reporting of problems. If it is decided to revert the functionality for 3.6.0b4, the implementation
change would be to permanently enable the legacy mode flag, change the
environment variable to It is expected that if we cannot feasibly make the change for 3.6 due to compatibility concerns, it will not be possible to make the change at any later time in Python 3.x. Affected ModulesThis PEP implicitly includes all modules within the Python that either pass path
names to the operating system, or otherwise use As of 3.6.0a4, the following modules require modification:
The following modules use
The following native code uses one of the encoding or decoding functions, but do not require any modification:
Rejected AlternativesUse strict mbcs decodingThis is essentially the same as the proposed change, but instead of changing
This approach allows the use of new functionality that is only available as *W APIs and also detection of encoding/decoding errors. For example, rather than silently replacing Unicode characters with ‘?’, it would be possible to warn or fail the operation. Compared to the proposed fix, this could enable some new functionality but does not fix any of the problems described initially. New runtime errors may cause some problems to be more obvious and lead to fixes, provided library maintainers are interested in supporting Windows and adding a separate code path to treat filesystem paths as strings. Making the encoding mbcs without strict errors is equivalent to the legacy-mode switch being enabled by default. This is a possible course of action if there is significant breakage of actual code and a need to extend the deprecation period, but still a desire to have the simplifications to the CPython source. Make bytes paths an error on WindowsBy preventing the use of bytes paths on Windows completely we prevent users from hitting encoding issues. However, the motivation for this PEP is to increase the likelihood that code written on POSIX will also work correctly on Windows. This alternative would move the other direction and make such code completely incompatible. As this does not benefit users in any way, we reject it. Make bytes paths an error on all platformsBy deprecating and then disable the use of bytes paths on all platforms we prevent users from hitting encoding issues regardless of where the code was originally written. This would require a full deprecation cycle, as there are currently no warnings on platforms other than Windows. This is likely to be seen as a hostile action against Python developers in general, and as such is rejected at this time. Code that may breakThe following code patterns may break or see different behaviour as a result of this change. Each of these examples would have been fragile in code intended for cross-platform use. The suggested fixes demonstrate the most compatible way to handle path encoding issues across all platforms and across multiple Python versions. Note that all of these examples produce deprecation warnings on Python 3.3 and later. Not managing encodings across boundariesCode that does not manage encodings when crossing protocol boundaries may
currently be working by chance, but could encounter issues when either encoding
changes. Note that the source of >>> filename = open('filename_in_mbcs.txt', 'rb').read() >>> text = open(filename, 'r').read() To correct this code, the encoding of the bytes in >>> # Fix 1: Open file as text (default encoding) >>> filename = open('filename_in_mbcs.txt', 'r').read() >>> text = open(filename, 'r').read() >>> # Fix 2: Open file as text (explicit encoding) >>> filename = open('filename_in_mbcs.txt', 'r', encoding='mbcs').read() >>> text = open(filename, 'r').read() >>> # Fix 3: Explicitly decode the path >>> filename = open('filename_in_mbcs.txt', 'rb').read() >>> text = open(filename.decode('mbcs'), 'r').read() Where the creator of >>> some_object.filename = r'C:\Users\Steve\Documents\my_file.txt'.encode('mbcs') >>> filename = some_object.filename >>> type(filename) <class 'bytes'> >>> text = open(filename, 'r').read() To fix this code for best compatibility across operating systems and Python versions, the filename should be exposed as str: >>> # Fix 1: Expose as str >>> some_object.filename = r'C:\Users\Steve\Documents\my_file.txt' >>> filename = some_object.filename >>> type(filename) <class 'str'> >>> text = open(filename, 'r').read() Alternatively, the encoding used for the path needs to be made available to the
user. Specifying >>> # Fix 2: Use fsencode >>> some_object.filename = os.fsencode(r'C:\Users\Steve\Documents\my_file.txt') >>> filename = some_object.filename >>> type(filename) <class 'bytes'> >>> text = open(filename, 'r').read() >>> # Fix 3: Expose as explicit encoding >>> some_object.filename = r'C:\Users\Steve\Documents\my_file.txt'.encode('cp437') >>> some_object.filename_encoding = 'cp437' >>> filename = some_object.filename >>> type(filename) <class 'bytes'> >>> filename = filename.decode(some_object.filename_encoding) >>> type(filename) <class 'str'> >>> text = open(filename, 'r').read() Explicitly using ‘mbcs’Code that explicitly encodes text using ‘mbcs’ before passing to file system
APIs is now passing incorrectly encoded bytes. Note that the source of
>>> filename = open('files.txt', 'r').readline().rstrip() >>> text = open(filename.encode('mbcs'), 'r') To correct this code, the string should be passed without explicit encoding, or
should use >>> # Fix 1: Do not encode the string >>> filename = open('files.txt', 'r').readline().rstrip() >>> text = open(filename, 'r') >>> # Fix 2: Use correct encoding >>> filename = open('files.txt', 'r').readline().rstrip() >>> text = open(os.fsencode(filename), 'r') CopyrightThis document has been placed in the public domain. |
| You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |