Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Reverted from v. 10

...

Your .py files will now have a series of blocks that look like this, using C preprocessor syntax:

. . .

#ifndef USING_PYTHON2

from logginginterface import *

#else /* USING_PYTHON2 */

from .logginginterface import *

#endif /* USING_PYTHON2 */

NOTE: The sense of the "#ifdef/#ifndef" is backwards from what we really want,

but this will be dealt with in a later section below.

I've added annotated the lines further to make it clearer which lines are the python2 and which are the python3 code.

. . .

#ifndef USING_PYTHON2

import urllibfrom logginginterface import *                             # python2 code

#else /* USING_PYTHON2 */

import urllib.request, urllib.parse, urllib.errorfrom .logginginterface import *                            # python3 code

#endif /* USING_PYTHON2 */

. . .

#ifndef USING_PYTHON2  return urllib.quote(str(s), '')

import urllib                                              # python2 code

#else /* USING_PYTHON2 */

  return import urllib.request, urllib.parse.quote(str(s), ''), urllib.error          # python3 code

#endif /* USING_PYTHON2 */

. . .

#ifndef USING_PYTHON2

  os.umask(077)return urllib.quote(str(s), '')                          # python2 code

#else /* USING_PYTHON2 */

  os.umask(0o77)return urllib.parse.quote(str(s), '')                    # python3 code

#endif /* USING_PYTHON2 */

. . .

#ifndef USING_PYTHON2

  if openstack.has_key('custom_configuration'):os.umask(077)                                           # python2 code

#else /* USING_PYTHON2 */

  if 'custom_configuration' in openstack:os.umask(0o77)                                          # python3 code

#endif /* USING_PYTHON2 */

. . .

#ifndef USING_PYTHON2

  print >>fp, binascii.hexlify(b).decode('utf-8')if openstack.has_key('custom_configuration'):           # python2 code

#else /* USING_PYTHON2 */

  print(binascii.hexlify(b).decode('utf-8'), file=fp)if 'custom_configuration' in openstack:                 # python3 code

#endif /* USING_PYTHON2 */

. . .

#ifndef USING_PYTHON2

  debug("ctx node has the following Properties: {}".format(i.properties.keys()))print >>fp, binascii.hexlify(b).decode('utf-8')        # python2 code

#else /* USING_PYTHON2 */

  debug("ctx node has the following Properties: {}".format(list(i.properties.keys())))print(binascii.hexlify(b).decode('utf-8'), file=fp)    # python3 code

#endif /* USING_PYTHON2 */

. . .

#ifndef USING_PYTHON2

  for k, nv in want.items():debug("ctx node has the following Properties: {}".format(i.properties.keys()))

#else /* USING_PYTHON2 */

  for k, nv in list(want.items()):debug("ctx node has the following Properties: {}".format(list(i.properties.keys())))

#endif /* USING_PYTHON2 */

. . .

These .py files are currently executable by NEITHER Python2 NOR Python3, so the next step is to deal with the differences to make them runnable by BOTH.

...

#ifndef USING_PYTHON2

  for k, nv in want.items():                           # python2 code

#else /* USING_PYTHON2 */

  for k, nv in list(want.items()):                     # python3 code

#endif /* USING_PYTHON2 */

. . .

These .py files are currently executable by NEITHER Python2 NOR Python3, so the next step is to deal with the differences to make them runnable by BOTH.

  • There are some things allowed in Python2.7 that are mandated in Python3.
  • There are other things that can be done in just the Python3 form if the right settings are provided.
  • Or there are  things that can be done in just the Python2 form.
  • There are things that must be done differently in the two languages, so a run-time choice is needed.

We will make use of each of these features.

Preparatory Statements

First step is to add these statements to the very top of each .py file:

...

from __future__ import print_function
import sys
USING_PYTHON2 = sys.version_info[0] < 3

If your .py file starts with a #! invocation statement, place those lines AFTER the shbang:

...

#!/usr/bin/env python
from __future__ import print_function

import sys
USING_PYTHON2 = sys.version_info[0] < 3

These lines do two things:

...

  • form.
  • There are things that must be done differently in the two languages, so a run-time choice is needed.

We will make use of each of these features.

Preparatory Statements

First step is to add these statements to the very top of each .py file:

from __future__ import print_function
import sys
USING_PYTHON2 = sys.version_info[0] < 3

If your .py file starts with a #! invocation statement, place those lines AFTER the shbang:

#!/usr/bin/env python
from __future__ import print_function

import sys
USING_PYTHON2 = sys.version_info[0] < 3

These lines do two things:

  1. Allows the function version of the print statements to be used.
  2. Gives us a value (USING_PYTHON2) that can be tested for at run time to determine if we are running Python2 or Python3.

Changing the #ifndef/#else/#endif Statements

You need to change blocks such as this:

#ifndef USING_PYTHON2

some python2 code

#else /* USING_PYTHON2 */

some python3 code

#endif /* USING_PYTHON2 */


#ifdef USING_PYTHON2

   some python3 code

#endif /* USING_PYTHON2 */

to this. Make CERTAIN that the indentation is adjusted as well.

if USING_PYTHON2:

  some python2 code

else:

  some python3 code


if not USING_PYTHON2:

  some python3 code

Yes, there is some cognitive dissonance for changing "#ifndef" to "if", but the rest of the generated code is set up properly to be able to quickly edit the code.

  • Search for all instances of "#ifndef USING_PYTHON2" and change "#ifndef" to "if" and add a trailing ":".
  • Search for all instances of "#else /* USING_PYTHON2 */" and change that to "else:".
  • Remove all instances of "#endif /* USING_PYTHON2 */" from the code.
  • Re-indent the lines in between.


  • You should also search for "#ifdef USING_PYTHON2" and change that to "if not USING_PYTHON2:".

Import Statements

The way that imports of functions found in local files changes in Python3. So change blocks such as this:

#ifndef USING_PYTHON2

from logginginterface import *     # python2 code

#else /* USING_PYTHON2 */

from .logginginterface import *    # python3 code

#endif /* USING_PYTHON2 */

...

#ifndef USING_PYTHON2

import urlliburllib     # python2 code

#else /* USING_PYTHON2 */

import urllib.request, urllib.parse, urllib.errorerror     # python3 code

#endif /* USING_PYTHON2 */

...

if USING_PYTHON2:

  from logginginterface import *     # python2 code

  import urllib

else:

  from .logginginterface import **     # python3 code

  import urllib.request, urllib.parse, urllib.error

...

#ifndef USING_PYTHON2

  return urllib.quote(str(s), '')           # python2 code

#else /* USING_PYTHON2 */

  return urllib.parse.quote(str(s), '')     # python3 code

#endif /* USING_PYTHON2 */

...

#ifndef USING_PYTHON2

  os.umask(077)     # python2 code

#else /* USING_PYTHON2 */

  os.umask(0o77)     # python3 code

#endif /* USING_PYTHON2 */

...

#ifndef USING_PYTHON2

  if openstack.has_key('custom_configuration'):     # python2 code

#else /* USING_PYTHON2 */

  if 'custom_configuration' in openstack:     # python3 code

#endif /* USING_PYTHON2 */

...

#ifndef USING_PYTHON2

  print >>fp, binascii.hexlify(b).decode('utf-8')     # python2 code

#else /* USING_PYTHON2 */

  print(binascii.hexlify(b).decode('utf-8'), file=fp)     # python3 code

#endif /* USING_PYTHON2 */

...

#ifndef USING_PYTHON2

  debug("ctx node has the following Properties: {}".format(i.properties.keys()))     # python2 code

#else /* USING_PYTHON2 */

  debug("ctx node has the following Properties: {}".format(list(i.properties.keys())))     # python3 code

#endif /* USING_PYTHON2 */

...

to instead read

[tox]
envlist = py27,py34py36

Now run "tox test" to execute your test modules once with Python2 and then again with Python3. (The final choice of py34 vs py36 vs py37 is yet to be determined.)

...