From 73d960ab47e2b9e25b0fc0177c11f92788665d43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Sat, 14 Apr 2018 10:43:57 +0200 Subject: [PATCH 01/34] Add open to index, lead to File I/O (#37) Merges https://github.com/fedora-python/portingguide/pull/37 --- source/strings.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/strings.rst b/source/strings.rst index 6b2c699..42e34ba 100644 --- a/source/strings.rst +++ b/source/strings.rst @@ -204,7 +204,7 @@ The recommended fixer will import ``six`` and replace any uses of ``basestring`` by ``string_types``. -.. index:: file I/O +.. index:: file I/O, open .. _str-file-io: From 22c4d841bc37d54920896a52b6213e836d4e9968 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Tue, 17 Jul 2018 00:54:42 +0200 Subject: [PATCH 02/34] Fix the tests to work with modernize 0.6.1 modernize 0.6 had: $ python -m modernize -l Available transformations for the -f/--fix option: lib2to3.fixes.fix_apply ... (+empty line at the end) modernize 0.6.1 has: $ python -m modernize -l Available transformations for the -f/--fix and -x/--nofix options: lib2to3.fixes.fix_apply (apply) ... --- test_portingdb/test_fixer_library.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test_portingdb/test_fixer_library.py b/test_portingdb/test_fixer_library.py index 9835d3d..d1014b7 100644 --- a/test_portingdb/test_fixer_library.py +++ b/test_portingdb/test_fixer_library.py @@ -20,7 +20,10 @@ def fixer_names(): # use subprocess -- modernize doesn't have public fixer list API proc = subprocess.run([sys.executable, '-m', 'modernize', '-l'], stdout=subprocess.PIPE, encoding='ascii', check=True) - lines = proc.stdout.splitlines() + + # In modernize 0.6.1, the output has changed to list additional information + # This takes the first part only and works with older modernize as well + lines = [l.strip().split()[0] for l in proc.stdout.splitlines() if l] # first line is a header, all others should be fixers assert not FIXER_RE.fullmatch(lines[0]) From 1b1d33110b6cc8354910d76e329e1ee4bcf3353b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Tue, 17 Jul 2018 10:19:33 +0200 Subject: [PATCH 03/34] Dicts are sorted by insertion order in 3.7+ https://mail.python.org/pipermail/python-dev/2017-December/151283.html --- source/dicts.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/dicts.rst b/source/dicts.rst index c9539b0..5c23cbe 100644 --- a/source/dicts.rst +++ b/source/dicts.rst @@ -101,8 +101,8 @@ In Python 3.3+, this setting is the default:: [('c', 3), ('e', 5), ('d', 4), ('a', 1), ('b', 2)] Additionally, CPython 3.6+ uses a new implementation of dictionaries, -which makes them appear sorted by insertion order (though you shouldn't rely -on this behavior):: +which makes them appear sorted by insertion order (though you can only rely +on this behavior in Python 3.7+):: $ python3 order.py [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5)] From f4925b47332fa8280e0562b7b96424867bcac01b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Tue, 17 Jul 2018 00:46:03 +0200 Subject: [PATCH 04/34] Remove a confusing underscore I have no idea why it is there, yet I had to think about it. --- source/syntax.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/syntax.rst b/source/syntax.rst index 75031fa..f4bf7a7 100644 --- a/source/syntax.rst +++ b/source/syntax.rst @@ -91,12 +91,12 @@ The recommended fixer does a good job, though it doesn't catch the case where the name ``repr`` is redefined, as in:: repr = None - print(`1+_2`) + print(`1+2`) which becomes:: repr = None - print(repr(1+_2)) + print(repr(1+2)) Re-defining built-in functions is usually considered bad style, but it never hurts to check if the code does it. From d83e695a22cd84dd24c61da8e6a2690c84cfe196 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Tue, 17 Jul 2018 10:17:42 +0200 Subject: [PATCH 05/34] Couple typos or typo-like changes --- source/numbers.rst | 4 ++-- source/strings.rst | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/numbers.rst b/source/numbers.rst index fd9b647..5ee3a61 100644 --- a/source/numbers.rst +++ b/source/numbers.rst @@ -27,7 +27,7 @@ In Python 2, dividing two integers resulted in an integer:: This *truncating division* was inherited from C-based languages, but confused people who don't know those languages, -such as those coming from Javascript or pure math. +such as those coming from JavaScript or pure math. In Python 3, dividing two integers results in a float:: @@ -37,7 +37,7 @@ In Python 3, dividing two integers results in a float:: The ``//`` operator, which was added all the way back in Python 2.2, always performs truncating division:: - while_minutes = seconds // 60 + whole_minutes = seconds // 60 The ``from __future__ import division`` directive causes the ``/`` operator to behave the same in Python 2 as it does in Python 3. diff --git a/source/strings.rst b/source/strings.rst index 42e34ba..6da5010 100644 --- a/source/strings.rst +++ b/source/strings.rst @@ -300,7 +300,7 @@ If your software handles multiple text encodings, or handles user-specified encodings, make sure this capability is well-tested. Under Linux, run your software with the ``LC_ALL`` environment variable -set to ``C`` and ``tr_TR.UTF-8``, and check handling of any command-line +set to ``C`` and ``tr_TR.utf8``, and check handling of any command-line arguments and environment variables that may contain non-ASCII characters. From 3c001231b8e7d84b23f2554b9eb9e25de4db3c74 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 20 Aug 2018 13:11:34 +0200 Subject: [PATCH 06/34] strings: Fix header levels The document structure was weird: specific Conversion sections were top-level instead of under "Conversion between text and bytes", and "File I/O" and "Testing Strings" were under "Type checking" instead of top-level. --- source/strings.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/strings.rst b/source/strings.rst index 6da5010..9d86dcf 100644 --- a/source/strings.rst +++ b/source/strings.rst @@ -120,7 +120,7 @@ Some common encodings are: Conversion to text ------------------- +.................. There is no built-in function that converts to text in both Python versions. The :ref:`six` library provides ``six.text_type``, which is fine if it appears @@ -134,7 +134,7 @@ following code at the beginning of a file:: Conversion to bytes -------------------- +................... There is no good function that converts an arbitrary object to bytes, as this operation does not make sense on arbitrary objects. @@ -209,7 +209,7 @@ The recommended fixer will import ``six`` and replace any uses of .. _str-file-io: File I/O -~~~~~~~~ +-------- * :ref:`Fixer `: ``python-modernize -wnf libmodernize.fixes.fix_open`` * Prevalence: Common @@ -262,7 +262,7 @@ We recommend adding them manually if the encoding is known. .. _testing-str: Testing Strings -~~~~~~~~~~~~~~~ +--------------- When everything is ported and tests are passing, it is a good idea to make sure your code handles strings correctly – even in unusual situations. From 4bec0d165e829db38de7c437706f005fab1d309a Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 20 Aug 2018 13:12:21 +0200 Subject: [PATCH 07/34] strings: Break out Encodings into its own section This makes it a bit discoverable and linkable. Also, add an example of referring to external standards. --- source/strings.rst | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/source/strings.rst b/source/strings.rst index 9d86dcf..aef57ec 100644 --- a/source/strings.rst +++ b/source/strings.rst @@ -97,13 +97,19 @@ trying all alternatives. Unlike images, one bytestring can often be successfully decoded using more than one encoding. -So, never assume an encoding without consulting relevant documentation + +Encodings +......... + +Never assume a text encoding without consulting relevant documentation and/or researching a string's use cases. If an encoding for a particular use case is determined, document it. -For example, a docstring can specify that some argument is “a bytestring -holding UTF-8-encoded text data”. +For example, a function docstring can specify that some argument is +“a bytestring holding UTF-8-encoded text data”, or module documentation may +clarify that „as per RFC 4514, LDAP attribute names are encoded to UTF-8 +for transmission“. -Some common encodings are: +Some common text encodings are: * ``UTF-8``: A widely used encoding that can encode any Unicode text, using one to four bytes per character. From 65f78e3f66ac3aa7645cbda561c7003f3edb3ae5 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 20 Aug 2018 13:12:59 +0200 Subject: [PATCH 08/34] strings: small fixes and clarifications --- source/strings.rst | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/source/strings.rst b/source/strings.rst index aef57ec..9ba283b 100644 --- a/source/strings.rst +++ b/source/strings.rst @@ -67,15 +67,15 @@ strings. For other data, you can use the native string in these circumstances: - * You are working with textual data - * Under Python 2, each “native string” value has a single well-defined - encoding (such as ``UTF-8`` or :func:`py2:locale.getpreferredencoding`) - * You do not mix native strings with either bytes or text – always - encode/decode diligently when converting to these types. +* You are working with textual data +* Under Python 2, each “native string” value has a single well-defined + encoding (e.g. ``UTF-8`` or :func:`py2:locale.getpreferredencoding`) +* You do not mix native strings with either bytes or text – always + encode/decode diligently when converting to these types. Native strings affect the semantics under Python 2 as little as possible, while not requiring the resulting Python 3 API to feel bad. But, having -a third incompatible type makes porting process harder. +a third incompatible type makes the porting process harder. Native strings are suitable mostly for conservative projects, where ensuring stability under Python 2 justifies extra porting effort. @@ -124,6 +124,11 @@ Some common text encodings are: * ``locale.getpreferredencoding()``: The “preferred encoding” for command-line arguments, environment variables, and terminal input/output. +If *you* are choosing an encoding to use – for example, your application +*defines* a file format rather than using a format standardized externally – +consider ``UTF-8``. +And whatever your choice is, explicitly document it. + Conversion to text .................. @@ -176,7 +181,7 @@ For example, these are all illegal:: import re pattern = re.compile(b'a+') - pattern.patch('aaaaaa') + pattern.match('aaaaaa') Type checking @@ -197,7 +202,7 @@ In Python 3, the concept of ``basestring`` makes no sense: text is only represented by ``str``. For type-checking text strings in code compatible with both versions, the -:ref:`six` library offers ``string_types``, which is ``(basestring,)`` +:ref:`six ` library offers ``string_types``, which is ``(basestring,)`` in Python 2 and ``(str,)`` in Python 3. The above code can be replaced by:: @@ -306,7 +311,7 @@ If your software handles multiple text encodings, or handles user-specified encodings, make sure this capability is well-tested. Under Linux, run your software with the ``LC_ALL`` environment variable -set to ``C`` and ``tr_TR.utf8``, and check handling of any command-line +set to ``C`` and to ``tr_TR.utf8``. Check handling of any command-line arguments and environment variables that may contain non-ASCII characters. From cd0d78fbcd2d5a0b80364f546b3a79b5069ad5f0 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 20 Aug 2018 15:23:08 +0200 Subject: [PATCH 09/34] Use `six library ` when linking to the Six section. - `six library` is the link text. - `` specifies the link target. --- source/strings.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/strings.rst b/source/strings.rst index 9ba283b..af7f916 100644 --- a/source/strings.rst +++ b/source/strings.rst @@ -21,8 +21,8 @@ In Python 2, the ``str`` type was used for two different kinds of values – In code, we will refer to this type as ``unicode`` – a short, unambiguous name, although one that is not built-in in Python 3. - Some projects refer to it as ``six.text_type`` (from the :ref:`six` - library). + Some projects refer to it as ``six.text_type`` + (from the :ref:`six library `). * @@ -134,8 +134,8 @@ Conversion to text .................. There is no built-in function that converts to text in both Python versions. -The :ref:`six` library provides ``six.text_type``, which is fine if it appears -once or twice in uncomplicated code. +The :ref:`six library ` provides ``six.text_type``, which is fine if it +appears once or twice in uncomplicated code. For better readability, we recommend using ``unicode``, which is unambiguous and clear, but it needs to be introduced with the following code at the beginning of a file:: @@ -202,7 +202,7 @@ In Python 3, the concept of ``basestring`` makes no sense: text is only represented by ``str``. For type-checking text strings in code compatible with both versions, the -:ref:`six ` library offers ``string_types``, which is ``(basestring,)`` +:ref:`six library ` offers ``string_types``, which is ``(basestring,)`` in Python 2 and ``(str,)`` in Python 3. The above code can be replaced by:: From 8c1debe9eaa0c99ed55230bce14f3bb30c3594f7 Mon Sep 17 00:00:00 2001 From: Jeff Trull Date: Mon, 28 Jan 2019 17:40:08 -0800 Subject: [PATCH 10/34] Correct name of future feature Name seems to be "absolute_import" not "absolute_imports". This tripped me up. --- source/imports.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/imports.rst b/source/imports.rst index 0ba5b26..02517ea 100644 --- a/source/imports.rst +++ b/source/imports.rst @@ -12,7 +12,7 @@ Absolute imports * :ref:`Fixer `: ``python-modernize -wnf libmodernize.fixes.fix_import`` (See caveat below) * Prevalence: Common -* Future import: ``from __future__ import absolute_imports`` +* Future import: ``from __future__ import absolute_import`` * Specification: `PEP 328 `_ Under Python 2, when importing from inside a package, the package's own modules @@ -43,7 +43,7 @@ Given the structure above, these statements would be equivalent Additionally, a *future import* was added to make all imports absolute (unless explicitly relative):: - from __future__ import absolute_imports + from __future__ import absolute_import Using this feature, ``from collections import deque`` will import from the standard library's ``collections`` module. From 1941760230573efb80d348c8cac844f589230459 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Tue, 29 Jan 2019 09:18:58 +0100 Subject: [PATCH 11/34] Rename the tests folder to match this project instead of portingdb --- {test_portingdb => test_portingguide}/test_fixer_library.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {test_portingdb => test_portingguide}/test_fixer_library.py (100%) diff --git a/test_portingdb/test_fixer_library.py b/test_portingguide/test_fixer_library.py similarity index 100% rename from test_portingdb/test_fixer_library.py rename to test_portingguide/test_fixer_library.py From 61f2e43696c5d5dd6cec145a1637136edce59c6f Mon Sep 17 00:00:00 2001 From: cclauss Date: Fri, 25 Jan 2019 12:18:03 +0100 Subject: [PATCH 12/34] Use feature detection instead of version detection Follow the Python porting best practice [___use feature detection instead of version detection___](https://docs.python.org/3/howto/pyporting.html#use-feature-detection-instead-of-version-detection). --- source/builtins.rst | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/source/builtins.rst b/source/builtins.rst index 5ee7075..1e0c2e4 100644 --- a/source/builtins.rst +++ b/source/builtins.rst @@ -105,12 +105,11 @@ aren't of the ``file`` type. If type-checking for files is necessary, we recommend using a tuple of types that includes :class:`io.IOBase` and, under Python 2, ``file``:: - import six import io - if six.PY2: + try: file_types = file, io.IOBase - else: + except NameError: file_types = (io.IOBase,) ... @@ -263,9 +262,9 @@ Python 2.6 and below didn't have an ``importlib`` module. If your code uses ``reload()``, import it conditionally on Python 3:: - import six - - if not six.PY2: + try: + reload + except NameError: from importlib import reload @@ -283,9 +282,9 @@ In Python 3, it is moved to the ``sys`` module. If your code uses ``intern()``, import it conditionally on Python 3:: - import six - - if not six.PY2: + try: + intern + except NameError: from sys import intern From 0e8b64a924b6c62b64f351370a7f51c0eb4a83ea Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 29 Jan 2019 12:49:43 +0100 Subject: [PATCH 13/34] Adjust wording and add one more case of feature detection --- source/builtins.rst | 16 ++++++++++++++-- source/strings.rst | 5 ++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/source/builtins.rst b/source/builtins.rst index 1e0c2e4..7ddab60 100644 --- a/source/builtins.rst +++ b/source/builtins.rst @@ -108,8 +108,10 @@ that includes :class:`io.IOBase` and, under Python 2, ``file``:: import io try: + # Python 2: "file" is built-in file_types = file, io.IOBase except NameError: + # Python 3: "file" fully replased with IOBase file_types = (io.IOBase,) ... @@ -260,9 +262,11 @@ In Python 3, it is moved to the ``importlib`` module. Python 2.7 included an ``importlib`` module, but without a ``reload`` function. Python 2.6 and below didn't have an ``importlib`` module. -If your code uses ``reload()``, import it conditionally on Python 3:: +If your code uses ``reload()``, import it conditionally if it doesn't exist +(using `feature detection`_):: try: + # Python 2: "reload" is built-in reload except NameError: from importlib import reload @@ -280,9 +284,11 @@ Moved ``intern()`` The :func:`~sys.intern` function was built-in in Python 2. In Python 3, it is moved to the ``sys`` module. -If your code uses ``intern()``, import it conditionally on Python 3:: +If your code uses ``intern()``, import it conditionally if it doesn't exist +(using `feature detection`_):: try: + # Python 2: "intern" is built-in intern except NameError: from sys import intern @@ -306,3 +312,9 @@ If any of your classes defines the special method ``__coerce__``, remove that as well, and test that the removal did not break semantics. .. XXX: I've never seen serious use of ``coerce``, so the advice is limited. + + +.. Common links + ------------ + +.. _feature detection: https://docs.python.org/3/howto/pyporting.html#use-feature-detection-instead-of-version-detection diff --git a/source/strings.rst b/source/strings.rst index af7f916..1bbd1e6 100644 --- a/source/strings.rst +++ b/source/strings.rst @@ -140,7 +140,10 @@ For better readability, we recommend using ``unicode``, which is unambiguous and clear, but it needs to be introduced with the following code at the beginning of a file:: - if not six.PY2: + try: + # Python 2: "unicode" is built-in + unicode + except NameError: unicode = str From 228b47314b8345d2543eae09592070fd22915f91 Mon Sep 17 00:00:00 2001 From: Steffen Allner Date: Mon, 4 Mar 2019 17:45:02 +0100 Subject: [PATCH 14/34] Fix the name of the fixer. --- source/dicts.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/dicts.rst b/source/dicts.rst index 5c23cbe..50c5450 100644 --- a/source/dicts.rst +++ b/source/dicts.rst @@ -10,7 +10,7 @@ There are three most significant changes related to dictionaries in Python 3. Removed ``dict.has_key()`` ~~~~~~~~~~~~~~~~~~~~~~~~~~ -* :ref:`Fixer `: ``python-modernize -wnf lib2to3.fixes.has_key`` (See caveat below) +* :ref:`Fixer `: ``python-modernize -wnf lib2to3.fixes.fix_has_key`` (See caveat below) * Prevalence: Common The ``dict.has_key()`` method, long deprecated in favor of the ``in`` operator, From ed46a5ac5796bedee41c2330c15b08482277f9d1 Mon Sep 17 00:00:00 2001 From: Steffen Allner Date: Mon, 4 Mar 2019 22:54:47 +0100 Subject: [PATCH 15/34] Fix tests. First, modernize was only imported, but not used. The package changed the importing mechanism. Second, fix the actual test in that it should now find more typos in fixer names. --- test_portingguide/test_fixer_library.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test_portingguide/test_fixer_library.py b/test_portingguide/test_fixer_library.py index d1014b7..f012db7 100644 --- a/test_portingguide/test_fixer_library.py +++ b/test_portingguide/test_fixer_library.py @@ -4,14 +4,14 @@ import sys import pytest -import modernize basepath = Path(__file__).parent.parent / 'source' all_rst_files = [str(p.relative_to(basepath)) for p in basepath.glob('**/*.rst')] -# Fixer names are in the format ".fixes.fix_" -FIXER_RE = re.compile(r'\w+\.fixes\.fix_\w+') +# Fixer names are in the format ".fixes.fix_" but we test also +# for typos like ".fixes.ifx_". +FIXER_RE = re.compile(r'\w+\.fixes\.\w+') @pytest.fixture(scope='module') From 0285d4b15044e899db046191293c2a4af6b4b93a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Tue, 29 Jan 2019 12:29:22 +0100 Subject: [PATCH 16/34] Test future imports See also https://github.com/fedora-python/portingguide/pull/48 This tests the imports on Python 3, not Python 2, but at least the imports we care about should exit cleanly on Python 3. --- test_portingguide/test_future_imports.py | 30 ++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 test_portingguide/test_future_imports.py diff --git a/test_portingguide/test_future_imports.py b/test_portingguide/test_future_imports.py new file mode 100644 index 0000000..14bd1dd --- /dev/null +++ b/test_portingguide/test_future_imports.py @@ -0,0 +1,30 @@ +from pathlib import Path +import subprocess +import re +import sys + +import pytest + +basepath = Path(__file__).parent.parent / 'source' +all_rst_files = [str(p.relative_to(basepath)) + for p in basepath.glob('**/*.rst')] + +# All future imports mentioned +FUTURE_RE = re.compile(r'from __future__ import [^\s`]+') + + +def try_import(line): + cp = subprocess.run([sys.executable, '-c', line]) + return cp.returncode + + +@pytest.mark.parametrize('filename', all_rst_files) +def test_future_imports_work(filename): + """Test that all mentioned future imports work""" + path = basepath / filename + with path.open() as f: + for lineno, line in enumerate(f, start=1): + for match in FUTURE_RE.finditer(line): + found_text = match.group(0) + print(f'{filename}:{lineno}:{found_text}') + assert try_import(found_text) == 0 From eb27fdc0a1daeb34249f45aef6781e6897ef872e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Walter=20D=C3=B6rwald?= Date: Tue, 7 May 2019 18:43:12 +0200 Subject: [PATCH 17/34] Fix typo in comprehensions.rst (#54) Merges https://github.com/fedora-python/portingguide/pull/54 --- source/comprehensions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/comprehensions.rst b/source/comprehensions.rst index 23fb8fe..4d1e01b 100644 --- a/source/comprehensions.rst +++ b/source/comprehensions.rst @@ -1,7 +1,7 @@ Comprehensions -------------- -List comprehensions, a shrtcut for creating lists, have been in Python +List comprehensions, a shortcut for creating lists, have been in Python since version 2.0. Python 2.4 added a similar feature – generator expressions; then 2.7 (and 3.0) introduced set and dict comprehensions. From 40b2e4acac92ac183c7a969162b9e7144631fdd0 Mon Sep 17 00:00:00 2001 From: Paul Crowley Date: Fri, 24 May 2019 23:38:44 -0700 Subject: [PATCH 18/34] Fix repetition: s/for i in for i in/for i in / Merges https://github.com/fedora-python/portingguide/pull/56 --- source/comprehensions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/comprehensions.rst b/source/comprehensions.rst index 4d1e01b..823a49f 100644 --- a/source/comprehensions.rst +++ b/source/comprehensions.rst @@ -47,7 +47,7 @@ To fix this, either rewrite the code to not use the iteration variable after a list comprehension, or convert the comprehension to a ``for`` loop:: powers = [] - for i in for i in range(10): + for i in range(10): powers.append(2**i) In some cases, the change might silently cause different behavior. From 4bee508e3bf6404cf8e1b948d9fa15dc77c24110 Mon Sep 17 00:00:00 2001 From: Paul Crowley Date: Sat, 25 May 2019 05:10:29 -0700 Subject: [PATCH 19/34] imports: s/importmentation/implementation/ --- source/imports.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/imports.rst b/source/imports.rst index 02517ea..91a80db 100644 --- a/source/imports.rst +++ b/source/imports.rst @@ -103,7 +103,7 @@ Import Cycles * :ref:`Fixer `: None * Prevalence: Rare -Python 3 introduced a reworked importmentation of ``import`` in the form +Python 3 introduced a reworked implementation of ``import`` in the form of the :py:mod:`importlib` module. The new machinery is backwards-compatible in practice, except that some import cycles, especially those involving submodules, now raise From 8ed00524f943208246a622d82e88b9a49aec6710 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 8 Jan 2020 17:49:12 +0100 Subject: [PATCH 20/34] Discuss the `ur` string prefix Also reword other parts of the strings section Fixes: https://github.com/fedora-python/portingguide/issues/51 --- source/strings.rst | 58 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/source/strings.rst b/source/strings.rst index 1bbd1e6..f9ed149 100644 --- a/source/strings.rst +++ b/source/strings.rst @@ -14,7 +14,7 @@ In Python 2, the ``str`` type was used for two different kinds of values – **Text** contains human-readable messages, represented as a sequence of Unicode codepoints. - Usually, it does not contain unprintable control characters such as NULL. + Usually, it does not contain unprintable control characters such as ``\0``. This type is available as ``str`` in Python 3, and ``unicode`` in Python 2. @@ -58,19 +58,20 @@ can use what is conceptually a third type: The **native string** (``str``) – text in Python 3, bytes in Python 2 -Custom ``__str__`` and ``__repr__`` methods, and code that deals with +Custom ``__str__`` and ``__repr__`` methods and code that deals with Python language objects (such as attribute/function names) will always need to use the native string, because that is what each version of Python uses for internal text-like data. Developer-oriented texts, such as exception messages, could also be native strings. -For other data, you can use the native string in these circumstances: +For other data, you should only use the native string if all of the following +hold: -* You are working with textual data +* you are working with textual data, * Under Python 2, each “native string” value has a single well-defined - encoding (e.g. ``UTF-8`` or :func:`py2:locale.getpreferredencoding`) -* You do not mix native strings with either bytes or text – always + encoding (e.g. ``UTF-8`` or :func:`py2:locale.getpreferredencoding`), and +* you do not mix native strings with either bytes or text – always encode/decode diligently when converting to these types. Native strings affect the semantics under Python 2 as little as possible, @@ -161,13 +162,56 @@ Depending on what you need, explicitly use a serialization function String Literals --------------- +* :ref:`Fixer `: None +* Prevalence: Very common + Quoted string literals can be prefixed with ``b`` or ``u`` to get bytes or text, respectively. These prefixes work both in Python 2 (2.6+) and 3 (3.3+). Literals without these prefixes result in native strings. +In Python 3, the ``u`` prefix does nothing; it is only allowed for backwards +compatibility. +Likewise, the ``b`` prefix does nothing in Python 2. + Add a ``b`` or ``u`` prefix to all strings, unless a native string is desired. +Unfortunately, the choice between text and bytes cannot generally be automated. + +Raw Unicode strings +................... + +* :ref:`Fixer `: None +* Prevalence: Rare + +In Python 2, the ``r`` prefix could be combined with ``u`` to avoid processing +backslash escapes. +However, this *did not* turn off processing Unicode escapes (``\u....`` or +``\U........``), as the ``u`` prefix took precedence over ``r``:: + + >>> print u"\x23☺\u2744" # Python 2 with Encoding: UTF-8 + #☺❄ + >>> print ur"\x23☺\u2744" # Python 2 with Encoding: UTF-8 + \x23☺❄ + +This may be confusing at first. +Keeping this would be even more confusing in Python 3, where the ``u`` prefix +is a no-op backwards-compatible behavior. +Python 3 avoids the choice between confusing or backwards-incompatible +semantics by forbidding ``ru`` altogether. + +Avoid the ``ur`` prefixed in string literals. + +A general process is to: + +* change all non-ASCII characters to Unicode escapes, +* use ``br`` instead of ``ur`` to get a byte string, and +* decode Unicode escapes using the ``'raw_unicode_escape'`` codec:: + + >>> print(br"\x23\u263a\u2744".decode('raw_unicode_escape')) + \x23☺❄ + +Usually, the literals are simpler so the change can be more straightforward. .. index:: TypeError; mixing text and bytes @@ -186,6 +230,8 @@ For example, these are all illegal:: pattern = re.compile(b'a+') pattern.match('aaaaaa') +Encode or decode the data to make the types match. + Type checking ------------- From 347db1d9bef8f5e8c9bec4c9dd3e80b78a68f8c4 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 8 Jan 2020 16:43:58 +0100 Subject: [PATCH 21/34] Fix switch-up in bound/unbound method descrioptions Fixes: https://github.com/fedora-python/portingguide/issues/57 Thanks Austin Chang for reporting this. --- source/core-obj-misc.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/core-obj-misc.rst b/source/core-obj-misc.rst index 41892b2..b56711c 100644 --- a/source/core-obj-misc.rst +++ b/source/core-obj-misc.rst @@ -194,8 +194,8 @@ Do this change in all classes that implement ``__nonzero__``. Unbound Methods ~~~~~~~~~~~~~~~ -Python 2 had two kinds of methods: *bound* methods, which you could retreive -from a class object, and *unbound* methods, which were retreived from +Python 2 had two kinds of methods: *unbound* methods, which you could retreive +from a class object, and *bound* methods, which were retreived from an instance:: >>> class Hello(object): From e528e505d63b15e7dae95435131504e1c8838b72 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 9 Jan 2020 11:19:44 +0100 Subject: [PATCH 22/34] Apply fixes from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Miro Hrončok --- source/strings.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/strings.rst b/source/strings.rst index f9ed149..1ded19c 100644 --- a/source/strings.rst +++ b/source/strings.rst @@ -198,9 +198,9 @@ This may be confusing at first. Keeping this would be even more confusing in Python 3, where the ``u`` prefix is a no-op backwards-compatible behavior. Python 3 avoids the choice between confusing or backwards-incompatible -semantics by forbidding ``ru`` altogether. +semantics by forbidding ``ur`` altogether. -Avoid the ``ur`` prefixed in string literals. +Avoid the ``ur`` prefix in string literals. A general process is to: From b7744b94ec4582bc8a84befb71b39341ec11443b Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 9 Jan 2020 11:45:58 +0100 Subject: [PATCH 23/34] Double the backslash to port ``ur``-prefixed literals. --- source/strings.rst | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/source/strings.rst b/source/strings.rst index 1ded19c..cd23a34 100644 --- a/source/strings.rst +++ b/source/strings.rst @@ -202,16 +202,11 @@ semantics by forbidding ``ur`` altogether. Avoid the ``ur`` prefix in string literals. -A general process is to: +The most straightforward way to do this is to use plain ``u`` literals +with ``\\`` for a literal backslash:: -* change all non-ASCII characters to Unicode escapes, -* use ``br`` instead of ``ur`` to get a byte string, and -* decode Unicode escapes using the ``'raw_unicode_escape'`` codec:: - - >>> print(br"\x23\u263a\u2744".decode('raw_unicode_escape')) - \x23☺❄ - -Usually, the literals are simpler so the change can be more straightforward. + >>> print(u"\\x23\u263a\u2744") + \x23☺❄ .. index:: TypeError; mixing text and bytes From 7e43091254c851faba994f783d14d9d1177b5de8 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 9 Jan 2020 12:40:11 +0100 Subject: [PATCH 24/34] Add one more missing word --- source/strings.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/strings.rst b/source/strings.rst index cd23a34..f795513 100644 --- a/source/strings.rst +++ b/source/strings.rst @@ -196,7 +196,7 @@ However, this *did not* turn off processing Unicode escapes (``\u....`` or This may be confusing at first. Keeping this would be even more confusing in Python 3, where the ``u`` prefix -is a no-op backwards-compatible behavior. +is a no-op with backwards-compatible behavior. Python 3 avoids the choice between confusing or backwards-incompatible semantics by forbidding ``ur`` altogether. From e0d0b53cb44f205f78afed3a1bf540fa5510a41a Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 9 Jan 2020 12:41:14 +0100 Subject: [PATCH 25/34] =?UTF-8?q?Keep=20literal=20=E2=98=BA=20in=20code=20?= =?UTF-8?q?example?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/strings.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/strings.rst b/source/strings.rst index f795513..8bc3bdb 100644 --- a/source/strings.rst +++ b/source/strings.rst @@ -205,7 +205,7 @@ Avoid the ``ur`` prefix in string literals. The most straightforward way to do this is to use plain ``u`` literals with ``\\`` for a literal backslash:: - >>> print(u"\\x23\u263a\u2744") + >>> print(u"\\x23☺\u2744") \x23☺❄ From 8e1812c753696f0805fc1d38a431555a1b19ad18 Mon Sep 17 00:00:00 2001 From: Steffen Pankratz Date: Tue, 21 Apr 2020 16:38:19 +0200 Subject: [PATCH 26/34] Update builtins.rst --- source/builtins.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/builtins.rst b/source/builtins.rst index 7ddab60..1be8f41 100644 --- a/source/builtins.rst +++ b/source/builtins.rst @@ -111,7 +111,7 @@ that includes :class:`io.IOBase` and, under Python 2, ``file``:: # Python 2: "file" is built-in file_types = file, io.IOBase except NameError: - # Python 3: "file" fully replased with IOBase + # Python 3: "file" fully replaced with IOBase file_types = (io.IOBase,) ... From df7c4571a052cf3ab8a65f04b0bc6efae1ef14c5 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 22 Jun 2020 10:12:00 +0200 Subject: [PATCH 27/34] Add a licence The relevant contributors agreed to this in: https://github.com/fedora-python/portingguide/issues/62 --- LICENCE.CC-BY-SA | 372 +++++++++++++++++++++++++++++++++++++++++++++++ LICENCE.CC0 | 121 +++++++++++++++ README | 16 ++ 3 files changed, 509 insertions(+) create mode 100644 LICENCE.CC-BY-SA create mode 100644 LICENCE.CC0 diff --git a/LICENCE.CC-BY-SA b/LICENCE.CC-BY-SA new file mode 100644 index 0000000..2b56468 --- /dev/null +++ b/LICENCE.CC-BY-SA @@ -0,0 +1,372 @@ +Creative Commons Attribution-ShareAlike 4.0 International Public +License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution-ShareAlike 4.0 International Public License ("Public +License"). To the extent this Public License may be interpreted as a +contract, You are granted the Licensed Rights in consideration of Your +acceptance of these terms and conditions, and the Licensor grants You +such rights in consideration of benefits the Licensor receives from +making the Licensed Material available under these terms and +conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. BY-SA Compatible License means a license listed at + creativecommons.org/compatiblelicenses, approved by Creative + Commons as essentially the equivalent of this Public License. + + d. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + e. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + f. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + g. License Elements means the license attributes listed in the name + of a Creative Commons Public License. The License Elements of this + Public License are Attribution and ShareAlike. + + h. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + i. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + j. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + k. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + l. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + m. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. Additional offer from the Licensor -- Adapted Material. + Every recipient of Adapted Material from You + automatically receives an offer from the Licensor to + exercise the Licensed Rights in the Adapted Material + under the conditions of the Adapter's License You apply. + + c. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + b. ShareAlike. + + In addition to the conditions in Section 3(a), if You Share + Adapted Material You produce, the following conditions also apply. + + 1. The Adapter's License You apply must be a Creative Commons + license with the same License Elements, this version or + later, or a BY-SA Compatible License. + + 2. You must include the text of, or the URI or hyperlink to, the + Adapter's License You apply. You may satisfy this condition + in any reasonable manner based on the medium, means, and + context in which You Share Adapted Material. + + 3. You may not offer or impose any additional or different terms + or conditions on, or apply any Effective Technological + Measures to, Adapted Material that restrict exercise of the + rights granted under the Adapter's License You apply. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material, + + including for purposes of Section 3(b); and + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.” The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. + diff --git a/LICENCE.CC0 b/LICENCE.CC0 new file mode 100644 index 0000000..0e259d4 --- /dev/null +++ b/LICENCE.CC0 @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/README b/README index 3678fde..b5be14a 100644 --- a/README +++ b/README @@ -28,3 +28,19 @@ We have a few automatic tests. To run them, install dependencies: ... and run: $ python -m pytest -v + + +Licence +------- + +This guide is released under the [CC BY-SA 4.0] licence. + +Additionally, all code in this repository (including code samples in the text, +tests, build scripts) is dedicated to the public domain under the +[CC0 1.0] dedication. + +[CC BY-SA 4.0]: https://creativecommons.org/licenses/by-sa/4.0/ +[CC0 1.0]: https://creativecommons.org/publicdomain/zero/1.0/ + +See the files `LICENCE.CC-BY-SA` and `LICENCE.CC0`, respectively, for the +license text. From 82c9007d87fecb0becf8276ebd3a13e27a123a87 Mon Sep 17 00:00:00 2001 From: Jeff Widman Date: Mon, 18 Jan 2021 12:53:59 -0800 Subject: [PATCH 28/34] Be less entitled "complaining" implies a sense of entitlement... Many of these 3p libs are open source libs maintained by volunteers, so let's bit a little more polite and "notify" them instead. https://github.com/fedora-python/portingguide/pull/64 --- source/dicts.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/dicts.rst b/source/dicts.rst index 50c5450..5d5e256 100644 --- a/source/dicts.rst +++ b/source/dicts.rst @@ -29,7 +29,7 @@ it does not check that its object is actually a dictionary. If you use a third-party dict-like class, it should implement ``in`` already. -If not, complain to its author: it should have been added as part of adding +If not, notify its author: it should have been added as part of adding Python 3 support. If your own codebase contains a custom dict-like class, add From b2a1ed0011c37e602c58e21276c45d4f7fbf6ff9 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 3 Sep 2021 14:28:55 +0200 Subject: [PATCH 29/34] =?UTF-8?q?Fix=20fixer=20names:=20lib2to3=20?= =?UTF-8?q?=E2=86=92=20fissix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/builtins.rst | 6 +++--- source/comprehensions.rst | 2 +- source/core-obj-misc.rst | 2 +- source/dicts.rst | 2 +- source/exceptions.rst | 6 +++--- source/numbers.rst | 6 +++--- source/syntax.rst | 6 +++--- source/tools.rst | 13 +++++++++---- 8 files changed, 24 insertions(+), 19 deletions(-) diff --git a/source/builtins.rst b/source/builtins.rst index 1be8f41..4145bb4 100644 --- a/source/builtins.rst +++ b/source/builtins.rst @@ -124,7 +124,7 @@ that includes :class:`io.IOBase` and, under Python 2, ``file``:: Removed ``apply()`` ~~~~~~~~~~~~~~~~~~~ -* :ref:`Fixer `: ``python-modernize -wnf lib2to3.fixes.fix_apply`` (but see below) +* :ref:`Fixer `: ``python-modernize -wnf fissix.fixes.fix_apply`` (but see below) * Prevalence: Common In Python 2, the function :func:`apply` was built in. @@ -152,7 +152,7 @@ in some of your modules, revert the fixer's changes in that module. Moved ``reduce()`` ~~~~~~~~~~~~~~~~~~ -* :ref:`Fixer `: ``python-modernize -wnf lib2to3.fixes.fix_reduce`` +* :ref:`Fixer `: ``python-modernize -wnf fissix.fixes.fix_reduce`` * Prevalence: Uncommon In Python 2, the function :func:`reduce` was built in. @@ -175,7 +175,7 @@ The recommended fixer will add this import automatically. The ``exec()`` function ~~~~~~~~~~~~~~~~~~~~~~~ -* :ref:`Fixer `: ``python-modernize -wnf lib2to3.fixes.fix_exec`` +* :ref:`Fixer `: ``python-modernize -wnf fissix.fixes.fix_exec`` * Prevalence: Rare In Python 2, :func:`exec` was a statement. In Python 3, it is a function. diff --git a/source/comprehensions.rst b/source/comprehensions.rst index 823a49f..4a69677 100644 --- a/source/comprehensions.rst +++ b/source/comprehensions.rst @@ -75,7 +75,7 @@ Unfortunately, you will need to find and fix these cases manually. Comprehensions over Tuples ~~~~~~~~~~~~~~~~~~~~~~~~~~ -* :ref:`Fixer `: ``python-modernize -wnf lib2to3.fixes.fix_paren`` +* :ref:`Fixer `: ``python-modernize -wnf fissix.fixes.fix_paren`` * Prevalence: Rare Python 2 allowed list comprehensions over bare, non-parenthesized tuples: diff --git a/source/core-obj-misc.rst b/source/core-obj-misc.rst index b56711c..97e1f2c 100644 --- a/source/core-obj-misc.rst +++ b/source/core-obj-misc.rst @@ -17,7 +17,7 @@ classes. Function Attributes ~~~~~~~~~~~~~~~~~~~ -* :ref:`Fixer `: ``python-modernize -wnf lib2to3.fixes.fix_funcattrs`` (but see below) +* :ref:`Fixer `: ``python-modernize -wnf fissix.fixes.fix_funcattrs`` (but see below) * Prevalence: Rare In Python, functions are mutable objects that support custom attributes. diff --git a/source/dicts.rst b/source/dicts.rst index 5d5e256..1d1d3de 100644 --- a/source/dicts.rst +++ b/source/dicts.rst @@ -10,7 +10,7 @@ There are three most significant changes related to dictionaries in Python 3. Removed ``dict.has_key()`` ~~~~~~~~~~~~~~~~~~~~~~~~~~ -* :ref:`Fixer `: ``python-modernize -wnf lib2to3.fixes.fix_has_key`` (See caveat below) +* :ref:`Fixer `: ``python-modernize -wnf fissix.fixes.fix_has_key`` (See caveat below) * Prevalence: Common The ``dict.has_key()`` method, long deprecated in favor of the ``in`` operator, diff --git a/source/exceptions.rst b/source/exceptions.rst index 5694228..850aaa9 100644 --- a/source/exceptions.rst +++ b/source/exceptions.rst @@ -26,7 +26,7 @@ supporting Python 3. The new ``except`` syntax ~~~~~~~~~~~~~~~~~~~~~~~~~ -* :ref:`Fixer `: ``python-modernize -wnf lib2to3.fixes.fix_except`` +* :ref:`Fixer `: ``python-modernize -wnf fissix.fixes.fix_except`` * Prevalence: Very common In Python 2, the syntax for catching exceptions was @@ -137,7 +137,7 @@ to the exception to use it outside the ``except`` clause. Iterating Exceptions ~~~~~~~~~~~~~~~~~~~~ -* :ref:`Fixer `: ``python-modernize -wnf lib2to3.fixes.fix_except`` (but see caveat below) +* :ref:`Fixer `: ``python-modernize -wnf fissix.fixes.fix_except`` (but see caveat below) * Prevalence: Rare In Python 2, exceptions were *iterable*, so it was possible to “unpack” the @@ -196,7 +196,7 @@ Otherwise, switch to using a dedicated Exception class. The Removed ``StandardError`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -* :ref:`Fixer `: ``python-modernize -wnf lib2to3.fixes.fix_standarderror`` (but see caveat below) +* :ref:`Fixer `: ``python-modernize -wnf fissix.fixes.fix_standarderror`` (but see caveat below) * Prevalence: Rare The :class:`py2:StandardError` class is removed in Python 3. diff --git a/source/numbers.rst b/source/numbers.rst index 5ee3a61..3370884 100644 --- a/source/numbers.rst +++ b/source/numbers.rst @@ -100,7 +100,7 @@ This change has several consequences. Removal of the ``long`` type ............................ -* :ref:`Fixer `: ``python-modernize -wnf lib2to3.fixes.fix_long`` +* :ref:`Fixer `: ``python-modernize -wnf fissix.fixes.fix_long`` * Prevalence: Common The ``long`` builtin no longer exists. @@ -130,7 +130,7 @@ The recommended fixer will do this. The ``L`` suffix not allowed in numeric literals ................................................ -* :ref:`Fixer `: ``python-modernize -wnf lib2to3.fixes.fix_numliterals`` (but see below) +* :ref:`Fixer `: ``python-modernize -wnf fissix.fixes.fix_numliterals`` (but see below) * Prevalence: Very common In Python 2, ``12345L`` designated a ``long`` literal. @@ -175,7 +175,7 @@ Call ``str()`` instead of ``repr()`` when the result might be a (long) integer. Octal Literals ~~~~~~~~~~~~~~ -* :ref:`Fixer `: ``python-modernize -wnf lib2to3.fixes.fix_numliterals`` (but see below) +* :ref:`Fixer `: ``python-modernize -wnf fissix.fixes.fix_numliterals`` (but see below) * Prevalence: Uncommon Python 2's other holdover from C-based languages is the syntax of octal diff --git a/source/syntax.rst b/source/syntax.rst index f4bf7a7..463445f 100644 --- a/source/syntax.rst +++ b/source/syntax.rst @@ -39,7 +39,7 @@ You can use the following Bash command for this:: Tuple Unpacking in Parameter Lists ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -* :ref:`Fixer `: ``python-modernize -wnf lib2to3.fixes.fix_tuple_params`` (fixup needed) +* :ref:`Fixer `: ``python-modernize -wnf fissix.fixes.fix_tuple_params`` (fixup needed) * Prevalence: Common Python 3 requires that each argument of a ``def`` function has a name. @@ -79,7 +79,7 @@ named function would be an improvement. Backticks ~~~~~~~~~ -* :ref:`Fixer `: ``python-modernize -wnf lib2to3.fixes.fix_repr`` (with caveat) +* :ref:`Fixer `: ``python-modernize -wnf fissix.fixes.fix_repr`` (with caveat) * Prevalence: Common The backtick (`````) operator was removed in Python 3. @@ -108,7 +108,7 @@ hurts to check if the code does it. The Inequality Operator ~~~~~~~~~~~~~~~~~~~~~~~ -* :ref:`Fixer `: ``python-modernize -wnf lib2to3.fixes.fix_ne`` +* :ref:`Fixer `: ``python-modernize -wnf fissix.fixes.fix_ne`` * Prevalence: Rare In the spirit of “There's only one way to do it”, Python 3 removes the diff --git a/source/tools.rst b/source/tools.rst index 09a7584..cb256fb 100644 --- a/source/tools.rst +++ b/source/tools.rst @@ -52,10 +52,15 @@ These are best handled by the ``python-modernize`` tool – a code-to-code translator that takes a Python 2 codebase and updates it to be compatible with both Python 2 and 3. -The tool builds on top of ``2to3``, a library that comes with Python. ``2to3`` -was once intended as the main porting tool. It turned out inadequate for that -task, but ``python-modernize`` (among others) successfully reuses its general -infrastructure. +.. note:: + + ``python-modernize`` was built on top of ``2to3`` from of Python's + standard library. ``2to3`` was once intended as the main porting tool. + It turned out inadequate for that task, but ``python-modernize`` + (among others) successfully reuses its general infrastructure. + Because ``2to3`` itself is built into Python and thus missing improvements + newer than the the Python that runs it, ``python-modernize`` now uses a + fork of ``2to3`` called ``fissix``. Assuming code is in version control, you'll generally want to run ``python-modernize`` with the ``-wn`` flags: ``-w`` flag causes the tool to From 278f372bb5aeb699af0e4c8d3e79c060a619455c Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 3 Sep 2021 14:34:54 +0200 Subject: [PATCH 30/34] Switch CI to GitHub Actions --- .github/workflows/main.yml | 18 ++++++++++++++++++ .travis.yml | 10 ---------- 2 files changed, 18 insertions(+), 10 deletions(-) create mode 100644 .github/workflows/main.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..5789bcc --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,18 @@ +name: Run pytest + +on: + push: + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: '3.6' + - name: Install dependencies + run: python -m pip install -r test-requirements.txt + - name: Run tests + run: python -m pytest -v diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index a5d8f25..0000000 --- a/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -language: python - -python: - - "3.6" - -install: python -m pip install -r test-requirements.txt - -script: python -m pytest -v - -sudo: false From 3ea375015e6321db3529d2bdeeb6c29978a5e565 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 3 Sep 2021 15:21:30 +0200 Subject: [PATCH 31/34] Cover `async` and `await` --- source/syntax.rst | 61 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/source/syntax.rst b/source/syntax.rst index 463445f..feb7953 100644 --- a/source/syntax.rst +++ b/source/syntax.rst @@ -126,6 +126,9 @@ New Reserved Words * :ref:`Fixer `: None * Prevalence: Rare +Constants +......... + In Python 3, ``None``, ``True`` and ``False`` are syntactically keywords, not variable names, and cannot be assigned to. This was partially the case with ``None`` even in Python 2.6. @@ -133,6 +136,64 @@ This was partially the case with ``None`` even in Python 2.6. Hopefully, production code does not assign to ``True`` or ``False``. If yours does, figure a way to do it differently. +``async`` and ``await`` +....................... + +Since Python 3.7, ``async`` and ``await`` are also keywords. + +If your code uses these names, rename it. +If other code depends on the names, keep the old name available for +old Python versions. +The way to do this will be different in each case, but generally +you'll need to take advantage of the fact that in Python's various namespaces +the strings ``'async'`` and ``'await'`` are still valid keys, even if they +are not accesible usual with the syntax. + +For module-level functions, classes and constants, also assign the original +name using :py:func:`globals()`. +For example, a function previously named ``async`` could look like this:: + + def asynchronous(): + """... + + This function used to be called `async`. + It is still available under old name. + """ + + globals()['async'] = asynchronous + +For methods, and class-level constants, assign the original name using +``setattr``:: + + class MyClass: + def asynchronous(self): + """... + + This method used to be called `async`. + It is still available under old name. + """ + + setattr(MyClass, 'async', MyClass.asynchronous) + +For function parameters, more work is required. The result will depend on +whether the argument is optional and whether ``None`` is a valid value for it. +Here is a general starting point:: + + def process_something(asynchronous=None, **kwargs): + if asynchronous is None: + asynchronous = kwargs.get('async', None) + else: + if 'async' in kwargs: + raise TypeError('Both `asynchronous` and `async` specified') + if asynchronous is None: + raise TypeError('The argument `asynchronous` is required') + +For function arguments, if the parameter cannot be renamed as above, +use “double star” syntax that allows you to pass arbitrary argument names:: + + process_something(**{'async': True}) + + Other Syntax Changes ~~~~~~~~~~~~~~~~~~~~ From d58747c364933237b89a7ece090661ef54d044fb Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 3 Sep 2021 15:36:45 +0200 Subject: [PATCH 32/34] Cover generators raising StopIteration --- source/iterators.rst | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/source/iterators.rst b/source/iterators.rst index 57a5e89..4052a4e 100644 --- a/source/iterators.rst +++ b/source/iterators.rst @@ -255,3 +255,31 @@ fixer's output and revert the changes for objects of this class. The fixer will not add a ``__next__`` method to your classes. You will need to do this manually. + +Generators cannot raise ``StopIteration`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* :ref:`Fixer `: None +* Prevalence: Rare + +Since Python 3.7, generators cannot raise ``StopIteration`` directly, +but must stop with ``return`` (or at the end of the function). +This change was done to prevent subtle errors when a ``StopIteration`` +exception “leaks” between unrelated generators. + +For example, the following generator is considered a programming error, +and in Python 3.7+ it raises ``RuntimeError``:: + + def count_to(maximum): + i = 0 + while True: + yield i + i += 1 + if i >= maximum: + raise StopIteration() + +Convert the ``raise StopIteration()`` to ``return``. + +If your code uses a helper function that can raise ``StopIteration`` to +end the generator that calls it, you will need to move the returning logic +to the generator itself. From 93328c4f63824fcdde5a70b9bd5c03dc48315ae1 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 9 Sep 2021 11:09:14 +0200 Subject: [PATCH 33/34] Cover invoking Python (sys.executable, shebangs) (#69) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Miro Hrončok --- source/index.rst | 1 + source/invoking-python.rst | 56 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 source/invoking-python.rst diff --git a/source/index.rst b/source/index.rst index 9a51efb..13182ce 100644 --- a/source/index.rst +++ b/source/index.rst @@ -49,6 +49,7 @@ Still with us? Let's dive in! classes comprehensions core-obj-misc + invoking-python etc diff --git a/source/invoking-python.rst b/source/invoking-python.rst new file mode 100644 index 0000000..e307c05 --- /dev/null +++ b/source/invoking-python.rst @@ -0,0 +1,56 @@ +Invoking Python +--------------- + +While this is not a change in Python 3, the transition increased the number of +systems that have more than one Python interpreter installed: it is not +uncommon for ``python``, ``python2``, ``python3``, ``python3.6`` and +``python3.9`` to all be valid system commands; other interpreters may be +installed in non-standard locations. + +This makes it important to use the correct command for each situation. + + +Current interpreter +~~~~~~~~~~~~~~~~~~~ + +The current Python interpreter should be invoked via ``sys.executable``. + +Python provides the path of the currently running interpreter as +:data:`sys.executable`. +This variable should be preferred over ``python`` or other hard-coded commands. + +For example, rather than:: + + subprocess.Popen('python', 'somescript.py') + +use:: + + subprocess.Popen(sys.executable, 'somescript.py') + +The assumption that ``'python'`` is correct is only valid in tightly controlled +environments; however, even in those environments ``sys.executable`` is likely +to be correct. + +The documentation does include a warning: + + If Python is unable to retrieve the real path to its executable, + ``sys.executable`` will be an empty string or ``None``. + +In practice, this does not apply to mainstream platforms. +If ``sys.executable`` is unusable, then either your platform's concept of +launching a process via filename is somehow unusual (and in this +case you should know what to do), or there's an issue in Python itself. + + +Unix shebangs +~~~~~~~~~~~~~ + +On Unix, executables written in Python must have a shebang line identifying +the interpreter. +The correct shebang to use will depend on the environment you are targeting +and on the version compatibility of the project. + +General recommendations for Python shebangs are listed in +the `For Python script publishers`_ section of PEP 394. + +.. _For Python script publishers: https://www.python.org/dev/peps/pep-0394/#for-python-script-publishers From 6624d1933fc3315f284a7c55c93629c6da24c986 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 9 Sep 2021 13:48:16 +0200 Subject: [PATCH 34/34] Add notes on incompleteness & newer Python versions https://github.com/fedora-python/portingguide/pull/70 --- source/process.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/source/process.rst b/source/process.rst index 41a88bc..57ed0f7 100644 --- a/source/process.rst +++ b/source/process.rst @@ -148,6 +148,17 @@ tackled in a typical project. We recommend that you skim the introduction of each of the chapters, so that you know what you're up against before you start. +Note that while the guide is fairly comprehensive, there are changes it does +not cover. +Be prepared to find a few issues specific to your code base that you'll need +to figure out independently. + +Also note that the guide was written for Python 3.6. +It includes several updates for newer versions, but we recommend skimming +[What's New lists](https://docs.python.org/3/whatsnew/index.html) +in the Python documentation to familiarize yourself with +the changes in newer versions of Python. + .. index:: dropping Python 2