Repository: coin-or/pulp

Issue:
PuLP does not recognize PL bound type in .mps files
**System:**
MacBook (INTEL CPU), Ventura 13.2, Python 3.8
PuLP 2.7.0

From Wikipedia : 
[https://en.wikipedia.org/wiki/MPS_(format)](url)
`The optional BOUNDS section specifies lower and upper bounds on individual variables, if they are not given by rows in the matrix.  .... Bound type PL is for a free positive from zero to plus infinity, but as this is the normal default, it is seldom used.`

About 10% of models in MipLib2017 benchmark contain `PL Bound  variable` lines and PuLP throws an exception when running 
`var, prob = LpProblem.fromMPS(modelName)`

The fix appears to be easy: in mps_lp.py, under `readMPSSetBounds` add two lines.  There is no need to call `set_both_bounds` because the bounds are <0, infinity> which is the default

```
   if bound == "FR":
        set_both_bounds(None, None)
        return
    elif bound == "BV":
        set_both_bounds(0, 1)
        return
    # add these two lines
    elif bound == "PL":
        return
```


Discussion:

Patch:
diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml
index 1d7795e..4bb1240 100644
--- a/.github/workflows/pythonpackage.yml
+++ b/.github/workflows/pythonpackage.yml
@@ -10,6 +10,11 @@ jobs:
       matrix:
         python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12']
         os: [ubuntu-latest, macOS-latest, windows-latest]
+        exclude:
+          - os: macOS-latest
+            python-version: '3.7'
+          - os: macOS-latest
+            python-version: '3.8'
 
     steps:
       - uses: actions/checkout@v4
diff --git a/pulp/mps_lp.py b/pulp/mps_lp.py
index e5ef2be..37620f9 100644
--- a/pulp/mps_lp.py
+++ b/pulp/mps_lp.py
@@ -175,6 +175,10 @@ def readMPSSetBounds(line, variable_dict):
     elif bound == "BV":
         set_both_bounds(0, 1)
         return
+    elif bound == "PL":
+        # bounds equal to defaults
+        return
+
     value = float(line[3])
     if bound in ["LO", "UP"]:
         set_one_bound(bound, value)


Test patch:
diff --git a/.github/workflows/publish-to-test-pypi.yml b/.github/workflows/publish-to-test-pypi.yml
index baffec3..7d60960 100644
--- a/.github/workflows/publish-to-test-pypi.yml
+++ b/.github/workflows/publish-to-test-pypi.yml
@@ -20,7 +20,7 @@ jobs:
       uses: pypa/gh-action-pypi-publish@release/v1
       with:
         password: ${{ secrets.test_pypi_password }}
-        repository_url: https://test.pypi.org/legacy/
+        repository-url: https://test.pypi.org/legacy/
     - name: Publish distribution 📦 to PyPI
       if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
       uses: pypa/gh-action-pypi-publish@release/v1
diff --git a/pulp/tests/test_pulp.py b/pulp/tests/test_pulp.py
index 90f4007..76f5f92 100644
--- a/pulp/tests/test_pulp.py
+++ b/pulp/tests/test_pulp.py
@@ -42,6 +42,10 @@ BOUNDS
 ENDATA
 """
 
+EXAMPLE_MPS_PL_BOUNDS = EXAMPLE_MPS_RHS56.replace(
+    "LO BND1      YTWO                -1", "PL BND1      YTWO                  "
+)
+
 
 def gurobi_test(test_item):
     @functools.wraps(test_item)
@@ -1208,6 +1212,15 @@ class BaseSolverTest:
             os.unlink(h.name)
             self.assertEqual(problem.constraints["LIM2"].constant, -10)
 
+        def test_importMPS_PL_bound(self):
+            """Import MPS file with PL bound type."""
+            with tempfile.NamedTemporaryFile(delete=False) as h:
+                h.write(str.encode(EXAMPLE_MPS_PL_BOUNDS))
+            print("\t Testing reading MPS files - PL bound")
+            _, problem = LpProblem.fromMPS(h.name)
+            os.unlink(h.name)
+            self.assertIsInstance(problem, LpProblem)
+
         # def test_importMPS_2(self):
         #     name = self._testMethodName
         #     # filename = name + ".mps"