Repository: Pyomo/pyomo

Issue:
Add Python 3.11 Support
Test Pyomo against Python 3.11.


Discussion:

Resolving patch:
diff --git a/pyomo/common/config.py b/pyomo/common/config.py
--- a/pyomo/common/config.py
+++ b/pyomo/common/config.py
@@ -1285,12 +1285,7 @@ def __getstate__(self):
         # can allocate the state dictionary.  If it is not, then we call
         # the super-class's __getstate__ (since that class is NOT
         # 'object').
-        _base = super()
-        if hasattr(_base, '__getstate__'):
-            state = _base.__getstate__()
-        else:
-            state = {}
-        state.update((key, getattr(self, key)) for key in ConfigBase.__slots__)
+        state = {key: getattr(self, key) for key in ConfigBase.__slots__}
         state['_domain'] = _picklable(state['_domain'], self)
         state['_parent'] = None
         return state
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -199,6 +199,7 @@ def _print_deps(self, deplist):
         'Programming Language :: Python :: 3.8',
         'Programming Language :: Python :: 3.9',
         'Programming Language :: Python :: 3.10',
+        'Programming Language :: Python :: 3.11',
         'Programming Language :: Python :: Implementation :: CPython',
         'Programming Language :: Python :: Implementation :: PyPy',
         'Topic :: Scientific/Engineering :: Mathematics',
@@ -250,7 +251,10 @@ def _print_deps(self, deplist):
             # The following optional dependencies are difficult to
             # install on PyPy (binary wheels are not available), so we
             # will only "require" them on other (CPython) platforms:
-            'casadi; implementation_name!="pypy"',  # dae
+            #
+            # DAE can use casadi; as of 1 Nov 22, casadi has not been
+            # released for Python 3.11
+            'casadi; implementation_name!="pypy" and python_version<"3.11"',
             'numdifftools; implementation_name!="pypy"', # pynumero
             'pandas; implementation_name!="pypy"',
             'seaborn; implementation_name!="pypy"',   # parmest.graphics


Test patch:
diff --git a/.github/workflows/test_branches.yml b/.github/workflows/test_branches.yml
--- a/.github/workflows/test_branches.yml
+++ b/.github/workflows/test_branches.yml
@@ -31,23 +31,23 @@ jobs:
       fail-fast: false
       matrix:
         os: [ubuntu-latest]
-        python: ['3.10']
+        python: ['3.11']
         other: [""]
         category: [""]
 
         include:
         - os: ubuntu-latest
-          python: '3.10'
+          python: '3.11'
           TARGET: linux
           PYENV: pip
 
         - os: macos-latest
-          python: 3.8
+          python: '3.10'
           TARGET: osx
           PYENV: pip
 
         - os: windows-latest
-          python: 3.7
+          python: 3.9
           TARGET: win
           PYENV: conda
           PACKAGES: glpk
@@ -78,7 +78,7 @@ jobs:
           PYENV: pip
 
         - os: ubuntu-latest
-          python: 3.8
+          python: '3.10'
           other: /cython
           setup_options: --with-cython
           skip_doctest: 1
diff --git a/.github/workflows/test_pr_and_main.yml b/.github/workflows/test_pr_and_main.yml
--- a/.github/workflows/test_pr_and_main.yml
+++ b/.github/workflows/test_pr_and_main.yml
@@ -34,7 +34,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [ubuntu-latest, macos-latest, windows-latest]
-        python: [3.7, 3.8, 3.9, '3.10']
+        python: [3.7, 3.8, 3.9, '3.10', '3.11']
         other: [""]
         category: [""]
 
@@ -78,7 +78,7 @@ jobs:
           PYENV: pip
 
         - os: ubuntu-latest
-          python: 3.8
+          python: '3.10'
           other: /cython
           setup_options: --with-cython
           skip_doctest: 1
@@ -114,6 +114,11 @@ jobs:
           TARGET: win
           PYENV: pip
 
+        exclude:
+        # Exclude 3.11 on conda until matplotlib!=3.6.1 is released on
+        # conda-forge for python 3.11
+        - {os: windows-latest, python: 3.11}
+
     steps:
     - name: Checkout Pyomo source
       uses: actions/checkout@v3
diff --git a/pyomo/core/tests/unit/test_disable_methods.py b/pyomo/core/tests/unit/test_disable_methods.py
--- a/pyomo/core/tests/unit/test_disable_methods.py
+++ b/pyomo/core/tests/unit/test_disable_methods.py
@@ -150,13 +150,15 @@ def test_disable(self):
                 RuntimeError, "Cannot access property 'g' on _abstract_simple "
                 "'foo' before it has been constructed"):
             x.g
-        with self.assertRaisesRegex(AttributeError, "can't set attribute"):
+        with self.assertRaisesRegex(
+                AttributeError, "(can't set attribute)|(object has no setter)"):
             x.g = 1
         with self.assertRaisesRegex(
                 RuntimeError, "Cannot custom_pmsg _abstract_simple "
                 "'foo' before it has been constructed"):
             x.h
-        with self.assertRaisesRegex(AttributeError, "can't set attribute"):
+        with self.assertRaisesRegex(
+                AttributeError, "(can't set attribute)|(object has no setter)"):
             x.h = 1
 
         self.assertEqual(x.construct(), 'construct')