summaryrefslogtreecommitdiff
path: root/docs/python3.rst
blob: 02593a263cc5d8f9e915beaee3b9bbf4b6e1c2ad (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
Python 3 Support
================

.. currentmodule:: click

Click supports Python 3 but like all other command line utility libraries,
it suffers from the Unicode text model in Python 3.  All examples in the
documentation were written so that they run on both Python 2.x and
Python 3.3 or higher.

At the moment the strong recommendation is to use Python 2 for these
utilities unless Python 3 is a hard requirement.

.. _python3-limitations:

Python 3 Limitations
--------------------

At the moment click suffers from a few problems on Python 3:

*   The command line in Unix traditionally is in bytes and not unicode.
    While there are encoding hits for all of this, there are generally
    some situations where this can break.  The most common one is SSH
    connections to machines with different locales.

    Misconfigured environments can currently cause a wide range of unicode
    problems on Python 3 due to the lack of support for roundtripping
    surrogate escapes.  This will be fixed on a case by case basis going
    forward.

*   Standard input and output on Python 3 is opened in unicode mode by
    default.  Click has to reopen the stream in binary mode in certain
    situations.  Because there is no standardized way to do this, this
    might not always work.  Primarily this can become a problem when
    testing command line applications.

    This is not supported::

        sys.stdin = io.StringIO('Input here')
        sys.stdout = io.StringIO()

    Instead you need to do this::

        input = 'Input here'
        in_stream = io.BytesIO(input.encode('utf-8'))
        sys.stdin = io.TextIOWrapper(in_stream, encoding='utf-8')
        out_stream = io.BytesIO()
        sys.stdout = io.TextIOWrapper(out_stream, encoding='utf-8')

    Remember that in that case you need to use ``out_stream.getvalue()``
    and not ``sys.stdout.getvalue()`` if you want to access the buffer
    contents as the wrapper will not forward that method.

Python 2 / 3 Differences
------------------------

Click attempts to minimize the differences between Python 2 and Python 3
by following the best practices for both languages.

On Python 2 the following is true:

*   ``sys.stdin``, ``sys.stdout``, and ``sys.stderr`` are opened in binary
    mode but under some circumstances they support unicode output.  Click
    attempts to not subvert this but provides support for forcing streams
    to be unicode-based.
*   ``sys.argv`` is always bytes-based.  Click will pass bytes to all
    input types and convert as necessary.  The :class:`STRING` type
    automatically will decode properly the input value into a string by
    trying the most appropriate encodings.
*   When dealing with files, click will never go through the unicode APIs
    and will instead use the operating system's byte APIs to open the
    files.

On Python 3 the following is true:

*   ``sys.stdin``, ``sys.stdout`` and ``sys.stderr`` are by default
    text-based.  When click needs a binary stream it attempts to discover
    the underlying binary stream.  See :ref:`python3-limitations` for how
    this works.
*   ``sys.argv`` is always unicode-based.  This also means that the native
    type for input values to the types in click is unicode and not bytes.

    This causes problems when the terminal is incorrectly set and Python
    does not figure out the encoding.  In that case the unicode string
    will contain error bytes encoded as surrogate escapes.
*   When dealing with files click will always use the unicode file system
    API calls by using the operating system's reported or guessed
    filesystem encoding.  Surrogates are supported for filenames so it
    should be possible to open files through the :class:`File` type even
    if the environment is misconfigured.