Repository: USEPA/WNTR (language: python)

Issue:
Please add Python 3.10 support
Please add Python 3.10 support. Thanks.


Discussion / hints:
As reported in #194, it is possible to generate the necessary library for Python 3.10 in the sources of the latest release:
1. set `use_swig` and `build` to True in setup.py
2. execute the command `python setup.py build_ext --inplace`
3. use pip to install your local copy of wntr with `pip install --user -e [path to wntr]`


Fix patch:
diff --git a/.github/workflows/quick_check.yml b/.github/workflows/quick_check.yml
index 70d94b1f6..1eaa6e147 100644
--- a/.github/workflows/quick_check.yml
+++ b/.github/workflows/quick_check.yml
@@ -13,19 +13,28 @@ on:
 
 jobs:
   test:
+    strategy:
+      matrix:
+        python-version: ['3.8', '3.10']
     runs-on: ubuntu-latest
     steps:
     - uses: actions/checkout@v2
     - name: Set up Python ${{ matrix.python-version }}
-      uses: actions/setup-python@v2
+      uses: actions/setup-python@v4
       with:
-        python-version: 3.8
+        python-version: ${{ matrix.python-version }}
     - name: Install packages
       run: |
         python -m pip install --upgrade pip
         pip install -r requirements.txt
-        pip install --upgrade coverage nose
+        pip install --upgrade coverage pytest
+    # Build extensions are accessible from package level, similar to python setup.py build_ext --inplace
+    - name: Install package for development
+      run: |
         python setup.py develop
-    - name: Run tests
+    - name: Run tests and coverage (unittests plus doctests)
       run: |
-        nosetests -v --nologcapture --with-doctest --exclude=test_examples --with-coverage --cover-package=wntr --cover-min-percentage=70 wntr
+        coverage run --source=wntr --omit="*/tests/*" -m pytest -m "not time_consuming" --doctest-modules --doctest-glob="*.rst" wntr        
+        coverage report --fail-under=70        
+    # coverage run --source=wntr --omit="*/tests/*" --append -m pytest --doctest-glob="*.rst" documentation 
+
diff --git a/.gitignore b/.gitignore
index ee22f7a2a..b3b727b9d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,8 @@
 _build/
 build/
 wntr.egg-info/
+dist/
+docker/
 
 *.pyc
 *.egg
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 000000000..fc238db0a
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,7 @@
+[build-system]
+requires = ["setuptools", "numpy"]
+build-backend = "setuptools.build_meta"
+
+[tool.pytest.ini_options]
+markers = ["time_consuming:excluded from ci quick test"]
+addopts = "--doctest-modules"
diff --git a/requirements.txt b/requirements.txt
index bb344fc46..acbb38847 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -12,8 +12,10 @@ utm
 openpyxl
 
 # Testing
-nose
+# nose
+pytest
 ipykernel
 nbformat
 nbconvert
- 
+coverage
+sphinx_rtd_theme
diff --git a/setup.py b/setup.py
index 8dfe90fa7..78cffb243 100644
--- a/setup.py
+++ b/setup.py
@@ -1,15 +1,21 @@
 from setuptools import setup, find_packages
 from setuptools.extension import Extension
-import numpy
 import os
 import re
+import sys
 
 use_swig = False
-build = False
+build = True
 
 extension_modules = list()
 
+# if sys.version_info.major >= 3 and sys.version_info.minor >= 10:
+#     print("Python version >= 3.10.x")
+#     build = True
+
 if build:
+    import numpy
+
     try:
         numpy_include = numpy.get_include()
     except AttributeError:
@@ -84,6 +90,8 @@
     else:
         raise RuntimeError("Unable to find version string.")
 
+print(extension_modules)
+
 setup(name=DISTNAME,
       version=VERSION,
       packages=PACKAGES,


Test patch:
diff --git a/.github/workflows/build_tests.yml b/.github/workflows/build_tests.yml
index db92a34aa..532adec37 100644
--- a/.github/workflows/build_tests.yml
+++ b/.github/workflows/build_tests.yml
@@ -1,4 +1,4 @@
-# This workflow will install Python dependencies, run tests and lint with a single version of Python
+# This workflow will install Python dependencies, run tests and lint with all currently supported python versions on all major plattforms
 # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
 
 name: build
@@ -12,13 +12,19 @@ on:
 jobs:
 
   build:
-    runs-on: ubuntu-latest
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        # python-version: ['3.7', '3.10']
+        # os: [ubuntu-latest]
+        python-version: ['3.7', '3.8', '3.9', '3.10']
+        os: [windows-latest, macOS-latest, ubuntu-latest] 
     steps:
     - uses: actions/checkout@v2
     - name: Set up Python
-      uses: actions/setup-python@v2
+      uses: actions/setup-python@v4
       with:
-        python-version: 3.8
+        python-version: ${{ matrix.python-version }}
     - name: Install dependencies
       run: |
         python --version
@@ -30,47 +36,53 @@ jobs:
         python setup.py bdist_wheel
         ls dist/*
     - name: Save wheel
-      uses: actions/upload-artifact@v2
+      uses: actions/upload-artifact@v3
       with:
-        name: wheel
-        path: dist/wntr*.whl
+        name: wntr_${{ matrix.python-version }}_${{ matrix.os }}.whl
+        path: dist/wntr*
 
   test:
-    name: Import test
+    name: Test install and usage of wntr
     needs: build
     runs-on: ${{ matrix.os }}
     strategy:
       matrix:
-        python-version: [3.6, 3.7, 3.8, 3.9]
+        # python-version: ['3.7', '3.10']
+        # os: [ubuntu-latest]
+        python-version: ['3.7', '3.8', '3.9', '3.10']
         os: [windows-latest, macOS-latest, ubuntu-latest]    
     steps:
     - name: Set up Python 
-      uses: actions/setup-python@v2
+      uses: actions/setup-python@v4
       with:
         python-version: ${{ matrix.python-version }}
     - name: Download wheel
-      uses: actions/download-artifact@v2
+      uses: actions/download-artifact@v3
       with:
-        name: wheel
+        name: wntr_${{ matrix.python-version }}_${{ matrix.os }}.whl
+    # - name: Discover
+    #   run: |
+    #     ls .
     - name: Install wntr
       run: |
         python -m pip install --upgrade pip
         pip install wheel
         pip install --find-links=. wntr
-    - name: Import test
+    - name: Usage of wntr
       run: |
         python -c "import wntr"
 
-  windows:
+  create_coverage_reports:
     runs-on: ${{ matrix.os }}
     strategy:
       matrix:
-        python-version: [3.6, 3.7, 3.8, 3.9]
-        os: [windows-latest]
+        python-version: ['3.7', '3.10']
+        # os: [ubuntu-latest]
+        os: [windows-latest, macOS-latest, ubuntu-latest]
     steps:
     - uses: actions/checkout@v2
     - name: Set up Python ${{ matrix.python-version }}
-      uses: actions/setup-python@v2
+      uses: actions/setup-python@v4
       with:
         python-version: ${{ matrix.python-version }}
     - name: Install dependencies
@@ -78,101 +90,36 @@ jobs:
         python --version
         python -m pip install --upgrade pip
         pip install -r requirements.txt
-        pip install coveralls
         python setup.py develop
+    # pip install coveralls
     - name: Run Tests
       run: |
         coverage erase
-        coverage run --context=${{ matrix.os }}.py${{ matrix.python-version }} --source=wntr --omit="*/tests/*","*/aml/evaluator.py","*/network_isolation/network_isolation.py" -m nose -v --nologcapture --with-doctest --exclude=test_examples --traverse-namespace wntr
+        coverage run --context=${{ matrix.os }}.py${{ matrix.python-version }} --source=wntr --omit="*/tests/*" -m pytest  --doctest-modules --doctest-glob="*.rst" wntr
+        coverage run --context=${{ matrix.os }}.py${{ matrix.python-version }} --source=wntr --omit="*/tests/*" --append -m pytest --doctest-glob="*.rst" documentation
       env:
         COVERAGE_FILE: .coverage.${{ matrix.python-version }}.${{ matrix.os }}
     - name: Save coverage
       uses: actions/upload-artifact@v2
-      # if: ${{ matrix.os != 'windows-latest' }}
       with:
         name: coverage
         path: .coverage.${{ matrix.python-version }}.${{ matrix.os }}
 
-  macOS:
-    runs-on: ${{ matrix.os }}
-    strategy:
-      matrix:
-        python-version: [3.6, 3.7, 3.8, 3.9]
-        os: [macOS-latest]
-    steps:
-    - uses: actions/checkout@v2
-    - name: Set up Python ${{ matrix.python-version }}
-      uses: actions/setup-python@v2
-      with:
-        python-version: ${{ matrix.python-version }}
-    - name: Install dependencies
-      run: |
-        python --version
-        python -m pip install --upgrade pip
-        pip install -r requirements.txt
-        pip install coveralls
-        python setup.py develop
-    - name: Run Tests
-      run: |
-        coverage erase
-        coverage run --context=${{ matrix.os }}.py${{ matrix.python-version }} --source=wntr --omit="*/tests/*","*/aml/evaluator.py","*/network_isolation/network_isolation.py" -m nose -v --nologcapture --with-doctest --exclude=test_examples --traverse-namespace --doctest-extension=.rst documentation/*.rst wntr
-      env:
-        COVERAGE_FILE: .coverage.${{ matrix.python-version }}.${{ matrix.os }}
-    - name: Save coverage
-      uses: actions/upload-artifact@v2
-      # if: ${{ matrix.os != 'windows-latest' }}
-      with:
-        name: coverage
-        path: .coverage.${{ matrix.python-version }}.${{ matrix.os }}
-
-  linux:
-    runs-on: ${{ matrix.os }}
-    strategy:
-      matrix:
-        python-version: [3.7, 3.8, 3.9]
-        os: [ubuntu-latest]
-    steps:
-    - uses: actions/checkout@v2
-    - name: Setup Python
-      uses: actions/setup-python@v2
-      with:
-        update-conda: true
-        python-version: ${{ matrix.python-version }}
-    - name: Install dependencies
-      run: |
-        python --version
-        python -m pip install --upgrade pip
-        pip install -r requirements.txt
-        pip install coveralls
-        python setup.py develop
-    - name: Run Tests
-      run: |
-        coverage erase
-        coverage run --context=${{ matrix.os }}.py${{ matrix.python-version }} --source=wntr --omit="*/tests/*","*/aml/evaluator.py","*/network_isolation/network_isolation.py" -m nose -v --nologcapture --with-doctest --doctest-extension=.rst --traverse-namespace documentation/*.rst wntr
-      env:
-        COVERAGE_FILE: .coverage.${{ matrix.python-version }}.${{ matrix.os }}
-    - name: Save coverage
-      uses: actions/upload-artifact@v2
-      # if: ${{ matrix.os != 'windows-latest' }}
-      with:
-        name: coverage
-        path: .coverage.${{ matrix.python-version }}.${{ matrix.os }}
-
-  coverage:
-    needs: [ windows, macOS, linux ]
-    runs-on: windows-latest
+  combine_reports:
+    needs: [ create_coverage_reports ]
+    runs-on: ubuntu-latest
     steps:
     - name: Set up Python 
-      uses: actions/setup-python@v2
+      uses: actions/setup-python@v4
       with:
-        python-version: 3.8
+        python-version: 3.9
     - uses: actions/checkout@v2
     - name: Install coverage
       run: |
         python -m pip install --upgrade pip
         pip install -r requirements.txt
-        pip install coveralls
         python setup.py develop
+    # pip install coveralls
     - name: Download coverage artifacts from test matrix
       uses: actions/download-artifact@v2
       with:
@@ -203,15 +150,15 @@ jobs:
         name: coverage
         path: htmlcov
 
-  coveralls:
-    needs: [ windows, macOS, linux ]
+  combine_reports_upload_coveralls:
+    needs: [ create_coverage_reports ]
     runs-on: ubuntu-latest
     continue-on-error: true
     steps:
     - name: Set up Python 
-      uses: actions/setup-python@v2
+      uses: actions/setup-python@v4
       with:
-        python-version: 3.8
+        python-version: 3.9
     - uses: actions/checkout@v2
     - name: Install coverage
       run: |
diff --git a/wntr/tests/test_demos.py b/wntr/tests/test_demos.py
index 2a1ac8bfe..1028c857a 100644
--- a/wntr/tests/test_demos.py
+++ b/wntr/tests/test_demos.py
@@ -1,6 +1,7 @@
 import os
 import sys
 import unittest
+import pytest
 from os import listdir
 from os.path import abspath, dirname, isfile, join
 import nbformat
@@ -20,7 +21,8 @@ def setUpClass(self):
     @classmethod
     def tearDownClass(self):
         pass
-
+    
+    @pytest.mark.time_consuming
     def test_that_examples_run(self):
         cwd = os.getcwd()
         os.chdir(examplesdir)
diff --git a/wntr/tests/test_examples.py b/wntr/tests/test_examples.py
index 8ece0b444..bc33edabb 100644
--- a/wntr/tests/test_examples.py
+++ b/wntr/tests/test_examples.py
@@ -2,6 +2,7 @@
 import os
 import sys
 import unittest
+import pytest
 from os import listdir
 from os.path import abspath, dirname, isfile, join
 from subprocess import call
@@ -20,7 +21,8 @@ def setUpClass(self):
     @classmethod
     def tearDownClass(self):
         pass
-
+    
+    @pytest.mark.time_consuming
     def test_that_examples_run(self):
         cwd = os.getcwd()
         os.chdir(examplesdir)