| from __future__ import annotations |
|
|
| import configparser |
| import json |
| import os |
| import warnings |
| from typing import Any |
|
|
| conf: dict[str, dict[str, Any]] = {} |
| default_conf_dir = os.path.join(os.path.expanduser("~"), ".config/fsspec") |
| conf_dir = os.environ.get("FSSPEC_CONFIG_DIR", default_conf_dir) |
|
|
|
|
| def set_conf_env(conf_dict, envdict=os.environ): |
| """Set config values from environment variables |
| |
| Looks for variables of the form ``FSSPEC_<protocol>`` and |
| ``FSSPEC_<protocol>_<kwarg>``. For ``FSSPEC_<protocol>`` the value is parsed |
| as a json dictionary and used to ``update`` the config of the |
| corresponding protocol. For ``FSSPEC_<protocol>_<kwarg>`` there is no |
| attempt to convert the string value, but the kwarg keys will be lower-cased. |
| |
| The ``FSSPEC_<protocol>_<kwarg>`` variables are applied after the |
| ``FSSPEC_<protocol>`` ones. |
| |
| Parameters |
| ---------- |
| conf_dict : dict(str, dict) |
| This dict will be mutated |
| envdict : dict-like(str, str) |
| Source for the values - usually the real environment |
| """ |
| kwarg_keys = [] |
| for key in envdict: |
| if key.startswith("FSSPEC_") and len(key) > 7 and key[7] != "_": |
| if key.count("_") > 1: |
| kwarg_keys.append(key) |
| continue |
| try: |
| value = json.loads(envdict[key]) |
| except json.decoder.JSONDecodeError as ex: |
| warnings.warn( |
| f"Ignoring environment variable {key} due to a parse failure: {ex}" |
| ) |
| else: |
| if isinstance(value, dict): |
| _, proto = key.split("_", 1) |
| conf_dict.setdefault(proto.lower(), {}).update(value) |
| else: |
| warnings.warn( |
| f"Ignoring environment variable {key} due to not being a dict:" |
| f" {type(value)}" |
| ) |
| elif key.startswith("FSSPEC"): |
| warnings.warn( |
| f"Ignoring environment variable {key} due to having an unexpected name" |
| ) |
|
|
| for key in kwarg_keys: |
| _, proto, kwarg = key.split("_", 2) |
| conf_dict.setdefault(proto.lower(), {})[kwarg.lower()] = envdict[key] |
|
|
|
|
| def set_conf_files(cdir, conf_dict): |
| """Set config values from files |
| |
| Scans for INI and JSON files in the given dictionary, and uses their |
| contents to set the config. In case of repeated values, later values |
| win. |
| |
| In the case of INI files, all values are strings, and these will not |
| be converted. |
| |
| Parameters |
| ---------- |
| cdir : str |
| Directory to search |
| conf_dict : dict(str, dict) |
| This dict will be mutated |
| """ |
| if not os.path.isdir(cdir): |
| return |
| allfiles = sorted(os.listdir(cdir)) |
| for fn in allfiles: |
| if fn.endswith(".ini"): |
| ini = configparser.ConfigParser() |
| ini.read(os.path.join(cdir, fn)) |
| for key in ini: |
| if key == "DEFAULT": |
| continue |
| conf_dict.setdefault(key, {}).update(dict(ini[key])) |
| if fn.endswith(".json"): |
| with open(os.path.join(cdir, fn)) as f: |
| js = json.load(f) |
| for key in js: |
| conf_dict.setdefault(key, {}).update(dict(js[key])) |
|
|
|
|
| def apply_config(cls, kwargs, conf_dict=None): |
| """Supply default values for kwargs when instantiating class |
| |
| Augments the passed kwargs, by finding entries in the config dict |
| which match the classes ``.protocol`` attribute (one or more str) |
| |
| Parameters |
| ---------- |
| cls : file system implementation |
| kwargs : dict |
| conf_dict : dict of dict |
| Typically this is the global configuration |
| |
| Returns |
| ------- |
| dict : the modified set of kwargs |
| """ |
| if conf_dict is None: |
| conf_dict = conf |
| protos = cls.protocol if isinstance(cls.protocol, (tuple, list)) else [cls.protocol] |
| kw = {} |
| for proto in protos: |
| |
| if proto in conf_dict: |
| kw.update(conf_dict[proto]) |
| |
| kw.update(**kwargs) |
| kwargs = kw |
| return kwargs |
|
|
|
|
| set_conf_files(conf_dir, conf) |
| set_conf_env(conf) |
|
|