... Python OpenStudio bindings: surface.vertices() -> tuple?
To OpenStudio SDK devs working with Python bindings ...
As a Python-SDK training exercise, I'm in the process of converting existing Ruby gems to Python packages/modules. It's going fairly smoothly (e.g. Ruby vs Python unit test results are lining up). Yet I'm hitting something weird when I try the following (here, using the interactive Python interpreter):
vtx = openstudio.Point3dVector()
vtx.append(openstudio.Point3d( 0, 0,10))
vtx.append(openstudio.Point3d( 0, 0, 0))
vtx.append(openstudio.Point3d(10, 0, 0))
vtx.append(openstudio.Point3d(10, 0,10))
model = openstudio.model.Model()
surface = openstudio.model.Surface(vtx, model)
print(surface.vertices().__class__.__name__)
>>> tuple
Tuple? Shouldn't it be an openstudio.model.Point3dVector? I can work around this, e.g. converting to a list, or generating a new Point3dVector (point by point). But it would be better to stick to SDK documented return types. This is the first snag of this nature I've come across.
Is this a bug, or expected behaviour? Am I missing something really obvious (documented somewhere)? I haven't found anything on UMH or the OpenStudio GitHub repo. Working with SDK v3.9.0 & Python 3.10, BTW.
Thanks in advance,
EDIT:
The following isn't really answering "why?" or "where can one find documentation on this?". If someone does provide a suitable answer, I'll gladly accept it, and edit the original question with my comments below.
In a nutshell, an OpenStudio getter method returns a copy of an OpenStudio model object. Lower-level objects, like 3D points or materials, are returned as other instances of the same class. 1D collections of objects (e.g. floor.vertices(), model.getSpaces()) are also returned as copies. Yet often as simpler, built-in class instances, like Python tuples or Ruby Arrays. I've only come across this with 1D collections, when using either Python or Ruby. There's nothing inherently wrong with this, but it isn't initially obvious. I'd suggest relying on the online OpenStudio SDK documentation with a pinch of caution, and test return variables of interest (particularly 1D collections) when coding (via interactive shells, unit tests, etc.). More of a head's up for beginners ...
... a few observations:
#1. This is not unique to Python. The Ruby bindings offer similar behaviour. I just hadn't paid attention to this until recently - my bad. Revisiting the example in the question:
Python:
import openstudio
vx1 = openstudio.Point3dVector()
vx1.append(openstudio.Point3d(10, 0, 0))
vx1.append(openstudio.Point3d(10,10, 0))
vx1.append(openstudio.Point3d( 0,10, 0))
vx1.append(openstudio.Point3d( 0, 0, 0))
vx2 = openstudio.Point3dVector()
vx2.append(openstudio.Point3d(20, 0, 0))
vx2.append(openstudio.Point3d(20,10, 0))
vx2.append(openstudio.Point3d(10,10, 0))
vx2.append(openstudio.Point3d(10, 0, 0))
vtx = openstudio.Point3dVectorVector()
vtx.append(vx1)
vtx.append(vx2)
print(len(vtx))
> 2
print(vtx.__class__.__name__)
> Point3dVectorVector
print(vtx[0].__class__.__name__)
> tuple
print(vtx[0][0].__class__.__name__)
> Point3d
Ruby ...


