Skip to content

Support for Introspection API's#339

Open
abhilash-kumar-nair wants to merge 4 commits into
masterfrom
akn/feat/modeling-server-support
Open

Support for Introspection API's#339
abhilash-kumar-nair wants to merge 4 commits into
masterfrom
akn/feat/modeling-server-support

Conversation

@abhilash-kumar-nair
Copy link
Copy Markdown
Contributor

@abhilash-kumar-nair abhilash-kumar-nair commented May 20, 2026

This PR adds support for introspecting the Model source, Model parameters, Top level packages and recursively look into their subpackages/models.

# Explore packages
print("Top level packages")
for package in ws.get_packages():
    print(f"  {package.name}")
    if package.name == "Unnamed":
        print("  Sub-packages:", package.get_sub_packages())
        print("  Sub-models:", package.get_sub_models())

# Inspect Model
model = ws.get_model("Unnamed.test")
print("Modelica source code")
print(model.get_source())

print("Model parameters")
print(model.get_parameters())

@abhilash-kumar-nair abhilash-kumar-nair force-pushed the akn/feat/modeling-server-support branch from bd92a0e to 06b6caf Compare May 21, 2026 04:48
@abhilash-kumar-nair abhilash-kumar-nair changed the title Akn/feat/modeling server support Support for Introspection API's May 21, 2026
Copy link
Copy Markdown
Contributor

@efredriksson-modelon efredriksson-modelon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks really cool 🧇

I think the main thing is to have get_model on the modeling_session object and accept that workspace.get_model().get_source() will not work. Otherwise there are too many ways you can create issues and this also streamlines the design significantly.

I also wonder about the async vs sync websocket client connection, which might be good to take a look at.

self, uri: URI, http_client: HTTPClient, ws_client: SyncWebSocketClient
):
self._base_uri = uri
self._http_client = http_client
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not used and can be removed?

Comment thread pyproject.toml

[tool.poetry.dependencies]
python = "^3.8.0"
python = ">=3.9.0,<3.12.0"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the upper limit should be 4.0. Adding the 3.12 limit can cause a lot of issues for downstream tooling.

Comment on lines +659 to +682
def __del__(self) -> None:
if self._modeling_session is not None:
self._modeling_session.close_nowait()
self._modeling_session = None

def close(self) -> None:
"""Close the modeling session WebSocket connection, if one was opened."""
if self._modeling_session is not None:
self._modeling_session.close()
self._modeling_session = None

def __enter__(self) -> "Workspace":
return self

def __exit__(self, *args: object) -> None:
self.close()

def _get_modeling_session(self) -> ModelingSession:
if self._modeling_session is None:
modeling_service = self._sal.start_modeling_session(workspace_id=self.id)
self._modeling_session = ModelingSession(
modeling_service.id, self.id, modeling_service, self._sal
)
return self._modeling_session
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This machinery adds a lot of hidden state and makes it easy for a user to setup things so things becomes unstable. I think that we should drop it and accept that the get_model on workspace does not have full functionality. This seems pretty minor given the reduced complexity.

)

@Experimental
def get_parameters(self) -> List[str]:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this returning the parameter names? If we want to add things in the future (start value/nominals/...) it might we worth return list[ModelParameter] where ModelParameter have the attribute name.

return self._modeling_sal.close_session_nowait()

@Experimental
def get_model_parmeters(self, modelica_path: str) -> List[str]:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here on list[str]. But should we even have these methods? I would have expected something like:

model_session.get_model(class_path).get_parameters()
model_session.get_model(class_path).get_source()

so we keep the API clean and give the user one way to do this.

"""Returns the libraries the package has dependencies on."""
return self._info()["uses"]

def get_sub_models(self) -> List[Model]:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think sub_models are a thing. I think this should just be called get_models.


@Experimental
@contextmanager
def start_modeling_session(self) -> Generator["ModelingSession", None, None]:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

start_modeling_session -> new_modeling_session might read a little nicer and match our other naming, but feel free to ignore this.

Comment on lines +64 to +67
_is_local_dev = (
os.environ.get("IMPACT_PYTHON_CLIENT_DEV", "false").lower() == "true"
)
scheme = "ws" if _is_local_dev else "wss"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to keep this in?

Comment on lines +39 to +42
self._conn = await websockets.connect(
self._uri,
additional_headers=headers,
) # type: ignore
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might be missing something here but you are creating an async connection and then you want to wrap it in a sync API? I have not tested it, but could we not use websockets.sync.client.connect directly?

Comment on lines +422 to +423
"This Model instance has no modeling session. "
"Obtain the model via workspace.get_model() to enable get_parameters()."
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Obtain the model via modeling_session.get_model, see workspace.new_model_session is what this would say.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants