Skip to content

config

CONFIG: ServerConfig = ServerConfig() module-attribute

This singleton loads the config from a hierarchy of sources (see customise_sources) and makes it importable in the server code.

DEFAULT_CONFIG_FILE_PATH: str = str(Path.home().joinpath('.optimade.json')) module-attribute

Default configuration file path.

This variable is used as the fallback value if the environment variable OPTIMADE_CONFIG_FILE is not set.

Note

It is set to: pathlib.Path.home()/.optimade.json

For Unix-based systems (Linux) this will be equivalent to ~/.optimade.json.

LogLevel

Replication of logging LogLevels

  • notset
  • debug
  • info
  • warning
  • error
  • critical
Source code in optimade/server/config.py
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
class LogLevel(Enum):
    """Replication of logging LogLevels

    - `notset`
    - `debug`
    - `info`
    - `warning`
    - `error`
    - `critical`

    """

    NOTSET = "notset"
    DEBUG = "debug"
    INFO = "info"
    WARNING = "warning"
    ERROR = "error"
    CRITICAL = "critical"

CRITICAL = 'critical' class-attribute

DEBUG = 'debug' class-attribute

ERROR = 'error' class-attribute

INFO = 'info' class-attribute

NOTSET = 'notset' class-attribute

WARNING = 'warning' class-attribute

ServerConfig

This class stores server config parameters in a way that can be easily extended for new config file types.

Source code in optimade/server/config.py
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
class ServerConfig(BaseSettings):
    """This class stores server config parameters in a way that
    can be easily extended for new config file types.

    """

    debug: bool = Field(
        False,
        description="Turns on Debug Mode for the OPTIMADE Server implementation",
    )

    insert_test_data: bool = Field(
        True,
        description=(
            "Insert test data into each collection on server initialisation. If true, the "
            "configured backend will be populated with test data on server start. Should be "
            "disabled for production usage."
        ),
    )

    use_real_mongo: Optional[bool] = Field(
        None, description="DEPRECATED: force usage of MongoDB over any other backend."
    )

    database_backend: SupportedBackend = Field(
        SupportedBackend.MONGOMOCK,
        description="Which database backend to use out of the supported backends.",
    )

    elastic_hosts: Optional[List[Dict]] = Field(
        None, description="Host settings to pass through to the `Elasticsearch` class."
    )

    mongo_database: str = Field(
        "optimade", description="Mongo database for collection data"
    )
    mongo_uri: str = Field("localhost:27017", description="URI for the Mongo server")
    links_collection: str = Field(
        "links", description="Mongo collection name for /links endpoint resources"
    )
    references_collection: str = Field(
        "references",
        description="Mongo collection name for /references endpoint resources",
    )
    structures_collection: str = Field(
        "structures",
        description="Mongo collection name for /structures endpoint resources",
    )
    page_limit: int = Field(20, description="Default number of resources per page")
    page_limit_max: int = Field(
        500, description="Max allowed number of resources per page"
    )
    default_db: str = Field(
        "test_server",
        description=(
            "ID of /links endpoint resource for the chosen default OPTIMADE implementation (only "
            "relevant for the index meta-database)"
        ),
    )
    root_path: Optional[str] = Field(
        None,
        description=(
            "Sets the FastAPI app `root_path` parameter. This can be used to serve the API under a"
            " path prefix behind a proxy or as a sub-application of another FastAPI app. See "
            "https://fastapi.tiangolo.com/advanced/sub-applications/#technical-details-root_path "
            "for details."
        ),
    )
    base_url: Optional[str] = Field(
        None, description="Base URL for this implementation"
    )
    implementation: Implementation = Field(
        Implementation(
            name="OPTIMADE Python Tools",
            version=__version__,
            source_url="https://github.com/Materials-Consortia/optimade-python-tools",
            maintainer={"email": "dev@optimade.org"},
            issue_tracker="https://github.com/Materials-Consortia/optimade-python-tools/issues",
        ),
        description="Introspective information about this OPTIMADE implementation",
    )
    index_base_url: Optional[AnyHttpUrl] = Field(
        None,
        description="An optional link to the base URL for the index meta-database of the provider.",
    )
    provider: Provider = Field(
        Provider(
            prefix="exmpl",
            name="Example provider",
            description="Provider used for examples, not to be assigned to a real database",
            homepage="https://example.com",
        ),
        description="General information about the provider of this OPTIMADE implementation",
    )
    provider_fields: Dict[
        Literal["links", "references", "structures"],
        List[Union[str, Dict[Literal["name", "type", "unit", "description"], str]]],
    ] = Field(
        {},
        description=(
            "A list of additional fields to be served with the provider's prefix attached, "
            "broken down by endpoint."
        ),
    )
    aliases: Dict[Literal["links", "references", "structures"], Dict[str, str]] = Field(
        {},
        description=(
            "A mapping between field names in the database with their corresponding OPTIMADE field"
            " names, broken down by endpoint."
        ),
    )
    length_aliases: Dict[
        Literal["links", "references", "structures"], Dict[str, str]
    ] = Field(
        {},
        description=(
            "A mapping between a list property (or otherwise) and an integer property that defines"
            " the length of that list, for example elements -> nelements. The standard aliases are"
            " applied first, so this dictionary must refer to the API fields, not the database "
            "fields."
        ),
    )
    index_links_path: Path = Field(
        Path(__file__).parent.joinpath("index_links.json"),
        description=(
            "Absolute path to a JSON file containing the MongoDB collecton of links entries "
            "(documents) to serve under the /links endpoint of the index meta-database. "
            "NB! As suggested in the previous sentence, these will only be served when using a "
            "MongoDB-based backend."
        ),
    )
    schema_url: Optional[Union[str, AnyHttpUrl]] = Field(
        f"https://schemas.optimade.org/openapi/v{__api_version__}/optimade.json",
        description=(
            "A URL that will be provided in the `meta->schema` field for every response"
        ),
    )

    index_schema_url: Optional[Union[str, AnyHttpUrl]] = Field(
        f"https://schemas.optimade.org/openapi/v{__api_version__}/optimade_index.json",
        description=(
            "A URL that will be provided in the `meta->schema` field for every response from the index meta-database."
        ),
    )

    log_level: LogLevel = Field(
        LogLevel.INFO, description="Logging level for the OPTIMADE server."
    )
    log_dir: Path = Field(
        Path("/var/log/optimade/"),
        description="Folder in which log files will be saved.",
    )
    validate_query_parameters: Optional[bool] = Field(
        True,
        description="If True, the server will check whether the query parameters given in the request are correct.",
    )

    @validator("implementation", pre=True)
    def set_implementation_version(cls, v):
        """Set defaults and modify bypassed value(s)"""
        res = {"version": __version__}
        res.update(v)
        return res

    @root_validator(pre=True)
    def use_real_mongo_override(cls, values):
        """Overrides the `database_backend` setting with MongoDB and
        raises a deprecation warning.

        """
        use_real_mongo = values.pop("use_real_mongo", None)
        if use_real_mongo is not None:
            warnings.warn(
                "'use_real_mongo' is deprecated, please set the appropriate 'database_backend' "
                "instead.",
                DeprecationWarning,
            )

            if use_real_mongo:
                values["database_backend"] = SupportedBackend.MONGODB

        return values

    class Config:
        """
        This is a pydantic model Config object that modifies the behaviour of
        ServerConfig by adding a prefix to the environment variables that
        override config file values. It has nothing to do with the OPTIMADE
        config.

        """

        env_prefix = "optimade_"
        extra = "allow"
        env_file_encoding = "utf-8"

        @classmethod
        def customise_sources(
            cls,
            init_settings: SettingsSourceCallable,
            env_settings: SettingsSourceCallable,
            file_secret_settings: SettingsSourceCallable,
        ) -> Tuple[SettingsSourceCallable, ...]:
            """
            **Priority of config settings sources**:

            1. Passed arguments upon initialization of
               [`ServerConfig`][optimade.server.config.ServerConfig].
            2. Environment variables, matching the syntax: `"OPTIMADE_"` or `"optimade_"` +
               `<config_name>`, e.g., `OPTIMADE_LOG_LEVEL=debug` or
               `optimade_log_dir=~/logs_dir/optimade/`.
            3. Configuration file (JSON/YAML) taken from:
               1. Environment variable `OPTIMADE_CONFIG_FILE`.
               2. Default location (see
                  [DEFAULT_CONFIG_FILE_PATH][optimade.server.config.DEFAULT_CONFIG_FILE_PATH]).
            4. Settings from secret file (see
               [pydantic documentation](https://pydantic-docs.helpmanual.io/usage/settings/#secret-support)
               for more information).

            """
            return (
                init_settings,
                env_settings,
                config_file_settings,
                file_secret_settings,
            )

aliases: Dict[Literal['links', 'references', 'structures'], Dict[str, str]] = Field({}, description='A mapping between field names in the database with their corresponding OPTIMADE field names, broken down by endpoint.') class-attribute

base_url: Optional[str] = Field(None, description='Base URL for this implementation') class-attribute

database_backend: SupportedBackend = Field(SupportedBackend.MONGOMOCK, description='Which database backend to use out of the supported backends.') class-attribute

debug: bool = Field(False, description='Turns on Debug Mode for the OPTIMADE Server implementation') class-attribute

default_db: str = Field('test_server', description='ID of /links endpoint resource for the chosen default OPTIMADE implementation (only relevant for the index meta-database)') class-attribute

elastic_hosts: Optional[List[Dict]] = Field(None, description='Host settings to pass through to the `Elasticsearch` class.') class-attribute

implementation: Implementation = Field(Implementation(name='OPTIMADE Python Tools', version=__version__, source_url='https://github.com/Materials-Consortia/optimade-python-tools', maintainer={'email': 'dev@optimade.org'}, issue_tracker='https://github.com/Materials-Consortia/optimade-python-tools/issues'), description='Introspective information about this OPTIMADE implementation') class-attribute

index_base_url: Optional[AnyHttpUrl] = Field(None, description='An optional link to the base URL for the index meta-database of the provider.') class-attribute

index_schema_url: Optional[Union[str, AnyHttpUrl]] = Field(f'https://schemas.optimade.org/openapi/v{__api_version__}/optimade_index.json', description='A URL that will be provided in the `meta->schema` field for every response from the index meta-database.') class-attribute

insert_test_data: bool = Field(True, description='Insert test data into each collection on server initialisation. If true, the configured backend will be populated with test data on server start. Should be disabled for production usage.') class-attribute

length_aliases: Dict[Literal['links', 'references', 'structures'], Dict[str, str]] = Field({}, description='A mapping between a list property (or otherwise) and an integer property that defines the length of that list, for example elements -> nelements. The standard aliases are applied first, so this dictionary must refer to the API fields, not the database fields.') class-attribute

log_dir: Path = Field(Path('/var/log/optimade/'), description='Folder in which log files will be saved.') class-attribute

log_level: LogLevel = Field(LogLevel.INFO, description='Logging level for the OPTIMADE server.') class-attribute

mongo_database: str = Field('optimade', description='Mongo database for collection data') class-attribute

mongo_uri: str = Field('localhost:27017', description='URI for the Mongo server') class-attribute

page_limit: int = Field(20, description='Default number of resources per page') class-attribute

page_limit_max: int = Field(500, description='Max allowed number of resources per page') class-attribute

provider: Provider = Field(Provider(prefix='exmpl', name='Example provider', description='Provider used for examples, not to be assigned to a real database', homepage='https://example.com'), description='General information about the provider of this OPTIMADE implementation') class-attribute

provider_fields: Dict[Literal['links', 'references', 'structures'], List[Union[str, Dict[Literal['name', 'type', 'unit', 'description'], str]]]] = Field({}, description="A list of additional fields to be served with the provider's prefix attached, broken down by endpoint.") class-attribute

references_collection: str = Field('references', description='Mongo collection name for /references endpoint resources') class-attribute

root_path: Optional[str] = Field(None, description='Sets the FastAPI app `root_path` parameter. This can be used to serve the API under a path prefix behind a proxy or as a sub-application of another FastAPI app. See https://fastapi.tiangolo.com/advanced/sub-applications/#technical-details-root_path for details.') class-attribute

schema_url: Optional[Union[str, AnyHttpUrl]] = Field(f'https://schemas.optimade.org/openapi/v{__api_version__}/optimade.json', description='A URL that will be provided in the `meta->schema` field for every response') class-attribute

structures_collection: str = Field('structures', description='Mongo collection name for /structures endpoint resources') class-attribute

use_real_mongo: Optional[bool] = Field(None, description='DEPRECATED: force usage of MongoDB over any other backend.') class-attribute

validate_query_parameters: Optional[bool] = Field(True, description='If True, the server will check whether the query parameters given in the request are correct.') class-attribute

Config

This is a pydantic model Config object that modifies the behaviour of ServerConfig by adding a prefix to the environment variables that override config file values. It has nothing to do with the OPTIMADE config.

Source code in optimade/server/config.py
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
class Config:
    """
    This is a pydantic model Config object that modifies the behaviour of
    ServerConfig by adding a prefix to the environment variables that
    override config file values. It has nothing to do with the OPTIMADE
    config.

    """

    env_prefix = "optimade_"
    extra = "allow"
    env_file_encoding = "utf-8"

    @classmethod
    def customise_sources(
        cls,
        init_settings: SettingsSourceCallable,
        env_settings: SettingsSourceCallable,
        file_secret_settings: SettingsSourceCallable,
    ) -> Tuple[SettingsSourceCallable, ...]:
        """
        **Priority of config settings sources**:

        1. Passed arguments upon initialization of
           [`ServerConfig`][optimade.server.config.ServerConfig].
        2. Environment variables, matching the syntax: `"OPTIMADE_"` or `"optimade_"` +
           `<config_name>`, e.g., `OPTIMADE_LOG_LEVEL=debug` or
           `optimade_log_dir=~/logs_dir/optimade/`.
        3. Configuration file (JSON/YAML) taken from:
           1. Environment variable `OPTIMADE_CONFIG_FILE`.
           2. Default location (see
              [DEFAULT_CONFIG_FILE_PATH][optimade.server.config.DEFAULT_CONFIG_FILE_PATH]).
        4. Settings from secret file (see
           [pydantic documentation](https://pydantic-docs.helpmanual.io/usage/settings/#secret-support)
           for more information).

        """
        return (
            init_settings,
            env_settings,
            config_file_settings,
            file_secret_settings,
        )

env_file_encoding = 'utf-8' class-attribute

env_prefix = 'optimade_' class-attribute

extra = 'allow' class-attribute

customise_sources(init_settings, env_settings, file_secret_settings) classmethod

Priority of config settings sources:

  1. Passed arguments upon initialization of ServerConfig.
  2. Environment variables, matching the syntax: "OPTIMADE_" or "optimade_" + <config_name>, e.g., OPTIMADE_LOG_LEVEL=debug or optimade_log_dir=~/logs_dir/optimade/.
  3. Configuration file (JSON/YAML) taken from:
  4. Environment variable OPTIMADE_CONFIG_FILE.
  5. Default location (see DEFAULT_CONFIG_FILE_PATH).
  6. Settings from secret file (see pydantic documentation for more information).
Source code in optimade/server/config.py
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
@classmethod
def customise_sources(
    cls,
    init_settings: SettingsSourceCallable,
    env_settings: SettingsSourceCallable,
    file_secret_settings: SettingsSourceCallable,
) -> Tuple[SettingsSourceCallable, ...]:
    """
    **Priority of config settings sources**:

    1. Passed arguments upon initialization of
       [`ServerConfig`][optimade.server.config.ServerConfig].
    2. Environment variables, matching the syntax: `"OPTIMADE_"` or `"optimade_"` +
       `<config_name>`, e.g., `OPTIMADE_LOG_LEVEL=debug` or
       `optimade_log_dir=~/logs_dir/optimade/`.
    3. Configuration file (JSON/YAML) taken from:
       1. Environment variable `OPTIMADE_CONFIG_FILE`.
       2. Default location (see
          [DEFAULT_CONFIG_FILE_PATH][optimade.server.config.DEFAULT_CONFIG_FILE_PATH]).
    4. Settings from secret file (see
       [pydantic documentation](https://pydantic-docs.helpmanual.io/usage/settings/#secret-support)
       for more information).

    """
    return (
        init_settings,
        env_settings,
        config_file_settings,
        file_secret_settings,
    )

set_implementation_version(v)

Set defaults and modify bypassed value(s)

Source code in optimade/server/config.py
284
285
286
287
288
289
@validator("implementation", pre=True)
def set_implementation_version(cls, v):
    """Set defaults and modify bypassed value(s)"""
    res = {"version": __version__}
    res.update(v)
    return res

use_real_mongo_override(values)

Overrides the database_backend setting with MongoDB and raises a deprecation warning.

Source code in optimade/server/config.py
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
@root_validator(pre=True)
def use_real_mongo_override(cls, values):
    """Overrides the `database_backend` setting with MongoDB and
    raises a deprecation warning.

    """
    use_real_mongo = values.pop("use_real_mongo", None)
    if use_real_mongo is not None:
        warnings.warn(
            "'use_real_mongo' is deprecated, please set the appropriate 'database_backend' "
            "instead.",
            DeprecationWarning,
        )

        if use_real_mongo:
            values["database_backend"] = SupportedBackend.MONGODB

    return values

SupportedBackend

Enumeration of supported database backends

  • elastic: Elasticsearch.
  • mongodb: MongoDB.
  • mongomock: Also MongoDB, but instead of using the pymongo driver to connect to a live Mongo database instance, this will use the mongomock driver, creating an in-memory database, which is mainly used for testing.
Source code in optimade/server/config.py
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
class SupportedBackend(Enum):
    """Enumeration of supported database backends

    - `elastic`: [Elasticsearch](https://www.elastic.co/).
    - `mongodb`: [MongoDB](https://www.mongodb.com/).
    - `mongomock`: Also MongoDB, but instead of using the
        [`pymongo`](https://pymongo.readthedocs.io/) driver to connect to a live Mongo database
        instance, this will use the [`mongomock`](https://github.com/mongomock/mongomock) driver,
        creating an in-memory database, which is mainly used for testing.

    """

    ELASTIC = "elastic"
    MONGODB = "mongodb"
    MONGOMOCK = "mongomock"

ELASTIC = 'elastic' class-attribute

MONGODB = 'mongodb' class-attribute

MONGOMOCK = 'mongomock' class-attribute

config_file_settings(settings)

Configuration file settings source.

Based on the example in the pydantic documentation, this function loads ServerConfig settings from a configuration file.

The file must be of either type JSON or YML/YAML.

Parameters:

Name Type Description Default
settings BaseSettings

The pydantic.BaseSettings class using this function as a pydantic.SettingsSourceCallable.

required

Returns:

Type Description
Dict[str, Any]

Dictionary of settings as read from a file.

Source code in optimade/server/config.py
 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
def config_file_settings(settings: BaseSettings) -> Dict[str, Any]:
    """Configuration file settings source.

    Based on the example in the
    [pydantic documentation](https://pydantic-docs.helpmanual.io/usage/settings/#adding-sources),
    this function loads ServerConfig settings from a configuration file.

    The file must be of either type JSON or YML/YAML.

    Parameters:
        settings: The `pydantic.BaseSettings` class using this function as a
            `pydantic.SettingsSourceCallable`.

    Returns:
        Dictionary of settings as read from a file.

    """
    import json
    import os
    import yaml

    encoding = settings.__config__.env_file_encoding
    config_file = Path(os.getenv("OPTIMADE_CONFIG_FILE", DEFAULT_CONFIG_FILE_PATH))

    res = {}
    if config_file.is_file():
        config_file_content = config_file.read_text(encoding=encoding)

        try:
            res = json.loads(config_file_content)
        except json.JSONDecodeError as json_exc:
            try:
                # This can essentially also load JSON files, as JSON is a subset of YAML v1,
                # but I suspect it is not as rigorous
                res = yaml.safe_load(config_file_content)
            except yaml.YAMLError as yaml_exc:
                warnings.warn(
                    f"Unable to parse config file {config_file} as JSON or YAML, using the "
                    "default settings instead..\n"
                    f"Errors:\n  JSON:\n{json_exc}.\n\n  YAML:\n{yaml_exc}"
                )
    else:
        warnings.warn(
            f"Unable to find config file at {config_file}, using the default settings instead."
        )

    if res is None:
        # This can happen if the yaml loading doesn't succeed properly, e.g., if the file is empty.
        warnings.warn(
            "Unable to load any settings from {config_file}, using the default settings instead."
        )
        res = {}

    return res