Skip to content

Commit 59366a0

Browse files
committed
[OMCPath] implementation version 2
* differentiate between * OMCPathReal => using OMC and * OMCPathLocal => using pathlibPath * OMCPathLocal uses the same strategy as pathlib.Path to differentiate * Posix or * Windows * align function definitions * for Python < 3.12 only local usage is allowed (as before!) * only change: use OMCPath instead of pathlib.Path; this should be done to allow usage of OMCPathReal for docker or WSL
1 parent 19c08c2 commit 59366a0

1 file changed

Lines changed: 104 additions & 31 deletions

File tree

OMPython/OMCSession.py

Lines changed: 104 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
CONDITIONS OF OSMC-PL.
3535
"""
3636

37+
import abc
3738
import io
3839
import json
3940
import logging
@@ -268,7 +269,54 @@ def getClassNames(self, className=None, recursive=False, qualified=False, sort=F
268269
return self._ask(question='getClassNames', opt=opt)
269270

270271

271-
class OMCPathReal(pathlib.PurePosixPath):
272+
class OMCPath(pathlib.PurePath, metaclass=abc.ABCMeta):
273+
"""
274+
Define the interface for OMCPath
275+
"""
276+
277+
def __init__(self, *path):
278+
super().__init__(*path)
279+
280+
# functions from pathlib.Path
281+
def with_segments(self, *pathsegments):
282+
raise NotImplementedError
283+
284+
def is_file(self, *, follow_symlinks=True):
285+
raise NotImplementedError
286+
287+
def is_dir(self, *, follow_symlinks=True):
288+
raise NotImplementedError
289+
290+
def read_text(self, encoding=None, errors=None, newline=None):
291+
raise NotImplementedError
292+
293+
def write_text(self, data: str, encoding=None, errors=None, newline=None):
294+
raise NotImplementedError
295+
296+
def mkdir(self, mode=0o777, parents=False, exist_ok=False):
297+
raise NotImplementedError
298+
299+
def cwd(self):
300+
raise NotImplementedError
301+
302+
def unlink(self, missing_ok: bool = False):
303+
raise NotImplementedError
304+
305+
def resolve(self, strict: bool = False):
306+
raise NotImplementedError
307+
308+
def absolute(self):
309+
raise NotImplementedError
310+
311+
def exists(self, *, follow_symlinks=True):
312+
raise NotImplementedError
313+
314+
# additional function specific for OMCPath
315+
def size(self) -> int:
316+
raise NotImplementedError
317+
318+
319+
class OMCPathReal(OMCPath, pathlib.PurePosixPath):
272320
"""
273321
Implementation of a basic Path object which uses OMC as backend. The connection to OMC is provided via a
274322
OMCSessionZMQ session object.
@@ -286,27 +334,27 @@ def with_segments(self, *pathsegments):
286334
"""
287335
return type(self)(*pathsegments, session=self._session)
288336

289-
def is_file(self) -> bool:
337+
def is_file(self, *, follow_symlinks=True):
290338
"""
291339
Check if the path is a regular file.
292340
"""
293341
return self._session.sendExpression(f'regularFileExists("{self.as_posix()}")')
294342

295-
def is_dir(self) -> bool:
343+
def is_dir(self, *, follow_symlinks=True):
296344
"""
297345
Check if the path is a directory.
298346
"""
299347
return self._session.sendExpression(f'directoryExists("{self.as_posix()}")')
300348

301-
def read_text(self, encoding=None, errors=None) -> str:
349+
def read_text(self, encoding=None, errors=None, newline=None):
302350
"""
303351
Read the content of the file represented by this path as text.
304352
305353
The additional arguments `encoding` and `errors` are only defined for compatibility with Path() definitions.
306354
"""
307355
return self._session.sendExpression(f'readFile("{self.as_posix()}")')
308356

309-
def write_text(self, data: str, encoding=None, errors=None, newline=None) -> bool:
357+
def write_text(self, data: str, encoding=None, errors=None, newline=None):
310358
"""
311359
Write text data to the file represented by this path.
312360
@@ -335,9 +383,9 @@ def cwd(self):
335383
Returns the current working directory as an OMCPath object.
336384
"""
337385
cwd_str = self._session.sendExpression('cd()')
338-
return OMCPath(cwd_str, session=self._session)
386+
return self.__class__(cwd_str, session=self._session)
339387

340-
def unlink(self, missing_ok: bool = False) -> bool:
388+
def unlink(self, missing_ok: bool = False):
341389
"""
342390
Unlink (delete) the file or directory represented by this path.
343391
"""
@@ -346,7 +394,7 @@ def unlink(self, missing_ok: bool = False) -> bool:
346394
raise FileNotFoundError(f"Cannot delete file {self.as_posix()} - it does not exists!")
347395
return res
348396

349-
def resolve(self, strict: bool = False) -> OMCPath:
397+
def resolve(self, strict: bool = False):
350398
"""
351399
Resolve the path to an absolute path. This is done based on available OMC functions.
352400
"""
@@ -386,14 +434,14 @@ def _omc_resolve(self, pathstr: str) -> OMCPath:
386434

387435
return omcpath_resolved
388436

389-
def absolute(self) -> OMCPath:
437+
def absolute(self):
390438
"""
391439
Resolve the path to an absolute path. This is done by calling resolve() as it is the best we can do
392440
using OMC functions.
393441
"""
394442
return self.resolve(strict=True)
395443

396-
def exists(self) -> bool:
444+
def exists(self, *, follow_symlinks=True):
397445
"""
398446
Semi replacement for pathlib.Path.exists().
399447
"""
@@ -413,22 +461,39 @@ def size(self) -> int:
413461
raise OMCSessionException(f"Error reading file size for path {self.as_posix()}!")
414462

415463

416-
if sys.version_info < (3, 12):
417-
warnings.warn(
418-
message="Python < 3.12 - using a limited compatibility class as OMCPath replacement.",
419-
category=DeprecationWarning,
420-
stacklevel=1,
421-
)
464+
class OMCPathLocal(OMCPath, pathlib.Path):
465+
"""
466+
Compatibility class for OMCPath in Python < 3.12. This allows to run all code which uses OMCPath (mainly
467+
ModelicaSystem) on these Python versions. There is one remaining limitation: only OMCProcessLocal will work as
468+
OMCPathCompatibility is based on the standard pathlib.Path implementation.
469+
"""
470+
471+
# modified copy of pathlib.Path.__new__() definition
472+
def __new__(cls, *args, **kwargs):
473+
logger.warning("Python < 3.12 - using a limited version of class OMCPath.")
474+
475+
if cls is OMCPathLocal:
476+
cls = OMCPathLocalWindows if os.name == 'nt' else OMCPathLocalPosix
477+
# noinspection PyUnresolvedReferences
478+
self = cls._from_parts(args)
479+
if not self._flavour.is_supported:
480+
raise NotImplementedError("cannot instantiate %r on your system"
481+
% (cls.__name__,))
482+
return self
483+
484+
def size(self) -> int:
485+
"""
486+
Needed compatibility function to have the same interface as OMCPathReal
487+
"""
488+
return self.stat().st_size
422489

423-
class OMCPathCompatibility(pathlib.PosixPath):
424490

425-
def size(self) -> int:
426-
return self.stat().st_size
491+
class OMCPathLocalPosix(pathlib.PosixPath, OMCPathLocal):
492+
pass
427493

428-
OMCPath = OMCPathCompatibility # noqa: F811
429494

430-
else:
431-
OMCPath = OMCPathReal
495+
class OMCPathLocalWindows(pathlib.WindowsPath, OMCPathLocal):
496+
pass
432497

433498

434499
class OMCSessionZMQ:
@@ -490,21 +555,29 @@ def omcpath(self, *path) -> OMCPath:
490555
Create an OMCPath object based on the given path segments and the current OMC session.
491556
"""
492557

493-
# fallback solution for Python < 3.12; a modified pathlib.Path object is used as OMCPath replacement
558+
if isinstance(self.omc_process, OMCProcessLocal):
559+
return OMCPathLocal(*path)
560+
494561
if sys.version_info < (3, 12):
495-
# noinspection PyArgumentList
496-
return OMCPath(*path)
497-
else:
498-
return OMCPath(*path, session=self)
562+
raise OMCSessionException("OMCPath for non-local usage is only supported for Python >= 3.12!")
563+
564+
return OMCPathReal(*path, session=self)
499565

500-
def omcpath_tempdir(self) -> OMCPath:
566+
def omcpath_tempdir(self, tempdir_base: Optional[OMCPath] = None) -> OMCPath:
501567
"""
502-
Get a temporary directory using OMC.
568+
Get a temporary directory using OMC. It is our own implementation as non-local usage relies on OMC to run all
569+
filesystem related access.
503570
"""
504571
names = [str(uuid.uuid4()) for _ in range(100)]
505572

506-
tempdir_str = self.sendExpression("getTempDirectoryPath()")
507-
tempdir_base = self.omcpath(tempdir_str)
573+
if tempdir_base is None:
574+
# shortcut for local usage
575+
if isinstance(self.omc_process, OMCProcessLocal):
576+
tempdir_str = tempfile.gettempdir()
577+
else:
578+
tempdir_str = self.sendExpression("getTempDirectoryPath()")
579+
tempdir_base = self.omcpath(tempdir_str)
580+
508581
tempdir: Optional[OMCPath] = None
509582
for name in names:
510583
# create a unique temporary directory name

0 commit comments

Comments
 (0)