Coverage for /builds/hweiske/ase/ase/config.py: 68.37%

98 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2024-04-22 11:22 +0000

1import configparser 

2import os 

3import shlex 

4import warnings 

5from collections.abc import Mapping 

6from pathlib import Path 

7 

8from ase.calculators.names import builtin, names, templates 

9 

10ASE_CONFIG_FILE = Path.home() / ".config/ase/config.ini" 

11 

12 

13class ASEEnvDeprecationWarning(DeprecationWarning): 

14 def __init__(self, message): 

15 self.message = message 

16 

17 

18class Config(Mapping): 

19 def __init__(self): 

20 def argv_converter(argv): 

21 return shlex.split(argv) 

22 

23 self.parser = configparser.ConfigParser( 

24 converters={"argv": argv_converter}) 

25 self.paths = [] 

26 

27 def _env(self): 

28 if self.parser.has_section('environment'): 

29 return self.parser['environment'] 

30 else: 

31 return {} 

32 

33 def __iter__(self): 

34 yield from self._env() 

35 

36 def __getitem__(self, item): 

37 # XXX We should replace the mapping behaviour with individual 

38 # methods to get from cfg or environment, or only from cfg. 

39 # 

40 # We cannot be a mapping very correctly without getting trouble 

41 # with mutable state needing synchronization with os.environ. 

42 

43 env = self._env() 

44 try: 

45 return env[item] 

46 except KeyError: 

47 pass 

48 

49 value = os.environ[item] 

50 warnings.warn(f'Loaded {item} from environment. ' 

51 'Please use configfile.', 

52 ASEEnvDeprecationWarning) 

53 

54 return value 

55 

56 def __len__(self): 

57 return len(self._env()) 

58 

59 def check_calculators(self): 

60 print("Calculators") 

61 print("===========") 

62 print() 

63 print("Configured in ASE") 

64 print(" | Installed on machine") 

65 print(" | | Name & version") 

66 print(" | | |") 

67 for name in names: 

68 # configured = False 

69 # installed = False 

70 template = templates.get(name) 

71 # if template is None: 

72 # XXX no template for this calculator. 

73 # We need templates for all calculators somehow, 

74 # but we can probably generate those for old FileIOCalculators 

75 # automatically. 

76 # continue 

77 

78 fullname = name 

79 try: 

80 codeconfig = self[name] 

81 except KeyError: 

82 codeconfig = None 

83 version = None 

84 else: 

85 if template is None: 

86 # XXX we should not be executing this 

87 if codeconfig is not None and "builtin" in codeconfig: 

88 # builtin calculators 

89 version = "builtin" 

90 else: 

91 version = None 

92 else: 

93 profile = template.load_profile(codeconfig) 

94 # XXX should be made robust to failure here: 

95 with warnings.catch_warnings(): 

96 warnings.simplefilter("ignore") 

97 version = profile.version() 

98 

99 fullname = name 

100 if version is not None: 

101 fullname += f"--{version}" 

102 

103 def tickmark(thing): 

104 return "[ ]" if thing is None else "[x]" 

105 

106 msg = " {configured} {installed} {fullname}".format( 

107 configured=tickmark(codeconfig), 

108 installed=tickmark(version), 

109 fullname=fullname, 

110 ) 

111 print(msg) 

112 

113 def print_everything(self): 

114 print("Configuration") 

115 print("-------------") 

116 print() 

117 if not self.paths: 

118 print("No configuration loaded.") 

119 

120 for path in self.paths: 

121 print(f"Loaded: {path}") 

122 

123 print() 

124 for name, section in self.parser.items(): 

125 print(name) 

126 if not section: 

127 print(" (Nothing configured)") 

128 for key, val in section.items(): 

129 print(f" {key}: {val}") 

130 print() 

131 

132 def as_dict(self): 

133 return {key: dict(val) for key, val in self.parser.items()} 

134 

135 def _read_paths(self, paths): 

136 self.paths += self.parser.read(paths) 

137 

138 @classmethod 

139 def read(cls): 

140 envpath = os.environ.get("ASE_CONFIG_PATH") 

141 if envpath is None: 

142 paths = [ASE_CONFIG_FILE, ] 

143 else: 

144 paths = [Path(p) for p in envpath.split(":")] 

145 

146 cfg = cls() 

147 cfg._read_paths(paths) 

148 

149 # add sections for builtin calculators 

150 for name in builtin: 

151 cfg.parser.add_section(name) 

152 cfg.parser[name]["builtin"] = "True" 

153 return cfg 

154 

155 

156cfg = Config.read()