From 6e58be3fa94c9eb857084a2dbd4cea7b8d7849f4 Mon Sep 17 00:00:00 2001
From: Christoph Knote <christoph.knote@physik.uni-muenchen.de>
Date: Wed, 16 Sep 2020 12:04:05 +0200
Subject: [PATCH] Make compatible with Python 3

---
 .gitignore              |  7 +++++++
 CHANGES.rst             |  5 +++++
 boxmox/.gitignore       |  4 +++-
 boxmox/__init__.py      | 14 +++++++-------
 boxmox/data.py          | 41 ++++++++++++++++++++++-------------------
 boxmox/experiment.py    |  6 +++---
 boxmox/plotter.py       |  2 +-
 examples/run_tests.bash | 27 +++++++++++++++++++++++++++
 setup.py                |  6 ++----
 9 files changed, 77 insertions(+), 35 deletions(-)
 create mode 100644 .gitignore
 create mode 100644 examples/run_tests.bash

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..881b75f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,7 @@
+boxmox.egg-info/dependency_links.txt
+boxmox.egg-info/entry_points.txt
+boxmox.egg-info/not-zip-safe
+boxmox.egg-info/PKG-INFO
+boxmox.egg-info/requires.txt
+boxmox.egg-info/SOURCES.txt
+boxmox.egg-info/top_level.txt
diff --git a/CHANGES.rst b/CHANGES.rst
index 83dc67b..16b0e2c 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,6 +1,11 @@
 Changelog
 =========
 
+1.1.0 (2020-09-16)
+------------------
+
+- Python 3 compatible 
+
 1.0.0 (2017-12-19)
 ------------------
 
diff --git a/boxmox/.gitignore b/boxmox/.gitignore
index 94d8735..3bc2d2d 100644
--- a/boxmox/.gitignore
+++ b/boxmox/.gitignore
@@ -1,2 +1,4 @@
 work
-work/*
\ No newline at end of file
+work/*
+**/*.pyc
+**/__pycache__
diff --git a/boxmox/__init__.py b/boxmox/__init__.py
index d0ebe91..2a50f12 100644
--- a/boxmox/__init__.py
+++ b/boxmox/__init__.py
@@ -1,25 +1,25 @@
 try:
-    from _site_specific import *
+    from ._site_specific import *
 except:
     pass
 
-import _installation
+from . import _installation
 
-from data import InputFile, InputFileOrig, InputFile17, Output, ConcentrationOutput, RatesOutput, AdjointOutput, JacobianOutput, HessianOutput
-from fluxes import FluxParser
+from .data import InputFile, InputFileOrig, InputFile17, Output, ConcentrationOutput, RatesOutput, AdjointOutput, JacobianOutput, HessianOutput
+from .fluxes import FluxParser
 
 work_path = _installation.validate()
 if work_path is None:
     import warnings
     warnings.warn("BOXMOX unusable - experiment execution disabled.")
 else:
-    from experiment import Experiment, ExperimentFromExample, ExperimentFromExistingRun, Namelist, examples, compiledMechs
+    from .experiment import Experiment, ExperimentFromExample, ExperimentFromExistingRun, Namelist, examples, compiledMechs
 
 try:
     import matplotlib
-    from plotter import ExperimentPlotter
+    from .plotter import ExperimentPlotter
 except:
     import warnings
     warnings.warn('matplotlib not found - plotting disabled.')
 
-import _console
\ No newline at end of file
+from . import _console
diff --git a/boxmox/data.py b/boxmox/data.py
index 8a875f8..3fd18ea 100644
--- a/boxmox/data.py
+++ b/boxmox/data.py
@@ -1,7 +1,10 @@
 import os
 import sys
 import shutil
-import StringIO
+try:
+    from StringIO import StringIO ## for Python 2
+except ImportError:
+    from io import StringIO ## for Python 3
 import csv
 
 import numpy as np
@@ -18,7 +21,7 @@ def _mygenfromtxt2(f):
         f.seek(curpos)
         spamreader = csv.reader(f, skipinitialspace = True, delimiter=" ")
     # twice as fast as np.genfromtxt(..., names=True)
-    hdr = spamreader.next()
+    hdr = next(spamreader)
     dat = []
     for row in spamreader:
         dat.append( tuple(map(float, row)) )
@@ -36,7 +39,7 @@ def _mygenfromtxt(f):
         f.seek(curpos)
         spamreader = csv.reader(f, skipinitialspace = True, delimiter=" ")
     # twice as fast as np.genfromtxt(..., names=True)
-    hdr = spamreader.next()
+    hdr = next(spamreader)
     dat = []
     for row in spamreader:
         dat.append( tuple(map(float, row)) )
@@ -50,7 +53,7 @@ def InputFile(fpath=None, version=1.7):
     '''
     if not fpath is None:
         # file format discovery: 3 lines with numbers ==> 1.7
-        with open(fpath, 'rb') as f:
+        with open(fpath, 'r') as f:
             one = f.readline()
             two = f.readline()
             tre = f.readline()
@@ -102,7 +105,7 @@ class InputFile17:
         '''
         Read input file from path.
         '''
-        with open(fpath, 'rb') as f:
+        with open(fpath, 'r') as f:
             nvar         = int(f.readline().replace(',', ''))
             nanc         = int(f.readline().replace(',', ''))
             self.timeFormat   = int(f.readline().replace(',', ''))
@@ -117,7 +120,7 @@ class InputFile17:
         self._data     = { hdr[i]: [ x[i] for x in dmp ] for i in range(ntime+nanc, len(dmp[0])) }
 
     def __str__(self):
-        f = StringIO.StringIO()
+        f = StringIO()
         self.write(f)
         return(f.getvalue())
 
@@ -170,10 +173,10 @@ class InputFile17:
         #: File path of the underlying file (if it exists (yet))
         self.fpath       = fpath
         if not self.fpath is None:
-            try:
-                self.read(self.fpath)
-            except Exception as e:
-                print("Reading input file {:s} failed: {:s}".format(self.fpath, str(e)))
+#            try:
+            self.read(self.fpath)
+#            except Exception as e:
+#                print("Reading input file {:s} failed: {:s}".format(self.fpath, str(e)))
 
 class InputFileOrig:
     '''
@@ -205,7 +208,7 @@ class InputFileOrig:
         '''
         Read input file from path.
         '''
-        with open(fpath, 'rb') as f:
+        with open(fpath, 'r') as f:
             self.nvar         = int(f.readline().replace(',', ''))
             self.timeFormat   = int(f.readline().replace(',', ''))
             dmp               = _mygenfromtxt(f)
@@ -215,7 +218,7 @@ class InputFileOrig:
         self._data     = { x: dmp[x] for x in dmp.dtype.names }
 
     def __str__(self):
-        f = StringIO.StringIO()
+        f = StringIO()
         self.write(f)
         return(f.getvalue())
 
@@ -279,7 +282,7 @@ class Output(object):
 
         :param File f: file object or stream where to write to, defaults to sysout.
         '''
-        with open(self.fpath, 'rb') as i:
+        with open(self.fpath, 'r') as i:
             for line in i:
                 f.write(line)
 
@@ -321,17 +324,17 @@ class ConcentrationOutput(Output):
             out = self.data
         return out
     def __str__(self):
-        fout = StringIO.StringIO()
-        with open(self.fpath, 'rb') as f:
+        fout = StringIO()
+        with open(self.fpath, 'r') as f:
             fout.write(f.read())
         return fout.getvalue()
 
     def __init__(self, fpath, vars=None):
         self.fpath = fpath
-        with open(fpath, 'rb') as f:
+        with open(fpath, 'r') as f:
             # not using mygenfromtxt as we know the file format and this is way faster...
             spamreader = csv.reader(f, skipinitialspace = True, delimiter=" ")
-            hdr = spamreader.next()
+            hdr = next(spamreader)
             dat = []
             for row in spamreader:
                 dat.append( tuple(map(float, row)) )
@@ -365,14 +368,14 @@ class RatesOutput(Output):
             #              line 2 ->  10001 10002 10003 10004  ...  20000
             #              line 3 ->  20001 20002 20003 20004  ...  30000
             #              and so on ...
-            rateFormat = num_of_reacs/10000 + 1
+            rateFormat = int(num_of_reacs/10000 + 1)
 
             for x in range( rateFormat ):
                 # Column names are not needed ...
                 _ = f.readline()
 
             # Length :: number_of_timesteps * rateFormat:
-            ratesRaw = [map(float, xline.strip().split()) for xline in f.readlines()]
+            ratesRaw = [list(map(float, xline.strip().split())) for xline in f.readlines()]
 
         # "Unfold" the raw data:
         ratesUnfold = ratesRaw[ ::rateFormat]
diff --git a/boxmox/experiment.py b/boxmox/experiment.py
index dd4e975..d785dbe 100644
--- a/boxmox/experiment.py
+++ b/boxmox/experiment.py
@@ -90,7 +90,7 @@ class Namelist:
     def __init__(self, path = None):
         self._namelist = None
 
-        firstExample = examples[examples.keys()[0]]
+        firstExample = examples[list(examples.keys())[0]]
         self.read(os.path.join(firstExample.path, "BOXMOX.nml"))
         if not path is None:
             if os.path.exists(path):
@@ -139,7 +139,7 @@ class Experiment:
         versionFile = os.path.join(path, 'VERSION')
         if os.path.exists(versionFile):
             try:
-                with open(versionFile, 'rb') as f:
+                with open(versionFile, 'r') as f:
                     line = f.readline().rstrip()
                     line = ".".join(line.split(".")[0:min(2, len(line.split(".")))])
                     # for development only:
@@ -230,7 +230,7 @@ class Experiment:
         self.namelist.write(os.path.join(self.path, 'BOXMOX.nml'))
 
         for type in self.input:
-            with open( os.path.join(self.path, type + '.csv'), 'wb') as f:
+            with open( os.path.join(self.path, type + '.csv'), 'w') as f:
                 self.input[type].write(f, version=self.version)
 
         pwd = os.getcwd()
diff --git a/boxmox/plotter.py b/boxmox/plotter.py
index ee01ac5..e57af61 100644
--- a/boxmox/plotter.py
+++ b/boxmox/plotter.py
@@ -19,7 +19,7 @@ class ExperimentPlotter:
 
         fig, ax = plt.subplots()
 
-        if isinstance(specs, (str, unicode)):
+        if isinstance(specs, (str)):
             specs = [ specs ]
 
         nspecs = len(specs)
diff --git a/examples/run_tests.bash b/examples/run_tests.bash
new file mode 100644
index 0000000..c540750
--- /dev/null
+++ b/examples/run_tests.bash
@@ -0,0 +1,27 @@
+export KPP_HOME=/Users/lechriso/git/boxmox/boxmox/        
+export PATH="$KPP_HOME/bin:$KPP_HOME/boxmox/bin:$PATH"
+export BOXMOX_WORK_PATH=/tmp/
+
+echo " - - - - "
+echo "PYTHON 2"
+echo 
+echo "simple.py"
+echo
+python2 simple.py
+echo 
+echo "intermediate.py"
+echo
+python2 intermediate.py
+echo
+
+echo " - - - - "
+echo "PYTHON 3"
+echo 
+echo "simple.py"
+echo
+python3 simple.py
+echo 
+echo "intermediate.py"
+echo
+python3 intermediate.py
+echo
diff --git a/setup.py b/setup.py
index 3db26b4..03f5695 100644
--- a/setup.py
+++ b/setup.py
@@ -8,7 +8,7 @@ def read(filename):
 setup(name='boxmox',
       description='Python wrapper for the chemical box model BOXMOX',
       long_description=read('README.rst') + '\n\n' + read('INSTALL.rst') + '\n\n' + read('CHANGES.rst'),
-      version='1.0.0',
+      version='1.1.0',
       url='https://boxmodeling.meteo.physik.uni-muenchen.de',
       author='Christoph Knote',
       author_email='christoph.knote@physik.uni-muenchen.de',
@@ -22,15 +22,13 @@ setup(name='boxmox',
             'Intended Audience :: Science/Research',
             'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
             'Operating System :: POSIX',
-            'Programming Language :: Python :: 2 :: Only',
+            'Programming Language :: Python',
             'Topic :: Education',
             'Topic :: Scientific/Engineering',
             'Topic :: Utilities'
         ],
       keywords='',
-      python_requires='<3',
       packages=['boxmox'],
-#      install_requires=['subprocess32;python_version<"3.0"', 'numpy', 'f90nml', 'pyparsing' ],
       install_requires=['numpy', 'f90nml', 'pyparsing' ],
       entry_points={
           'console_scripts':
-- 
GitLab