config.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. from __future__ import annotations
  2. import configparser
  3. import json
  4. import os
  5. import warnings
  6. from typing import Any
  7. conf: dict[str, dict[str, Any]] = {}
  8. default_conf_dir = os.path.join(os.path.expanduser("~"), ".config/fsspec")
  9. conf_dir = os.environ.get("FSSPEC_CONFIG_DIR", default_conf_dir)
  10. def set_conf_env(conf_dict, envdict=os.environ):
  11. """Set config values from environment variables
  12. Looks for variables of the form ``FSSPEC_<protocol>`` and
  13. ``FSSPEC_<protocol>_<kwarg>``. For ``FSSPEC_<protocol>`` the value is parsed
  14. as a json dictionary and used to ``update`` the config of the
  15. corresponding protocol. For ``FSSPEC_<protocol>_<kwarg>`` there is no
  16. attempt to convert the string value, but the kwarg keys will be lower-cased.
  17. The ``FSSPEC_<protocol>_<kwarg>`` variables are applied after the
  18. ``FSSPEC_<protocol>`` ones.
  19. Parameters
  20. ----------
  21. conf_dict : dict(str, dict)
  22. This dict will be mutated
  23. envdict : dict-like(str, str)
  24. Source for the values - usually the real environment
  25. """
  26. kwarg_keys = []
  27. for key in envdict:
  28. if key.startswith("FSSPEC_") and len(key) > 7 and key[7] != "_":
  29. if key.count("_") > 1:
  30. kwarg_keys.append(key)
  31. continue
  32. try:
  33. value = json.loads(envdict[key])
  34. except json.decoder.JSONDecodeError as ex:
  35. warnings.warn(
  36. f"Ignoring environment variable {key} due to a parse failure: {ex}"
  37. )
  38. else:
  39. if isinstance(value, dict):
  40. _, proto = key.split("_", 1)
  41. conf_dict.setdefault(proto.lower(), {}).update(value)
  42. else:
  43. warnings.warn(
  44. f"Ignoring environment variable {key} due to not being a dict:"
  45. f" {type(value)}"
  46. )
  47. elif key.startswith("FSSPEC"):
  48. warnings.warn(
  49. f"Ignoring environment variable {key} due to having an unexpected name"
  50. )
  51. for key in kwarg_keys:
  52. _, proto, kwarg = key.split("_", 2)
  53. conf_dict.setdefault(proto.lower(), {})[kwarg.lower()] = envdict[key]
  54. def set_conf_files(cdir, conf_dict):
  55. """Set config values from files
  56. Scans for INI and JSON files in the given dictionary, and uses their
  57. contents to set the config. In case of repeated values, later values
  58. win.
  59. In the case of INI files, all values are strings, and these will not
  60. be converted.
  61. Parameters
  62. ----------
  63. cdir : str
  64. Directory to search
  65. conf_dict : dict(str, dict)
  66. This dict will be mutated
  67. """
  68. if not os.path.isdir(cdir):
  69. return
  70. allfiles = sorted(os.listdir(cdir))
  71. for fn in allfiles:
  72. if fn.endswith(".ini"):
  73. ini = configparser.ConfigParser()
  74. ini.read(os.path.join(cdir, fn))
  75. for key in ini:
  76. if key == "DEFAULT":
  77. continue
  78. conf_dict.setdefault(key, {}).update(dict(ini[key]))
  79. if fn.endswith(".json"):
  80. with open(os.path.join(cdir, fn)) as f:
  81. js = json.load(f)
  82. for key in js:
  83. conf_dict.setdefault(key, {}).update(dict(js[key]))
  84. def apply_config(cls, kwargs, conf_dict=None):
  85. """Supply default values for kwargs when instantiating class
  86. Augments the passed kwargs, by finding entries in the config dict
  87. which match the classes ``.protocol`` attribute (one or more str)
  88. Parameters
  89. ----------
  90. cls : file system implementation
  91. kwargs : dict
  92. conf_dict : dict of dict
  93. Typically this is the global configuration
  94. Returns
  95. -------
  96. dict : the modified set of kwargs
  97. """
  98. if conf_dict is None:
  99. conf_dict = conf
  100. protos = cls.protocol if isinstance(cls.protocol, (tuple, list)) else [cls.protocol]
  101. kw = {}
  102. for proto in protos:
  103. # default kwargs from the current state of the config
  104. if proto in conf_dict:
  105. kw.update(conf_dict[proto])
  106. # explicit kwargs always win
  107. kw.update(**kwargs)
  108. kwargs = kw
  109. return kwargs
  110. set_conf_files(conf_dir, conf)
  111. set_conf_env(conf)