@@ -249,13 +249,15 @@ def getClassNames(self, className=None, recursive=False, qualified=False, sort=F
249249 return self ._ask (question = 'getClassNames' , opt = opt )
250250
251251
252- class OMCPathReal (pathlib .PurePosixPath ):
252+ class OMPathABC (pathlib .PurePosixPath , metaclass = abc . ABCMeta ):
253253 """
254- Implementation of a basic (PurePosix)Path object which uses OMC as backend. The connection to OMC is provided via an
255- instances of OMCSession* classes.
254+ Implementation of a basic (PurePosix)Path object to be used within OMPython. The derived classes can use OMC as
255+ backend and - thus - work on different configurations like docker or WSL. The connection to OMC is provided via an
256+ instances of classes derived from BaseSession.
256257
257- PurePosixPath is selected to cover usage of OMC in docker or via WSL. Usage of specialised function could result in
258- errors as well as usage on a Windows system due to slightly different definitions (PureWindowsPath).
258+ PurePosixPath is selected as it covers all but Windows systems (Linux, docker, WSL). However, the code is written
259+ such that possible Windows system are taken into account. Nevertheless, the overall functionality is limited
260+ compared to standard pathlib.Path objects.
259261 """
260262
261263 def __init__ (self , * path , session : OMCSession ) -> None :
@@ -266,46 +268,122 @@ def with_segments(self, *pathsegments):
266268 """
267269 Create a new OMCPath object with the given path segments.
268270
269- The original definition of Path is overridden to ensure the OMC session is set.
271+ The original definition of Path is overridden to ensure the session data is set.
270272 """
271273 return type (self )(* pathsegments , session = self ._session )
272274
273- def is_file (self , * , follow_symlinks = True ) -> bool :
275+ @abc .abstractmethod
276+ def is_file (self ) -> bool :
277+ """
278+ Check if the path is a regular file.
279+ """
280+
281+ @abc .abstractmethod
282+ def is_dir (self ) -> bool :
283+ """
284+ Check if the path is a directory.
285+ """
286+
287+ @abc .abstractmethod
288+ def is_absolute (self ):
289+ """
290+ Check if the path is an absolute path.
291+ """
292+
293+ @abc .abstractmethod
294+ def read_text (self ) -> str :
295+ """
296+ Read the content of the file represented by this path as text.
297+ """
298+
299+ @abc .abstractmethod
300+ def write_text (self , data : str ):
301+ """
302+ Write text data to the file represented by this path.
303+ """
304+
305+ @abc .abstractmethod
306+ def mkdir (self , parents : bool = True , exist_ok : bool = False ):
307+ """
308+ Create a directory at the path represented by this class.
309+
310+ The argument parents with default value True exists to ensure compatibility with the fallback solution for
311+ Python < 3.12. In this case, pathlib.Path is used directly and this option ensures, that missing parent
312+ directories are also created.
313+ """
314+
315+ @abc .abstractmethod
316+ def cwd (self ):
317+ """
318+ Returns the current working directory as an OMPathBase object.
319+ """
320+
321+ @abc .abstractmethod
322+ def unlink (self , missing_ok : bool = False ) -> None :
323+ """
324+ Unlink (delete) the file or directory represented by this path.
325+ """
326+
327+ @abc .abstractmethod
328+ def resolve (self , strict : bool = False ):
329+ """
330+ Resolve the path to an absolute path.
331+ """
332+
333+ def absolute (self ):
334+ """
335+ Resolve the path to an absolute path. Just a wrapper for resolve().
336+ """
337+ return self .resolve ()
338+
339+ def exists (self ) -> bool :
340+ """
341+ Semi replacement for pathlib.Path.exists().
342+ """
343+ return self .is_file () or self .is_dir ()
344+
345+ @abc .abstractmethod
346+ def size (self ) -> int :
347+ """
348+ Get the size of the file in bytes - this is an extra function and the best we can do using OMC.
349+ """
350+
351+
352+ class _OMCPath (OMPathABC ):
353+ """
354+ Implementation of a OMPathBase using OMC as backend. The connection to OMC is provided via an instances of an
355+ OMCSession* classes.
356+ """
357+
358+ def is_file (self ) -> bool :
274359 """
275360 Check if the path is a regular file.
276361 """
277362 return self ._session .sendExpression (expr = f'regularFileExists("{ self .as_posix ()} ")' )
278363
279- def is_dir (self , * , follow_symlinks = True ) -> bool :
364+ def is_dir (self ) -> bool :
280365 """
281366 Check if the path is a directory.
282367 """
283368 return self ._session .sendExpression (expr = f'directoryExists("{ self .as_posix ()} ")' )
284369
285370 def is_absolute (self ):
286371 """
287- Check if the path is an absolute path considering the possibility that we are running locally on Windows. This
288- case needs special handling as the definition of is_absolute() differs.
372+ Check if the path is an absolute path.
289373 """
290374 if isinstance (self ._session , OMCSessionLocal ) and platform .system () == 'Windows' :
291375 return pathlib .PureWindowsPath (self .as_posix ()).is_absolute ()
292376 return super ().is_absolute ()
293377
294- def read_text (self , encoding = None , errors = None , newline = None ) -> str :
378+ def read_text (self ) -> str :
295379 """
296380 Read the content of the file represented by this path as text.
297-
298- The additional arguments `encoding`, `errors` and `newline` are only defined for compatibility with Path()
299- definition.
300381 """
301382 return self ._session .sendExpression (expr = f'readFile("{ self .as_posix ()} ")' )
302383
303- def write_text (self , data : str , encoding = None , errors = None , newline = None ):
384+ def write_text (self , data : str ):
304385 """
305386 Write text data to the file represented by this path.
306-
307- The additional arguments `encoding`, `errors`, and `newline` are only defined for compatibility with Path()
308- definitions.
309387 """
310388 if not isinstance (data , str ):
311389 raise TypeError (f"data must be str, not { data .__class__ .__name__ } " )
@@ -315,11 +393,13 @@ def write_text(self, data: str, encoding=None, errors=None, newline=None):
315393
316394 return len (data )
317395
318- def mkdir (self , mode = 0o777 , parents = False , exist_ok = False ):
396+ def mkdir (self , parents : bool = True , exist_ok : bool = False ):
319397 """
320- Create a directory at the path represented by this OMCPath object .
398+ Create a directory at the path represented by this class .
321399
322- The additional arguments `mode`, and `parents` are only defined for compatibility with Path() definitions.
400+ The argument parents with default value True exists to ensure compatibility with the fallback solution for
401+ Python < 3.12. In this case, pathlib.Path is used directly and this option ensures, that missing parent
402+ directories are also created.
323403 """
324404 if self .is_dir () and not exist_ok :
325405 raise FileExistsError (f"Directory { self .as_posix ()} already exists!" )
@@ -328,7 +408,7 @@ def mkdir(self, mode=0o777, parents=False, exist_ok=False):
328408
329409 def cwd (self ):
330410 """
331- Returns the current working directory as an OMCPath object.
411+ Returns the current working directory as an OMPathBase object.
332412 """
333413 cwd_str = self ._session .sendExpression (expr = 'cd()' )
334414 return OMCPath (cwd_str , session = self ._session )
@@ -381,19 +461,6 @@ def _omc_resolve(self, pathstr: str) -> str:
381461
382462 return pathstr_resolved
383463
384- def absolute (self ):
385- """
386- Resolve the path to an absolute path. This is done by calling resolve() as it is the best we can do
387- using OMC functions.
388- """
389- return self .resolve (strict = True )
390-
391- def exists (self , follow_symlinks = True ) -> bool :
392- """
393- Semi replacement for pathlib.Path.exists().
394- """
395- return self .is_file () or self .is_dir ()
396-
397464 def size (self ) -> int :
398465 """
399466 Get the size of the file in bytes - this is an extra function and the best we can do using OMC.
@@ -408,47 +475,47 @@ def size(self) -> int:
408475 raise OMCSessionException (f"Error reading file size for path { self .as_posix ()} !" )
409476
410477
411- if sys .version_info < (3 , 12 ):
478+ class OMPathCompatibility (pathlib .Path ):
479+ """
480+ Compatibility class for OMPathBase in Python < 3.12. This allows to run all code which uses OMPathBase (mainly
481+ ModelicaSystem) on these Python versions. There are remaining limitation as only local execution is possible.
482+ """
412483
413- class OMCPathCompatibility (pathlib .Path ):
484+ # modified copy of pathlib.Path.__new__() definition
485+ def __new__ (cls , * args , ** kwargs ):
486+ logger .warning ("Python < 3.12 - using a version of class OMCPath "
487+ "based on pathlib.Path for local usage only." )
488+
489+ if cls is OMPathCompatibility :
490+ cls = OMPathCompatibilityWindows if os .name == 'nt' else OMPathCompatibilityPosix
491+ self = cls ._from_parts (args )
492+ if not self ._flavour .is_supported :
493+ raise NotImplementedError (f"cannot instantiate { cls .__name__ } on your system" )
494+ return self
495+
496+ def size (self ) -> int :
414497 """
415- Compatibility class for OMCPath in Python < 3.12. This allows to run all code which uses OMCPath (mainly
416- ModelicaSystem) on these Python versions. There is one remaining limitation: only OMCProcessLocal will work as
417- OMCPathCompatibility is based on the standard pathlib.Path implementation.
498+ Needed compatibility function to have the same interface as OMCPathReal
418499 """
500+ return self .stat ().st_size
419501
420- # modified copy of pathlib.Path.__new__() definition
421- def __new__ (cls , * args , ** kwargs ):
422- logger .warning ("Python < 3.12 - using a version of class OMCPath "
423- "based on pathlib.Path for local usage only." )
424502
425- if cls is OMCPathCompatibility :
426- cls = OMCPathCompatibilityWindows if os .name == 'nt' else OMCPathCompatibilityPosix
427- self = cls ._from_parts (args )
428- if not self ._flavour .is_supported :
429- raise NotImplementedError (f"cannot instantiate { cls .__name__ } on your system" )
430- return self
431-
432- def size (self ) -> int :
433- """
434- Needed compatibility function to have the same interface as OMCPathReal
435- """
436- return self .stat ().st_size
503+ class OMPathCompatibilityPosix (pathlib .PosixPath , OMPathCompatibility ):
504+ """
505+ Compatibility class for OMCPath on Posix systems (Python < 3.12)
506+ """
437507
438- class OMCPathCompatibilityPosix (pathlib .PosixPath , OMCPathCompatibility ):
439- """
440- Compatibility class for OMCPath on Posix systems (Python < 3.12)
441- """
442508
443- class OMCPathCompatibilityWindows (pathlib .WindowsPath , OMCPathCompatibility ):
444- """
445- Compatibility class for OMCPath on Windows systems (Python < 3.12)
446- """
509+ class OMPathCompatibilityWindows (pathlib .WindowsPath , OMPathCompatibility ):
510+ """
511+ Compatibility class for OMCPath on Windows systems (Python < 3.12)
512+ """
447513
448- OMCPath = OMCPathCompatibility
449514
515+ if sys .version_info < (3 , 12 ):
516+ OMCPath = OMPathCompatibility
450517else :
451- OMCPath = OMCPathReal
518+ OMCPath = _OMCPath
452519
453520
454521class ModelExecutionException (Exception ):
@@ -570,13 +637,13 @@ def escape_str(value: str) -> str:
570637 """
571638 return OMCSession .escape_str (value = value )
572639
573- def omcpath (self , * path ) -> OMCPath :
640+ def omcpath (self , * path ) -> OMPathABC :
574641 """
575642 Create an OMCPath object based on the given path segments and the current OMC process definition.
576643 """
577644 return self .omc_process .omcpath (* path )
578645
579- def omcpath_tempdir (self , tempdir_base : Optional [OMCPath ] = None ) -> OMCPath :
646+ def omcpath_tempdir (self , tempdir_base : Optional [OMPathABC ] = None ) -> OMPathABC :
580647 """
581648 Get a temporary directory using OMC. It is our own implementation as non-local usage relies on OMC to run all
582649 filesystem related access.
@@ -796,7 +863,7 @@ def get_version(self) -> str:
796863 """
797864 return self .sendExpression ("getVersion()" , parsed = True )
798865
799- def set_workdir (self , workdir : OMCPath ) -> None :
866+ def set_workdir (self , workdir : OMPathABC ) -> None :
800867 """
801868 Set the workdir for this session.
802869 """
@@ -810,7 +877,7 @@ def model_execution_prefix(self, cwd: Optional[OMCPath] = None) -> list[str]:
810877
811878 return []
812879
813- def omcpath (self , * path ) -> OMCPath :
880+ def omcpath (self , * path ) -> OMPathABC :
814881 """
815882 Create an OMCPath object based on the given path segments and the current OMCSession* class.
816883 """
@@ -823,7 +890,7 @@ def omcpath(self, *path) -> OMCPath:
823890 raise OMCSessionException ("OMCPath is supported for Python < 3.12 only if OMCSessionLocal is used!" )
824891 return OMCPath (* path , session = self )
825892
826- def omcpath_tempdir (self , tempdir_base : Optional [OMCPath ] = None ) -> OMCPath :
893+ def omcpath_tempdir (self , tempdir_base : Optional [OMPathABC ] = None ) -> OMPathABC :
827894 """
828895 Get a temporary directory using OMC. It is our own implementation as non-local usage relies on OMC to run all
829896 filesystem related access.
@@ -840,10 +907,10 @@ def omcpath_tempdir(self, tempdir_base: Optional[OMCPath] = None) -> OMCPath:
840907 return self ._tempdir (tempdir_base = tempdir_base )
841908
842909 @staticmethod
843- def _tempdir (tempdir_base : OMCPath ) -> OMCPath :
910+ def _tempdir (tempdir_base : OMPathABC ) -> OMPathABC :
844911 names = [str (uuid .uuid4 ()) for _ in range (100 )]
845912
846- tempdir : Optional [OMCPath ] = None
913+ tempdir : Optional [OMPathABC ] = None
847914 for name in names :
848915 # create a unique temporary directory name
849916 tempdir = tempdir_base / name
@@ -1243,15 +1310,15 @@ def get_docker_container_id(self) -> str:
12431310
12441311 return self ._docker_container_id
12451312
1246- def model_execution_prefix (self , cwd : Optional [OMCPath ] = None ) -> list [str ]:
1313+ def model_execution_prefix (self , cwd : Optional [OMPathABC ] = None ) -> list [str ]:
12471314 """
12481315 Helper function which returns a command prefix needed for docker and WSL. It defaults to an empty list.
12491316 """
12501317 docker_cmd = [
12511318 "docker" , "exec" ,
12521319 "--user" , str (self ._getuid ()),
1253- ]
1254- if isinstance (cwd , OMCPath ):
1320+ ]
1321+ if isinstance (cwd , OMPathABC ):
12551322 docker_cmd += ["--workdir" , cwd .as_posix ()]
12561323 docker_cmd += self ._docker_extra_args
12571324 if isinstance (self ._docker_container_id , str ):
@@ -1520,7 +1587,7 @@ def __init__(
15201587 # connect to the running omc instance using ZMQ
15211588 self ._omc_port = self ._omc_port_get ()
15221589
1523- def model_execution_prefix (self , cwd : Optional [OMCPath ] = None ) -> list [str ]:
1590+ def model_execution_prefix (self , cwd : Optional [OMPathABC ] = None ) -> list [str ]:
15241591 """
15251592 Helper function which returns a command prefix needed for docker and WSL. It defaults to an empty list.
15261593 """
@@ -1530,7 +1597,7 @@ def model_execution_prefix(self, cwd: Optional[OMCPath] = None) -> list[str]:
15301597 wsl_cmd += ['--distribution' , self ._wsl_distribution ]
15311598 if isinstance (self ._wsl_user , str ):
15321599 wsl_cmd += ['--user' , self ._wsl_user ]
1533- if isinstance (cwd , OMCPath ):
1600+ if isinstance (cwd , OMPathABC ):
15341601 wsl_cmd += ['--cd' , cwd .as_posix ()]
15351602 wsl_cmd += ['--' ]
15361603
0 commit comments