Support both
I wanted to make an attempt at converting the BeautifulSoup library to 3x for a project I'm working on but I can see how it would be a pain to maintain two different branches of the code.
The current model to handle this include:
- make a change to the 2x branch
- run 2to3
- pray that it does the conversion properly the first time
- run the code
- run unit tests to verify that everything works
- copy the output to the 3x branch
This model works but IMHO it sucks. For every change/release you have to go through these steps ::sigh::. Plus, it discourages developers from extending the 3x branch with new features that can only be supported in py3k because you're still essentially targeting all the code to 2x.
The solution... use a preprocessor
Since I couldn't find a decent c-style preprocessor with #define and #ifdef directives for python I wrote one.
It's called pypreprocessor and can be found in the PYPI
Essentially, what you do is:
- import pypreprocessor
- detect which version of python the script is running in
- set a 'define' in the preprocessor for the version (ex 'python2' or 'python3')
- sprinkle '#ifdef python2' and '#ifdef python3' directives where the code is version specific
- run the code
That's it. Now it'll work in both 2x and 3x. If you are worried about added performance hit of running a preprocessor there's also a mode that will strip out all of the metadata and output the post-processed source to a file.
Best of all... you only have to do the 2to3 conversion once.
Here's the a working example:
#!/usr/bin/env python
# py2and3.py
import sys
from pypreprocessor import pypreprocessor
#exclude
if sys.version[:3].split('.')[0] == '2':
pypreprocessor.defines.append('python2')
if sys.version[:3].split('.')[0] == '3':
pypreprocessor.defines.append('python3')
pypreprocessor.parse()
#endexclude
#ifdef python2
print('You are using Python 2x')
#ifdef python3
print('You are using python 3x')
#else
print('Python version not supported')
#endif
These are the results in the terminal:
python py2and3.py
>>>You are using Python 2x
python3 py2and3.py
>>>You are using python 3x
If you want to output to a file and make clean version-specific source file with no extra meta-data, add these two lines somewhere before the pypreprocessor.parse() statement:
pypreprocessor.output = outputFileName.py
pypreprocessor.removeMeta = True
Then:
python py2and3.py
Will create a file called outputFileName.py that is python 2x specific with no extra metadata.
python3 py2and3.py
Will create a file called outputFileName.py that is python 3x specific with no extra metadata.
For documentation and more examples see check out pypreprocessor on GoogleCode.
I sincerely hope this helps. I love writing code in python and I hope to see support progress into the 3x realm asap. I hate to see the language not progress. Especially, since the 3x version resolves a lot of the featured WTFs and makes the syntax look a little more friendly to users migrating from other languages.
The documentation at this point is complete but not extensive. I'll try to get the wiki up with some more extensive information soon.
Update:
Although I designed pypreprocessor specifically to solve this issue, it doesn't work because the lexer does syntax checking on all of the code before any code is executed.
If python had real C preprocessor directive support it would allow developers to write both python2x and python3k code alongside each other in the same file but due to the bad reputation of the C preprocessor (abuse of macro replacement to change language keywords) I don't see legitimate C preprocessor support being added to python any time soon.