Coverage for pydelica/options/library.py: 73%
78 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-05-19 07:38 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-05-19 07:38 +0000
1import glob
2import logging
3import os
4import platform
5import re
6import shutil
7import tempfile
9from pydelica.exception import UnknownLibraryError
12class LibrarySetup:
13 """Object containing setup for a particular Modelica library version"""
15 def __init__(self) -> None:
16 """Create a setup object
18 Parameters
19 ----------
20 library_folder : str, optional
21 specify location of the Modelica libraries, else use system defaults
22 """
23 self._logger = logging.getLogger("PyDelica.LibrarySetup")
24 self._libraries = []
25 self._session_library = None
26 self._libraries = self._get_system_libraries()
28 def __enter__(self) -> "LibrarySetup":
29 return self
31 def __exit__(self, *_, **__) -> None:
32 if self._session_library:
33 shutil.rmtree(self._session_library)
35 def use_library(self, name: str, version: str, directory: str = "") -> None:
36 """Use a specific library version
38 This function looks through all library directories (symlinks) and
39 checks which match the requested library. The version number of the
40 directory is then compared to requested version and is symlinked
41 (or copied if Windows), other versions are unlinked (or removed).
43 Parameters
44 ----------
45 name : str
46 library to select
47 version : str
48 version requested
49 directory : str, optional
50 alternative directory containing library
52 Raises
53 ------
54 UnknownLibraryError
55 if the library and version are not recognised
56 """
57 _test_str = f"{name} {version}"
58 self._logger.debug(f"Selecting Library '{_test_str}'")
60 if all(_test_str not in i for i in self._libraries):
61 raise UnknownLibraryError(
62 f"Cannot import library '{name}' version '{version}', "
63 "library not found."
64 )
66 if directory:
67 _libraries = glob.glob(os.path.join(directory, "*"))
68 else:
69 _libraries = self._libraries
71 if not self._session_library:
72 self._session_library = tempfile.mkdtemp()
74 for library in _libraries:
75 # If library does not contain requested library name ignore
76 # and continue
77 if name.lower() not in library.lower():
78 continue
80 # Create the address for the symlink/destination
81 _new_lib = os.path.join(self._session_library, os.path.basename(library))
83 # if no space in library filename then assume no version number
84 if " " not in os.path.basename(library):
85 continue
87 _name, _info = os.path.basename(library).split()
89 # If the split name is length 1 this means there is no
90 # version string anyway
91 if not (_version_re_search := re.findall(r"[0-9]+\.[0-9]+\.[0-9]+", _info)):
92 continue
94 _version: str = _version_re_search[0]
96 # Check that the name matches the requested library name
97 if name.lower().strip() == _name.lower().strip():
98 # Check the version matches the requested version
99 # if it does ensure this is symlinked/copied,
100 # if not unlink/remove it
101 if version.lower().strip() == _version.lower().strip():
102 if not os.path.exists(_new_lib):
103 if platform.system() != "Windows":
104 self._logger.debug(f"Linking: {library} -> {_new_lib}")
105 os.symlink(library, _new_lib)
106 elif os.path.isdir(library):
107 # Libraries are directories in Windows
108 self._logger.debug(f"Copying: {library} -> {_new_lib}")
109 shutil.copytree(library, _new_lib, symlinks=True)
110 else:
111 # Libraries are directories in Windows
112 self._logger.debug(f"Copying: {library} -> {_new_lib}")
113 shutil.copyfile(library, _new_lib)
114 elif os.path.exists(_new_lib):
115 if platform.system() != "Windows":
116 self._logger.debug(f"Unlinking: {_new_lib} -> {library}")
117 os.unlink(_new_lib)
118 else:
119 self._logger.debug(f"Removing: {_new_lib}")
120 # Libraries are directories in Windows
121 if os.path.isdir(_new_lib):
122 shutil.rmtree(_new_lib)
123 else:
124 os.remove(_new_lib)
126 @property
127 def session_library(self) -> str | None:
128 return self._session_library
130 def _get_system_libraries(self) -> list[str]:
131 if "MODELICAPATH" in os.environ:
132 _library_dirs = os.environ["MODELICAPATH"].split(os.pathsep)
133 _libs = []
134 for library_dir in _library_dirs:
135 _libs += glob.glob(os.path.join(library_dir, "*"))
136 elif platform.system() == "Windows":
137 _home = os.environ["USERPROFILE"]
138 _user_libraries = os.path.join(
139 _home, "AppData", "Roaming", ".openmodelica", "libraries"
140 )
141 _library_dir = os.path.join(
142 os.environ["OPENMODELICAHOME"], "lib", "omlibrary"
143 )
144 _libs = glob.glob(os.path.join(_user_libraries, "*"))
145 _libs += glob.glob(os.path.join(_library_dir, "*"))
146 else:
147 # Try typical linux locations
148 _library_dirs = [
149 "/usr/lib/omlibrary",
150 f"{os.environ['HOME']}/.openmodelica/libraries",
151 ]
152 _libs = []
153 for library_dir in _library_dirs:
154 _libs += glob.glob(os.path.join(library_dir, "*"))
155 return _libs