Coverage for /builds/ase/ase/ase/config.py: 60.00%

90 statements  

« prev     ^ index     » next       coverage.py v7.5.3, created at 2025-08-02 00:12 +0000

1# fmt: off 

2 

3import configparser 

4import os 

5import shlex 

6import warnings 

7from collections.abc import Mapping 

8from pathlib import Path 

9 

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

11 

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

13 

14 

15class ASEEnvDeprecationWarning(DeprecationWarning): 

16 def __init__(self, message): 

17 self.message = message 

18 

19 

20class Config(Mapping): 

21 def __init__(self): 

22 def argv_converter(argv): 

23 return shlex.split(argv) 

24 

25 self.parser = configparser.ConfigParser( 

26 converters={"argv": argv_converter}, 

27 interpolation=configparser.ExtendedInterpolation()) 

28 self.paths = [] 

29 

30 def _env(self): 

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

32 return self.parser['environment'] 

33 else: 

34 return {} 

35 

36 def __iter__(self): 

37 yield from self._env() 

38 

39 def __getitem__(self, item): 

40 # XXX We should replace the mapping behaviour with individual 

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

42 # 

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

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

45 

46 env = self._env() 

47 try: 

48 return env[item] 

49 except KeyError: 

50 pass 

51 

52 value = os.environ[item] 

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

54 'Please use configfile.', 

55 ASEEnvDeprecationWarning) 

56 

57 return value 

58 

59 def __len__(self): 

60 return len(self._env()) 

61 

62 def check_calculators(self): 

63 print("Calculators") 

64 print("===========") 

65 print() 

66 print("Configured in ASE") 

67 print(" | Installed on machine") 

68 print(" | | Name & version") 

69 print(" | | |") 

70 for name in names: 

71 # configured = False 

72 # installed = False 

73 template = templates.get(name) 

74 # if template is None: 

75 # XXX no template for this calculator. 

76 # We need templates for all calculators somehow, 

77 # but we can probably generate those for old FileIOCalculators 

78 # automatically. 

79 # continue 

80 

81 fullname = name 

82 try: 

83 codeconfig = self[name] 

84 except KeyError: 

85 codeconfig = None 

86 version = None 

87 else: 

88 if template is None: 

89 # XXX we should not be executing this 

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

91 # builtin calculators 

92 version = "builtin" 

93 else: 

94 version = None 

95 else: 

96 profile = template.load_profile(codeconfig) 

97 # XXX should be made robust to failure here: 

98 with warnings.catch_warnings(): 

99 warnings.simplefilter("ignore") 

100 version = profile.version() 

101 

102 fullname = name 

103 if version is not None: 

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

105 

106 def tickmark(thing): 

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

108 

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

110 configured=tickmark(codeconfig), 

111 installed=tickmark(version), 

112 fullname=fullname, 

113 ) 

114 print(msg) 

115 

116 def print_header(self): 

117 print("Configuration") 

118 print("-------------") 

119 print() 

120 if not self.paths: 

121 print("No configuration loaded.") 

122 

123 for path in self.paths: 

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

125 

126 def as_dict(self): 

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

128 

129 def _read_paths(self, paths): 

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

131 

132 @classmethod 

133 def read(cls): 

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

135 if envpath is None: 

136 paths = [ASE_CONFIG_FILE, ] 

137 else: 

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

139 

140 cfg = cls() 

141 cfg._read_paths(paths) 

142 

143 # add sections for builtin calculators 

144 for name in builtin: 

145 cfg.parser.add_section(name) 

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

147 return cfg 

148 

149 

150cfg = Config.read()