1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
|
import configparser
import os
import shlex
import warnings
from collections.abc import Mapping
from pathlib import Path
from ase.calculators.names import builtin, names, templates
ASE_CONFIG_FILE = Path.home() / ".config/ase/config.ini"
class ASEEnvDeprecationWarning(DeprecationWarning):
def __init__(self, message):
self.message = message
class Config(Mapping):
def __init__(self):
def argv_converter(argv):
return shlex.split(argv)
self.parser = configparser.ConfigParser(
converters={"argv": argv_converter},
interpolation=configparser.ExtendedInterpolation())
self.paths = []
def _env(self):
if self.parser.has_section('environment'):
return self.parser['environment']
else:
return {}
def __iter__(self):
yield from self._env()
def __getitem__(self, item):
# XXX We should replace the mapping behaviour with individual
# methods to get from cfg or environment, or only from cfg.
#
# We cannot be a mapping very correctly without getting trouble
# with mutable state needing synchronization with os.environ.
env = self._env()
try:
return env[item]
except KeyError:
pass
value = os.environ[item]
warnings.warn(f'Loaded {item} from environment. '
'Please use configfile.',
ASEEnvDeprecationWarning)
return value
def __len__(self):
return len(self._env())
def check_calculators(self):
print("Calculators")
print("===========")
print()
print("Configured in ASE")
print(" | Installed on machine")
print(" | | Name & version")
print(" | | |")
for name in names:
# configured = False
# installed = False
template = templates.get(name)
# if template is None:
# XXX no template for this calculator.
# We need templates for all calculators somehow,
# but we can probably generate those for old FileIOCalculators
# automatically.
# continue
fullname = name
try:
codeconfig = self[name]
except KeyError:
codeconfig = None
version = None
else:
if template is None:
# XXX we should not be executing this
if codeconfig is not None and "builtin" in codeconfig:
# builtin calculators
version = "builtin"
else:
version = None
else:
profile = template.load_profile(codeconfig)
# XXX should be made robust to failure here:
with warnings.catch_warnings():
warnings.simplefilter("ignore")
version = profile.version()
fullname = name
if version is not None:
fullname += f"--{version}"
def tickmark(thing):
return "[ ]" if thing is None else "[x]"
msg = " {configured} {installed} {fullname}".format(
configured=tickmark(codeconfig),
installed=tickmark(version),
fullname=fullname,
)
print(msg)
def print_header(self):
print("Configuration")
print("-------------")
print()
if not self.paths:
print("No configuration loaded.")
for path in self.paths:
print(f"Loaded: {path}")
def as_dict(self):
return {key: dict(val) for key, val in self.parser.items()}
def _read_paths(self, paths):
self.paths += self.parser.read(paths)
@classmethod
def read(cls):
envpath = os.environ.get("ASE_CONFIG_PATH")
if envpath is None:
paths = [ASE_CONFIG_FILE, ]
else:
paths = [Path(p) for p in envpath.split(":")]
cfg = cls()
cfg._read_paths(paths)
# add sections for builtin calculators
for name in builtin:
cfg.parser.add_section(name)
cfg.parser[name]["builtin"] = "True"
return cfg
cfg = Config.read()
|