From 148d80bfeea2e8df51afd610b8b156234d62cdef Mon Sep 17 00:00:00 2001 From: syntron Date: Sat, 28 Jun 2025 20:44:15 +0200 Subject: [PATCH 1/9] [OMCPath] add class --- OMPython/OMCSession.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/OMPython/OMCSession.py b/OMPython/OMCSession.py index ac99dc05..41a96a67 100644 --- a/OMPython/OMCSession.py +++ b/OMPython/OMCSession.py @@ -268,6 +268,31 @@ def getClassNames(self, className=None, recursive=False, qualified=False, sort=F return self._ask(question='getClassNames', opt=opt) +class OMCPath(pathlib.PurePosixPath): + """ + Implementation of a basic Path object which uses OMC as backend. The connection to OMC is provided via a + OMCSessionZMQ session object. + """ + + def __init__(self, *path, session: OMCSessionZMQ): + super().__init__(*path) + self._session = session + + def with_segments(self, *pathsegments): + # overwrite this function of PurePosixPath to ensure session is set + return type(self)(*pathsegments, session=self._session) + + # TODO: implement needed methods from pathlib._abc.PathBase: + # is_dir() + # is_file() + # read_text() + binary()? + # write_text() + binary()? + # unlink() + # resolve() + # ... more ... + # ??? test if local (write OMC => READ local and the other way) and use shortcuts ??? + + class OMCSessionZMQ: def __init__( @@ -322,6 +347,9 @@ def __del__(self): self.omc_zmq = None + def omcpath(self, *path) -> OMCPath: + return OMCPath(*path, session=self) + def execute(self, command: str): warnings.warn("This function is depreciated and will be removed in future versions; " "please use sendExpression() instead", DeprecationWarning, stacklevel=2) From 9a9e7655bf79aaa8f0d10ba3a38081725d21cfbf Mon Sep 17 00:00:00 2001 From: syntron Date: Fri, 27 Jun 2025 22:53:08 +0200 Subject: [PATCH 2/9] [OMCPath] add implementation using OMC via sendExpression() --- OMPython/OMCSession.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/OMPython/OMCSession.py b/OMPython/OMCSession.py index 41a96a67..fb865ba4 100644 --- a/OMPython/OMCSession.py +++ b/OMPython/OMCSession.py @@ -282,12 +282,27 @@ def with_segments(self, *pathsegments): # overwrite this function of PurePosixPath to ensure session is set return type(self)(*pathsegments, session=self._session) + def is_file(self) -> bool: + return self._session.sendExpression(f'regularFileExists("{self.as_posix()}")') + + def is_dir(self) -> bool: + return self._session.sendExpression(f'directoryExists("{self.as_posix()}")') + + def read_text(self) -> str: + return self._session.sendExpression(f'readFile("{self.as_posix()}")') + + def write_text(self, data: str) -> bool: + return self._session.sendExpression(f'writeFile("{self.as_posix()}", "{data}", false)') + + def unlink(self) -> bool: + return self._session.sendExpression(f'deleteFile("{self.as_posix()}")') + # TODO: implement needed methods from pathlib._abc.PathBase: - # is_dir() - # is_file() - # read_text() + binary()? - # write_text() + binary()? - # unlink() + # OK - is_dir() + # OK - is_file() + # OK - read_text() + binary()? + # OK - write_text() + binary()? + # OK - unlink() # resolve() # ... more ... # ??? test if local (write OMC => READ local and the other way) and use shortcuts ??? From 7a9175a1fe5a61a7405daa50d3b88e666299a8d0 Mon Sep 17 00:00:00 2001 From: syntron Date: Sat, 28 Jun 2025 20:47:05 +0200 Subject: [PATCH 3/9] [OMCPath] add pytest (only docker at the moment) --- tests/test_OMCPath.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 tests/test_OMCPath.py diff --git a/tests/test_OMCPath.py b/tests/test_OMCPath.py new file mode 100644 index 00000000..106f1cc7 --- /dev/null +++ b/tests/test_OMCPath.py @@ -0,0 +1,26 @@ +import OMPython + + +def test_OMCPath_docker(): + omcp = OMPython.OMCProcessDocker(docker="openmodelica/openmodelica:v1.25.0-minimal") + om = OMPython.OMCSessionZMQ(omc_process=omcp) + assert om.sendExpression("getVersion()") == "OpenModelica 1.25.0" + + p1 = om.omcpath('/tmp') + assert str(p1) == "/tmp" + p2 = p1 / 'test.txt' + assert str(p2) == "/tmp/test.txt" + assert p2.write_text('test') + assert p2.read_text() == "test" + assert p2.is_file() + assert p2.parent.is_dir() + assert p2.unlink() + assert p2.is_file() == False + + del omcp + del om + + +if __name__ == '__main__': + test_OMCPath_docker() + print('DONE') \ No newline at end of file From 88d05318968b0d40f0d3b4e32cf4cf6c9ad4cb87 Mon Sep 17 00:00:00 2001 From: syntron Date: Sat, 28 Jun 2025 20:47:15 +0200 Subject: [PATCH 4/9] [OMCPath] TODO items --- OMPython/OMCSession.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/OMPython/OMCSession.py b/OMPython/OMCSession.py index fb865ba4..a03b7f57 100644 --- a/OMPython/OMCSession.py +++ b/OMPython/OMCSession.py @@ -274,6 +274,10 @@ class OMCPath(pathlib.PurePosixPath): OMCSessionZMQ session object. """ + # TODO: need to handle PurePosixPath and PureWindowsPath + # PureOMCPath => OMCPathPosix(PureOMCPath, PurePosixPath) + # => OMCPathWindows(PureOMCPath, PureWindowsPath) + def __init__(self, *path, session: OMCSessionZMQ): super().__init__(*path) self._session = session @@ -363,6 +367,8 @@ def __del__(self): self.omc_zmq = None def omcpath(self, *path) -> OMCPath: + # TODO: need to handle PurePosixPath and PureWindowsPath + # define it here based on the backend (omc_process) used? return OMCPath(*path, session=self) def execute(self, command: str): From 0d3d4109f17c4ccfb828a21218899b34c1bb827a Mon Sep 17 00:00:00 2001 From: syntron Date: Wed, 2 Jul 2025 22:34:46 +0200 Subject: [PATCH 5/9] [test_OMCPath] mypy fix --- tests/test_OMCPath.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_OMCPath.py b/tests/test_OMCPath.py index 106f1cc7..bae41714 100644 --- a/tests/test_OMCPath.py +++ b/tests/test_OMCPath.py @@ -15,7 +15,7 @@ def test_OMCPath_docker(): assert p2.is_file() assert p2.parent.is_dir() assert p2.unlink() - assert p2.is_file() == False + assert p2.is_file() is False del omcp del om From 735f5f2f5c9e782461a7b7782390b002d125b6e0 Mon Sep 17 00:00:00 2001 From: syntron Date: Wed, 2 Jul 2025 22:52:38 +0200 Subject: [PATCH 6/9] [test_OMCPath] fix end of file --- tests/test_OMCPath.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_OMCPath.py b/tests/test_OMCPath.py index bae41714..c0249df6 100644 --- a/tests/test_OMCPath.py +++ b/tests/test_OMCPath.py @@ -23,4 +23,4 @@ def test_OMCPath_docker(): if __name__ == '__main__': test_OMCPath_docker() - print('DONE') \ No newline at end of file + print('DONE') From 322f40e412123b15dab2bc3f6f28c5834717c56c Mon Sep 17 00:00:00 2001 From: syntron Date: Thu, 3 Jul 2025 09:21:35 +0200 Subject: [PATCH 7/9] [test_OMCPath] define test using OMCSessionZMQ() locally --- tests/test_OMCPath.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/test_OMCPath.py b/tests/test_OMCPath.py index c0249df6..9621b54b 100644 --- a/tests/test_OMCPath.py +++ b/tests/test_OMCPath.py @@ -1,6 +1,8 @@ import OMPython +import pytest +@pytest.mark.skip(reason="This test would fail (no docker on github)") def test_OMCPath_docker(): omcp = OMPython.OMCProcessDocker(docker="openmodelica/openmodelica:v1.25.0-minimal") om = OMPython.OMCSessionZMQ(omc_process=omcp) @@ -21,6 +23,23 @@ def test_OMCPath_docker(): del om +def test_OMCPath_local(): + om = OMPython.OMCSessionZMQ() + + p1 = om.omcpath('/tmp') + assert str(p1) == "/tmp" + p2 = p1 / 'test.txt' + assert str(p2) == "/tmp/test.txt" + assert p2.write_text('test') + assert p2.read_text() == "test" + assert p2.is_file() + assert p2.parent.is_dir() + assert p2.unlink() + assert p2.is_file() is False + + del om + + if __name__ == '__main__': test_OMCPath_docker() print('DONE') From 7dcf5d6a5be7579afefc0ab6e5f93637011b65f2 Mon Sep 17 00:00:00 2001 From: syntron Date: Sun, 6 Jul 2025 21:54:01 +0200 Subject: [PATCH 8/9] add TODO - need to check Python versions * not working: 3.10 * working: 3.12 --- OMPython/OMCSession.py | 1 + 1 file changed, 1 insertion(+) diff --git a/OMPython/OMCSession.py b/OMPython/OMCSession.py index a03b7f57..f0505383 100644 --- a/OMPython/OMCSession.py +++ b/OMPython/OMCSession.py @@ -277,6 +277,7 @@ class OMCPath(pathlib.PurePosixPath): # TODO: need to handle PurePosixPath and PureWindowsPath # PureOMCPath => OMCPathPosix(PureOMCPath, PurePosixPath) # => OMCPathWindows(PureOMCPath, PureWindowsPath) + # TODO: only working for Python 3.12+ (not working for 3.10!; 3.11?) def __init__(self, *path, session: OMCSessionZMQ): super().__init__(*path) From 34e838cc745604045bfd1cba6f5c5cc6d06295eb Mon Sep 17 00:00:00 2001 From: syntron Date: Sun, 6 Jul 2025 21:54:24 +0200 Subject: [PATCH 9/9] [test_OMCPath] activate docker based on test_docker --- tests/test_OMCPath.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/test_OMCPath.py b/tests/test_OMCPath.py index 9621b54b..bef2b473 100644 --- a/tests/test_OMCPath.py +++ b/tests/test_OMCPath.py @@ -1,8 +1,14 @@ +import sys import OMPython import pytest +skip_on_windows = pytest.mark.skipif( + sys.platform.startswith("win"), + reason="OpenModelica Docker image is Linux-only; skipping on Windows.", +) -@pytest.mark.skip(reason="This test would fail (no docker on github)") + +@skip_on_windows def test_OMCPath_docker(): omcp = OMPython.OMCProcessDocker(docker="openmodelica/openmodelica:v1.25.0-minimal") om = OMPython.OMCSessionZMQ(omc_process=omcp)