Converting 2 to 3 Notes - MridulS/pysal GitHub Wiki
PySAL Python 2.7 -> PySAL Python 3.4+
NOTE: This has been completed with PySAL 1.11.0
In order to have Python 2.7 and Python 3.4+ compliant code, some compatibility changes need to be made in the Python 2.7 code. We plan to maintain at least a Python 2.7 release, which gets automatically converted into Python 3.4 at install time.
Once done, we can still focus on a 2.7+ code base and use automated conversion to keep Python 3.4 compatibility. Since any CI framework can test multiple versions of the library, we can keep valid tests for both versions simultaneously with little effort.
In fact, you probably write code that is very close to python 3-compliant already.
#Where are we now?
Currently, all tests are passing in python 2.7 and python 3.4 from ljwolf's py3conv_cleaning branch. py3conv_cleaning is the current version of the python3-ready code. That branch has been cleaned up for merging to pysal/master.
The branch sjsrey py3merge has merged these changes and is passing.
This does not mean it is release-ready. Distribution and more rigorous testing are warranted.
For Travis, the build works on 2.7, but I can't figure out how to get it to build correctly on Python 3. It's missing pysal.py, and I'm not sure how or why setuptools are looking for that file.
Working on the Port
Since tests are passing, this is probably less urgent. But, if you'd like to validate, here's how to get a copy of PySAL working in Python 3.
Converting the Code
Clone ljwolf's py3conv_cleaning branch. This will give you two scripts in the git root, mk2 and mk3. The standard workflow is:
- make changes in your personal
py3convbranch. git addthe file you change andgit committhe file.- use
mk3to convert the file into python3.- all this script does is create a temporary branch,
2to3, where the output of the automated 2to3 changes go. - the key to remember is that this branch,
2to3is ephemeral and will be rewritten every time2to3is run!
- all this script does is create a temporary branch,
- test in python3, either using
nosetests-3.4orpython3and run the actualtest_*.pyfile. - to get back to your committed changes in the python2 branch, run
mk2.
The mk2 and mk3 scrips are <4 lines long. mk2 is only git checkout py3conv_cleaning && git stash. mk3 remakes the 2to3 branch and dumps the converted code into it.
##What's Broken?
Currently, all tests are passing in python 2.7 and 3.4!
###Continuous integration
test each testing directory separately using nosetests3 -w ./test, as nosetests3 will exit out of the testing suite prematurely otherwise. Not sure why.
MUST figure out how to configure .travis.yml to test 3.4 and 2.7, while converting 3.4 inline.
###Potential for ship-one codebase
If we want, we can investigate the use of __future__ and six to make the codebase work in both 3 and 2 without conversion. Right now, this is a far-off goal.
According to this guidance if we want to ship one code base, assuming it is coded in a forward-3-compatible style, the setup script can be modified so that either distutils or Distribute run 2to3 for us:
try: # Python 3
from distutils.command.build_py import build_py_2to3 as build_py
except ImportError: # Python 2
from distutils.command.build_py import build_py
setup(cmdclass = {'build_py': build_py},
# ...
)
using Distribute:
setup(use_2to3=True,
# ...
)
Full Development notes:
-
cg: all pass -
core: By far the hardest fixes are here, thanks to changes in Python3's string model. -
ESDA: Errors now are only showing in smoothing, with changes made to weights.-
test_gamma.pyworks on first try -
test_geary.pyworks on first try -
test_getisord.pyworks on first try -
test_join_counts.pyworks on first try -
test_mapclassify.pyhas a deprecation warning, but works on first try. -
test_mixture_smoothing.pyworks on first try -
test_moran.pyworks on first try -
test_smoothing.pyneeded major floatdiv modification. After the [assuncao rate bugfix merged] (https://github.com/pysal/pysal/pull/657) to master, this is complete.
-
-
meta: no tests. But, we know it's use ofurllib2will need to be rewritten. I've tried usingsix'surllibin place of where we usedurllib2as recommended. -
network: once the relative import is resolved, tests pass. -
Inequality: all pass -
Region: given fixed componentspysal/weights, the following holds:-
test_components.pyworks on first try -
test_maxp.py: assertion errors in all tests. @dfolch & @ljwolf fixed. -
test_randomregion.py: works on first try
-
-
spatial_dynamics: all pass -
spreg: We'll just list test by test:- diagnostics: all work
- error:
-
tests_error_sp.pyfails out withAssertionErroron a matrix. The difference between the two are only due to one elements' last digit. -
test_error_sp_het.pypasses -
test_error_sp_het_regimes.pyfails on some float/int stuff. After fixing those, we fail outlat2Wissues, also floor division. Once floor division is used where it should be, tests pass. -
test_error_sp_het_sparse.pyworks on first try. -
test_error_sp_hom.pyworks on first try -
test_error_sp_hom_regimes.pylooks like same initial errors astest_error_sp_het_regimes.py. Fixed by swapping out for floor div on the regime test code. -
test_error_sp_hom_sparse.pyworks on first try -
test_error_sp_regimes.pyalso has float/floor errors. Once fixed, we fail out on two assertion errors, both exceedingly close to each other, but slightly larger than the others I'd consider closed. -
test_error_sp_sparse.pyfails out on an array comparison, but visual inspection reveals the arrays are the same, save one entry's ten-thousandth place decimal. So... pretty safe to say this passes in spirit
-
- ml:
-
test_ml_error.pyfails on anAttributeError: can't set attributeon line 254 ofml_error.This was not an easy fix. But, essentially, it involved converting any property of a regression denoted by an@propertyto also have an accompanying setter. To do this, I had to modify the caching behavior. In concept, it's this flow. This keeps the setter/getter on a similar structure. I would've liked to implement this as a decorator that modifies properties, something likecached_property, but I couldn't get it to work immediately. This is probably the cleaner method going forward, but we would need two decorators to describe above. Once this is hacked intoRegressionPropsVMandRegressionPropsYinspreg/utils.py, this, and everything else with this problem, passes. -
test_ml_error_regimes.pyhad same regime problems as above. Once fixed, it had attribute errors like ml error. Seeml_error.pyfor discussion of resolution. -
test_ml_lag.pyhad the same error asml_error. -
test_ml_lag_regimes.pyhad same error asml_lag.
-
- ols:
-
test_ols.pyhadml_error-like attribute errors -
test_ols_regimes.pytoo -
test_ols_sparse.pyworks on first try
-
-
test_probit.pyhad the same attribute errors, and required significant modification, since it did not inherit fromRegressionPropsVMandRegressionPropsY. So, significant modification, same strategy. Tests now pass. - twosls:
-
test_twosls.pyattribute errors all over the place. Same idea as the fix discussed inml_error, but needed to be custom fortwoslsbecause it overrides thevmmethod. now passes. -
test_twosls_regimes.pyhad both attribute errors and float-floor errors. -
test_twosls_sp.pyfailed on attribute errors. seetwoslsfor answer. now passes. -
test_twosls_sp_regimes.pyfailed like plaintwosls_regimes.py. now passes. -
test_twosls_sp_sparse.pyfailed on attribute errors. now passes.
-
-
weights:- fixed the simple float conversion issues in tests.
-
lat2Wwas using float div rather than floor div, fixing that makeslat2Wwork correctly. We currently don't test lat2W in weights, but it gets tested by proxy because it's used inspreg. Should probably also sanity check the hexagonal lattice generator. -
test_Distancefails out on using lists where we should be using sets, so the results are identical, but they're not correctly ordered. They correspond by index to the correct neighbors. Tests rewritten here using dictionaries of the form:{id:{neighbor_id:weight}}, which can be changed in the future to construct a numpy array for allclose comparisons. -
test_userfails out in what looks like the exact same error & numbers fromtest_Distance. Tests rewritten to use dictionaries. -
test_weightsfails out also in the fact we're comparing against lists. The set of neighbors would be fine. Tests rewritten to use dictionaries. -
test_Wsetsfails out on lists v. sets. Rewritten to use sets.