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
« 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
8from ase.calculators.names import builtin, names, templates
10ASE_CONFIG_FILE = Path.home() / ".config/ase/config.ini"
13class ASEEnvDeprecationWarning(DeprecationWarning):
14 def __init__(self, message):
15 self.message = message
18class Config(Mapping):
19 def __init__(self):
20 def argv_converter(argv):
21 return shlex.split(argv)
23 self.parser = configparser.ConfigParser(
24 converters={"argv": argv_converter})
25 self.paths = []
27 def _env(self):
28 if self.parser.has_section('environment'):
29 return self.parser['environment']
30 else:
31 return {}
33 def __iter__(self):
34 yield from self._env()
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.
43 env = self._env()
44 try:
45 return env[item]
46 except KeyError:
47 pass
49 value = os.environ[item]
50 warnings.warn(f'Loaded {item} from environment. '
51 'Please use configfile.',
52 ASEEnvDeprecationWarning)
54 return value
56 def __len__(self):
57 return len(self._env())
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
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()
99 fullname = name
100 if version is not None:
101 fullname += f"--{version}"
103 def tickmark(thing):
104 return "[ ]" if thing is None else "[x]"
106 msg = " {configured} {installed} {fullname}".format(
107 configured=tickmark(codeconfig),
108 installed=tickmark(version),
109 fullname=fullname,
110 )
111 print(msg)
113 def print_everything(self):
114 print("Configuration")
115 print("-------------")
116 print()
117 if not self.paths:
118 print("No configuration loaded.")
120 for path in self.paths:
121 print(f"Loaded: {path}")
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()
132 def as_dict(self):
133 return {key: dict(val) for key, val in self.parser.items()}
135 def _read_paths(self, paths):
136 self.paths += self.parser.read(paths)
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(":")]
146 cfg = cls()
147 cfg._read_paths(paths)
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
156cfg = Config.read()