File: config.py

package info (click to toggle)
python-ase 3.24.0-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 15,448 kB
  • sloc: python: 144,945; xml: 2,728; makefile: 113; javascript: 47
file content (148 lines) | stat: -rw-r--r-- 4,435 bytes parent folder | download
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()