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

1import glob 

2import logging 

3import os 

4import platform 

5import re 

6import shutil 

7import tempfile 

8 

9from pydelica.exception import UnknownLibraryError 

10 

11 

12class LibrarySetup: 

13 """Object containing setup for a particular Modelica library version""" 

14 

15 def __init__(self) -> None: 

16 """Create a setup object 

17 

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() 

27 

28 def __enter__(self) -> "LibrarySetup": 

29 return self 

30 

31 def __exit__(self, *_, **__) -> None: 

32 if self._session_library: 

33 shutil.rmtree(self._session_library) 

34 

35 def use_library(self, name: str, version: str, directory: str = "") -> None: 

36 """Use a specific library version 

37 

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). 

42 

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 

51 

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}'") 

59 

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 ) 

65 

66 if directory: 

67 _libraries = glob.glob(os.path.join(directory, "*")) 

68 else: 

69 _libraries = self._libraries 

70 

71 if not self._session_library: 

72 self._session_library = tempfile.mkdtemp() 

73 

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 

79 

80 # Create the address for the symlink/destination 

81 _new_lib = os.path.join(self._session_library, os.path.basename(library)) 

82 

83 # if no space in library filename then assume no version number 

84 if " " not in os.path.basename(library): 

85 continue 

86 

87 _name, _info = os.path.basename(library).split() 

88 

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 

93 

94 _version: str = _version_re_search[0] 

95 

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) 

125 

126 @property 

127 def session_library(self) -> str | None: 

128 return self._session_library 

129 

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