Repository: NREL/OpenStudio (language: python)

Issue:
Conversion to/from pathlib.Path in python bindings now rejects openstudio.Path
Issue overview
--------------

I messed something up in https://github.com/NREL/OpenStudio/pull/4917

## Current Behavior

openstudio.Path is not accepted anymore in python bindings.

## Expected Behavior
<!--- Tell us what should happen -->

## Steps to Reproduce

```python
In [1]: from pathlib import Path

In [2]: import openstudio

In [4]: m = openstudio.model.Model()

In [5]: m.save('model.osm', True)
Out[5]: True

In [6]: m.save(Path('model.osm'), True)
Out[6]: True

In [7]: m.save(openstudio.toPath('model.osm'), True)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[7], line 1
----> 1 m.save(openstudio.toPath('model.osm'), True)

File ~/Virtualenvs/py39/lib/python3.9/site-packages/openstudio/openstudioutilitiesidf.py:1997, in Workspace.save(self, p, overwrite)
   1996 def save(self, p, overwrite=False):
-> 1997     return _openstudioutilitiesidf.Workspace_save(self, p, overwrite)

TypeError: Wrong number or type of arguments for overloaded function 'Workspace_save'.
  Possible C/C++ prototypes are:
    openstudio::Workspace::save(openstudio::path const &,bool)
    openstudio::Workspace::save(openstudio::path const &)
```

## Possible Solution
<!--- Optional, but if you can, suggest a fix/reason for the bug, -->

## Details

### Environment

Some additional details about your environment for this issue (if relevant):
 - Platform (Operating system, version): any, python
 - Version of OpenStudio (if using an intermediate build, include SHA): 3.7.0

### Context

https://unmethours.com/question/98612/why-does-modelsavepath-fail-with-path-an-openstudiopath/

Conversion to/from pathlib.Path in python bindings now rejects openstudio.Path
Issue overview
--------------

I messed something up in https://github.com/NREL/OpenStudio/pull/4917

## Current Behavior

openstudio.Path is not accepted anymore in python bindings.

## Expected Behavior
<!--- Tell us what should happen -->

## Steps to Reproduce

```python
In [1]: from pathlib import Path

In [2]: import openstudio

In [4]: m = openstudio.model.Model()

In [5]: m.save('model.osm', True)
Out[5]: True

In [6]: m.save(Path('model.osm'), True)
Out[6]: True

In [7]: m.save(openstudio.toPath('model.osm'), True)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[7], line 1
----> 1 m.save(openstudio.toPath('model.osm'), True)

File ~/Virtualenvs/py39/lib/python3.9/site-packages/openstudio/openstudioutilitiesidf.py:1997, in Workspace.save(self, p, overwrite)
   1996 def save(self, p, overwrite=False):
-> 1997     return _openstudioutilitiesidf.Workspace_save(self, p, overwrite)

TypeError: Wrong number or type of arguments for overloaded function 'Workspace_save'.
  Possible C/C++ prototypes are:
    openstudio::Workspace::save(openstudio::path const &,bool)
    openstudio::Workspace::save(openstudio::path const &)
```

## Possible Solution
<!--- Optional, but if you can, suggest a fix/reason for the bug, -->

## Details

### Environment

Some additional details about your environment for this issue (if relevant):
 - Platform (Operating system, version): any, python
 - Version of OpenStudio (if using an intermediate build, include SHA): 3.7.0

### Context

https://unmethours.com/question/98612/why-does-modelsavepath-fail-with-path-an-openstudiopath/



Discussion / hints:

Fix patch:
diff --git a/src/utilities/core/Path.i b/src/utilities/core/Path.i
index a46b5a3f33c..2612669350b 100644
--- a/src/utilities/core/Path.i
+++ b/src/utilities/core/Path.i
@@ -312,7 +312,7 @@ namespace openstudio {
         }
     }
 
-    %typemap(in, fragment="SWIG_openstudio_path") (path) {
+    %typemap(in, fragment="SWIG_openstudio_path") path {
 
       // check if input is a path already
       void *vptr = 0;
@@ -326,13 +326,8 @@ namespace openstudio {
           SWIG_exception_fail(SWIG_ValueError, "Invalid null reference openstudio::path");
         }
       } else if(PyUnicode_Check($input)) {
-        // Python 3
         std::string s(PyUnicode_AsUTF8($input));
         $1 = openstudio::toPath(s);
-      } else if(PyString_Check($input)) {
-        // Python2, PyString_Check does PyBytes_Check
-        std::string s(PyString_AsString($input));
-        $1 = openstudio::toPath(s);
       } else if (isPathInstance($input)) {
         PyObject * str_obj = PyObject_Str($input);   // New reference
         const char * s = PyUnicode_AsUTF8(str_obj);  // This stores the UTF-8 representation buffer within str_obj
@@ -343,10 +338,10 @@ namespace openstudio {
       }
     }
 
-    %typemap(typecheck, precedence=SWIG_TYPECHECK_STRING, fragment="SWIG_openstudio_path") (path) {
-      bool stringOrPathlibType = PyString_Check($input) || PyUnicode_Check($input) || isPathInstance($input);
+    %typemap(typecheck, precedence=SWIG_TYPECHECK_STRING, fragment="SWIG_openstudio_path") path {
+      bool stringOrPathlibType = PyUnicode_Check($input) || isPathInstance($input);
       bool pathType = false;
-      if (stringOrPathlibType){
+      if (!stringOrPathlibType){
         void *vptr = 0;
         int res = SWIG_ConvertPtr($input, &vptr, $&1_descriptor, 0);
         pathType = (SWIG_IsOK(res) && (vptr != 0));
@@ -372,7 +367,7 @@ namespace openstudio {
     %apply path { const path };
 
     // handle const path& separately
-    %typemap(in, fragment="SWIG_openstudio_path") (const path&) {
+    %typemap(in, fragment="SWIG_openstudio_path") const path& {
       $1=NULL;
 
       // check if input is a path already
@@ -387,13 +382,8 @@ namespace openstudio {
           SWIG_exception_fail(SWIG_ValueError, "Invalid null reference openstudio::path const &");
         }
       } else if(PyUnicode_Check($input)) {
-        // Python 3
         std::string s(PyUnicode_AsUTF8($input));
         $1 = new openstudio::path(openstudio::toPath(s));
-      } else if(PyString_Check($input)) {
-        // Python2, PyString_Check does PyBytes_Check
-        std::string s(PyString_AsString($input));
-        $1 = new openstudio::path(openstudio::toPath(s));
       } else if (isPathInstance($input)) {
         PyObject * str_obj = PyObject_Str($input);   // New reference
         const char * s = PyUnicode_AsUTF8(str_obj);
@@ -404,10 +394,10 @@ namespace openstudio {
       }
     }
 
-    %typemap(typecheck, precedence=SWIG_TYPECHECK_STRING, fragment="SWIG_openstudio_path") (const path&) {
-      bool stringOrPathlibType = PyString_Check($input) || PyUnicode_Check($input) || isPathInstance($input);
+    %typemap(typecheck, precedence=SWIG_TYPECHECK_STRING, fragment="SWIG_openstudio_path") const path& {
+      bool stringOrPathlibType = PyUnicode_Check($input) || isPathInstance($input);
       bool pathType = false;
-      if (stringOrPathlibType) {
+      if (!stringOrPathlibType) {
         void *vptr = 0;
         int res = SWIG_ConvertPtr($input, &vptr, $1_descriptor, 0);
         pathType = (SWIG_IsOK(res) && (vptr != 0));
@@ -415,8 +405,8 @@ namespace openstudio {
       $1 = (stringOrPathlibType || pathType) ? 1 : 0;
     }
 
-    %typemap(freearg) (const path&) {
-      if ($1){
+    %typemap(freearg) const path& {
+      if ($1) {
         delete $1;
       }
     }


Test patch:
diff --git a/python/test/test_usability.py b/python/test/test_usability.py
index 9d2ccb91c25..7d26e325ce5 100644
--- a/python/test/test_usability.py
+++ b/python/test/test_usability.py
@@ -21,7 +21,8 @@ def test_path():
     """Assert you can load a model with a str or a pathlib.Path without throwing."""
     assert openstudio.model.Model.load("wrong.osm").empty()
     assert openstudio.model.Model.load(Path("wrong.osm")).empty()
-
+    # And we still support toPath
+    assert openstudio.model.Model.load(openstudio.toPath("wrong.osm")).empty()
 
 def test_json():
     """We can return jsoncpp objects to a native python dict."""