if sys.py3k: # some py2k specific code pass Why? 1. readable 2. traceable Explained: 1. self-explanatory 2. sys.version_info >= (3, 0) or sys.version[0] == '3' is harder to trace when you need to find all python 3 related hacks -- anatoly t.
On Mon, Nov 05, 2012 at 12:49:24AM +0300, anatoly techtonik <techtonik@gmail.com> wrote:
if sys.py3k:
1. import sys 2. sys.py3k = sys.version_info >= (3, 0) 3. ??? 4. PROFIT! Oleg. -- Oleg Broytman http://phdru.name/ phd@phdru.name Programmers don't die, they just GOSUB without RETURN.
On Sun, Nov 4, 2012 at 1:49 PM, anatoly techtonik <techtonik@gmail.com> wrote:
if sys.py3k: # some py2k specific code (I assume your comment has a numeric typo?) pass
You would need that attribute to also be present in Python 2.x for your snippet to work, and my understanding is that 2.x is closed to feature additions.
Why? 1. readable 2. traceable
Explained: 1. self-explanatory
"py3k" is a nickname/codename that not everyone using Python 3 may know about.
2. sys.version_info >= (3, 0) or sys.version[0] == '3' is harder to trace when you need to find all python 3 related hacks
Rebutted: "There should be one-- and preferably only one --obvious way to do it." Apparently we already have at least 2 ways to do it; let's not muddle things further by adding yet another. Cheers, Chris
On 05/11/12 08:49, anatoly techtonik wrote:
if sys.py3k: # some py2k specific code pass
Do you expect every single Python 3.x version will have exactly the same feature set? That's not true now, and it won't be true in the future. In my option, a better approach is more verbose and a little more work, but safer and more reliable: check for the actual feature you care about, not some version number. E.g. I do things like this: # Bring back reload in Python 3. try: reload except NameError: from imp import reload Now your code is future-proofed: if Python 3.5 moves reload back into the builtins, your code won't needlessly replace it. Or if you're running under some environment that monkey-patches the builtins (I don't know, IDLE or IPython or something?) you will use their patched reload instead of the one in the imp module. Or I go the other way: try: any except NameError: # Python 2.4 compatibility. def any(items): for item in items: if item: return True return False Now if I'm running under a version of 2.4 that has backported the "any" function, I will prefer the backported version to my own. -- Steven
On 4 November 2012 22:33, Steven D'Aprano <steve@pearwood.info> wrote:
On 05/11/12 08:49, anatoly techtonik wrote:
if sys.py3k: # some py2k specific code pass
Do you expect every single Python 3.x version will have exactly the same feature set? That's not true now, and it won't be true in the future.
In my option, a better approach is more verbose and a little more work, but safer and more reliable: check for the actual feature you care about, not some version number. E.g. I do things like this:
# Bring back reload in Python 3. try: reload except NameError: from imp import reload
There are certain cases where explicitly checking the version makes sense. I think that Python 3 vs Python 2 is sometimes such a case. Python 3 changes the meaning of a number of elementary aspects of Python so that the same code can run without error but with different semantics under the two different version series. Checking the version rather than checking the attribute/name would often be a mistake when comparing say Python 2.6 and Python 2.7 since you're better off just sticking to 2.6 syntax and checking for potentially useful names available under 2.7 as you describe. On the other hand if you are distinguishing between 2.x and 3.x then it is sometimes clearer and more robust to explicitly make a version check rather than think hard about how to write code that works in both cases (and hope that you remember your reasoning later). It also makes it easier for you to clean up your codebase when you eventually drop support for 2.x. Oscar
On Mon, Nov 05, 2012 at 02:08:33AM +0000, Oscar Benjamin wrote:
There are certain cases where explicitly checking the version makes sense. I think that Python 3 vs Python 2 is sometimes such a case. Python 3 changes the meaning of a number of elementary aspects of Python so that the same code can run without error but with different semantics under the two different version series.
You can test for that without an explicit version check. if isinstance(map(lambda x: x, []), list): # Python 2 semantics ... else: # Python 3 semantics ... This now guards you against (e.g.) somebody backporting Python 3 semantics to "Python 2.8" (it's opensource, somebody could fork CPython), or running your code under "FooPython" which has 3.x semantics and a 1.x version number. This is more work than just mechanically looking at the version number, but it's not that much more work, and is more reliable since it explicitly checks for the feature you want, rather than an implicit check based on the version number. In any case, arguments about defensive coding style are getting off-topic. The point is that there are various ways to test for the existence of features, and adding yet another coarse-grained test "sys.py3k" doesn't gain us much (if anything). -- Steven
On Mon, Nov 5, 2012 at 9:30 AM, Steven D'Aprano <steve@pearwood.info> wrote:
On Mon, Nov 05, 2012 at 02:08:33AM +0000, Oscar Benjamin wrote:
There are certain cases where explicitly checking the version makes sense. I think that Python 3 vs Python 2 is sometimes such a case. Python 3 changes the meaning of a number of elementary aspects of Python so that the same code can run without error but with different semantics under the two different version series.
... In any case, arguments about defensive coding style are getting
off-topic. The point is that there are various ways to test for the existence of features, and adding yet another coarse-grained test "sys.py3k" doesn't gain us much (if anything).
The problem is to maintain the code in the long term. Python 3 documentation already misses the things about Python 2 modules, so with implicit feature tests legacy code checks may quickly get out of control. It's not uncommon to see unwieldy projects with huge codebase of repetitive code in corporate environment where people afraid to bring down legacy stuff, because they don't know why it was inserted in the first place. I thought of sys.py3k check as an explicit way to guard the code that should be maintained extra carefully for Python 3 compatibility, so that you can grep the source for this constant and remove all the hacks (such as bytes to string conversion) required to maintain the compatibility when the time comes to switch. Now I see that all points raised about it being too late, not sufficient (because API breakage occurs even between minor versions) are valid. The six module is an awesome alternative. Too bad it doesn't come bundled by default or as an easy "after installation" update. The granularity of a "feature" is interesting. Previously it was from __future__ import 'feature'` for forward compatibility, but it required planning features beforehand. Now there is a need to test for a feature, which is not present in the early version, and we have only implicit ways. These ways assume that we know what a feature and its symptoms are, which is not the case when code is not yours or you have little experience with either Python 2 or 3. I hoped that `if sys.py3k` could help make code more readable, but it will be more useful to have shared 'features' module which can contain explicit checks for existing features and return False for things that are not present. `six` is awesome though. -- anatoly t.
On Thu, Nov 8, 2012 at 5:35 AM, anatoly techtonik <techtonik@gmail.com> wrote:
I thought of sys.py3k check as an explicit way to guard the code that should be maintained extra carefully for Python 3 compatibility, so that you can grep the source for this constant and remove all the hacks (such as bytes to string conversion) required to maintain the compatibility when the time comes to switch.
I agree about greppability, it's a huge help. Hence the code comment; as long as you're consistent and you pick a keyword long enough or unusual enough to not occur anywhere else, you can easily do a "find across files" or "grep XYZ *" to find them all. And if you put the comment on the most significant line of code, line-based tools will be more useful. # Unideal: # py3k try: reload except NameError: from imp import reload # Better: try: # py3k reload except NameError: from imp import reload # Best: try: reload # py3k except NameError: from imp import reload # Also best: try: reload except NameError: from imp import reload # py3k Taking just the line with the keyword "py3k" on it will tell you exactly what that file is doing. ChrisA
Am 04.11.2012 22:49, schrieb anatoly techtonik:
if sys.py3k: # some py2k specific code pass
Why? 1. readable 2. traceable
Explained: 1. self-explanatory 2. sys.version_info >= (3, 0) or sys.version[0] == '3' is harder to trace when you need to find all python 3 related hacks
This proposal is roughly 3 minor versions late. I can offer you a sys.py3_4 attribute though... ;) Georg
On Mon, Nov 5, 2012 at 4:55 PM, Georg Brandl <g.brandl@gmx.net> wrote:
Am 04.11.2012 22:49, schrieb anatoly techtonik:
if sys.py3k: # some py2k specific code pass
Why? 1. readable 2. traceable
Explained: 1. self-explanatory 2. sys.version_info >= (3, 0) or sys.version[0] == '3' is harder to trace when you need to find all python 3 related hacks
This proposal is roughly 3 minor versions late. I can offer you a sys.py3_4 attribute though... ;)
Even better (http://packages.python.org/six/#package-contents): import six if six.PY3: # Ooh, Python 3 else: # Not Python 3 If anyone is trying to do single code base Python 2/3 support without relying on six, they're doing it wrong. Even bundling a copy (if you don't want to deal with dependency management issues) is a better idea than reinventing that wheel. If you *are* rolling your own (or need additional compatibility fixes that six doesn't provide), then all Python 2/3 compatibility hacks should be located in a small number of compatibility modules. They *shouldn't* be distributed widely throughout your codebase. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On Nov 05, 2012, at 06:04 PM, Nick Coghlan wrote:
Even better (http://packages.python.org/six/#package-contents):
import six
if six.PY3: # Ooh, Python 3 else: # Not Python 3
If anyone is trying to do single code base Python 2/3 support without relying on six, they're doing it wrong. Even bundling a copy (if you don't want to deal with dependency management issues) is a better idea than reinventing that wheel.
If you *are* rolling your own (or need additional compatibility fixes that six doesn't provide), then all Python 2/3 compatibility hacks should be located in a small number of compatibility modules. They *shouldn't* be distributed widely throughout your codebase.
While I agree with the sentiment, and also agree that six is an excellent package that can be very useful, I'll just point out that it's often very possible and not at all painful to write to a single code base without using it. It all depends on what your code does/needs. Cheers, -Barry
On Nov 6, 2012 2:12 AM, "Barry Warsaw" <barry@python.org> wrote:
On Nov 05, 2012, at 06:04 PM, Nick Coghlan wrote:
Even better (http://packages.python.org/six/#package-contents):
import six
if six.PY3: # Ooh, Python 3 else: # Not Python 3
If anyone is trying to do single code base Python 2/3 support without relying on six, they're doing it wrong. Even bundling a copy (if you don't want to deal with dependency management issues) is a better idea than reinventing that wheel.
If you *are* rolling your own (or need additional compatibility fixes that six doesn't provide), then all Python 2/3 compatibility hacks should be located in a small number of compatibility modules. They *shouldn't* be distributed widely throughout your codebase.
While I agree with the sentiment, and also agree that six is an excellent package that can be very useful, I'll just point out that it's often very possible and not at all painful to write to a single code base without
using
it. It all depends on what your code does/needs.
True, my own 2/3 compatible projects don't use it, but they also don't have any significant special cases for either version. I guess stick a "for non-trivial cases" qualifier in there somewhere :) Cheers, Nick. -- Sent from my phone, thus the relative brevity :)
Cheers, -Barry
_______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas
participants (9)
-
anatoly techtonik
-
Barry Warsaw
-
Chris Angelico
-
Chris Rebert
-
Georg Brandl
-
Nick Coghlan
-
Oleg Broytman
-
Oscar Benjamin
-
Steven D'Aprano