granular-configuration-language 2.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. granular_configuration_language-2.0.0/PKG-INFO +131 -0
  2. granular_configuration_language-2.0.0/README.md +104 -0
  3. granular_configuration_language-2.0.0/granular_configuration_language/__init__.py +12 -0
  4. granular_configuration_language-2.0.0/granular_configuration_language/_base_path.py +23 -0
  5. granular_configuration_language-2.0.0/granular_configuration_language/_build.py +94 -0
  6. granular_configuration_language-2.0.0/granular_configuration_language/_cache.py +105 -0
  7. granular_configuration_language-2.0.0/granular_configuration_language/_configuration.py +407 -0
  8. granular_configuration_language-2.0.0/granular_configuration_language/_json.py +72 -0
  9. granular_configuration_language-2.0.0/granular_configuration_language/_lazy_load_configuration.py +296 -0
  10. granular_configuration_language-2.0.0/granular_configuration_language/_load.py +28 -0
  11. granular_configuration_language-2.0.0/granular_configuration_language/_locations.py +121 -0
  12. granular_configuration_language-2.0.0/granular_configuration_language/_merge.py +59 -0
  13. granular_configuration_language-2.0.0/granular_configuration_language/_s.py +3 -0
  14. granular_configuration_language-2.0.0/granular_configuration_language/_utils.py +50 -0
  15. granular_configuration_language-2.0.0/granular_configuration_language/available_tags.py +14 -0
  16. granular_configuration_language-2.0.0/granular_configuration_language/exceptions.py +81 -0
  17. granular_configuration_language-2.0.0/granular_configuration_language/proxy.py +3 -0
  18. granular_configuration_language-2.0.0/granular_configuration_language/py.typed +0 -0
  19. granular_configuration_language-2.0.0/granular_configuration_language/yaml/__init__.py +4 -0
  20. granular_configuration_language-2.0.0/granular_configuration_language/yaml/_tags/__init__.py +7 -0
  21. granular_configuration_language-2.0.0/granular_configuration_language/yaml/_tags/_date.py +31 -0
  22. granular_configuration_language-2.0.0/granular_configuration_language/yaml/_tags/_del.py +9 -0
  23. granular_configuration_language-2.0.0/granular_configuration_language/yaml/_tags/_dict.py +10 -0
  24. granular_configuration_language-2.0.0/granular_configuration_language/yaml/_tags/_env.py +19 -0
  25. granular_configuration_language-2.0.0/granular_configuration_language/yaml/_tags/_mask.py +11 -0
  26. granular_configuration_language-2.0.0/granular_configuration_language/yaml/_tags/_merge.py +20 -0
  27. granular_configuration_language-2.0.0/granular_configuration_language/yaml/_tags/_parse_env.py +63 -0
  28. granular_configuration_language-2.0.0/granular_configuration_language/yaml/_tags/_parse_file.py +47 -0
  29. granular_configuration_language-2.0.0/granular_configuration_language/yaml/_tags/_placeholder.py +10 -0
  30. granular_configuration_language-2.0.0/granular_configuration_language/yaml/_tags/_ref.py +17 -0
  31. granular_configuration_language-2.0.0/granular_configuration_language/yaml/_tags/_sub.py +17 -0
  32. granular_configuration_language-2.0.0/granular_configuration_language/yaml/_tags/_uuid.py +12 -0
  33. granular_configuration_language-2.0.0/granular_configuration_language/yaml/_tags/func_and_class.py +48 -0
  34. granular_configuration_language-2.0.0/granular_configuration_language/yaml/classes.py +200 -0
  35. granular_configuration_language-2.0.0/granular_configuration_language/yaml/decorators/__init__.py +21 -0
  36. granular_configuration_language-2.0.0/granular_configuration_language/yaml/decorators/_base.py +238 -0
  37. granular_configuration_language-2.0.0/granular_configuration_language/yaml/decorators/_lazy.py +180 -0
  38. granular_configuration_language-2.0.0/granular_configuration_language/yaml/decorators/_lazy_eval.py +24 -0
  39. granular_configuration_language-2.0.0/granular_configuration_language/yaml/decorators/_tag_loader.py +159 -0
  40. granular_configuration_language-2.0.0/granular_configuration_language/yaml/decorators/_tag_tracker.py +34 -0
  41. granular_configuration_language-2.0.0/granular_configuration_language/yaml/decorators/_type_checking.py +122 -0
  42. granular_configuration_language-2.0.0/granular_configuration_language/yaml/decorators/interpolate/__init__.py +7 -0
  43. granular_configuration_language-2.0.0/granular_configuration_language/yaml/decorators/interpolate/_env_var_parser.py +43 -0
  44. granular_configuration_language-2.0.0/granular_configuration_language/yaml/decorators/interpolate/_interpolate.py +182 -0
  45. granular_configuration_language-2.0.0/granular_configuration_language/yaml/decorators/ref/__init__.py +3 -0
  46. granular_configuration_language-2.0.0/granular_configuration_language/yaml/decorators/ref/_ref.py +72 -0
  47. granular_configuration_language-2.0.0/granular_configuration_language/yaml/load/__init__.py +3 -0
  48. granular_configuration_language-2.0.0/granular_configuration_language/yaml/load/_constructors.py +29 -0
  49. granular_configuration_language-2.0.0/granular_configuration_language/yaml/load/_external.py +45 -0
  50. granular_configuration_language-2.0.0/granular_configuration_language/yaml/load/_handler.py +39 -0
  51. granular_configuration_language-2.0.0/pyproject.toml +150 -0
@@ -0,0 +1,131 @@
1
+ Metadata-Version: 2.3
2
+ Name: granular-configuration-language
3
+ Version: 2.0.0
4
+ Summary: This configuration utility library allows your code use YAML as a configuration language for internal and external parties, allowing configuration to be crafted from multiple sources and merged just before use, with added YAML Tags that run lazily for added functionality.
5
+ License: MIT
6
+ Author: Eric Jensen
7
+ Author-email: eric.jensen42@gmail.com
8
+ Requires-Python: >=3.10
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Natural Language :: English
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Utilities
18
+ Classifier: Typing :: Typed
19
+ Requires-Dist: python-dateutil ; python_version < "3.11"
20
+ Requires-Dist: python-jsonpath
21
+ Requires-Dist: ruamel.yaml (>=0.18)
22
+ Project-URL: Documentation, https://lifedox.github.io/granular-configuration-language/README.html
23
+ Project-URL: Homepage, https://lifedox.github.io/granular-configuration-language/README.html
24
+ Project-URL: Repository, https://github.com/lifedox/granular-configuration-language
25
+ Description-Content-Type: text/markdown
26
+
27
+ # `granular-configuration-language`
28
+
29
+ [![Coverage badge](https://raw.githubusercontent.com/lifedox/granular-configuration-language/python-coverage-comment-action-data/badge.svg)](https://github.com/lifedox/granular-configuration-language/tree/python-coverage-comment-action-data) ![Testing workflow](https://github.com/lifedox/granular-configuration-language/actions/workflows/testing.yaml/badge.svg?event=push) ![codeql workflow](https://github.com/lifedox/granular-configuration-language/actions/workflows/codeql-analysis.yaml/badge.svg?event=push)
30
+
31
+ > ⚠️ **This library is meant for trusted configuration files.** ⚠️
32
+
33
+ Get started or read more at the [documentation site](https://lifedox.github.io/granular-configuration-language/doc-spec/getting_started.html).
34
+
35
+ ## Why does this exist?
36
+
37
+ This library exists to allow your code use YAML as a configuration language for internal and external parties, allowing configuration to be crafted from multiple sources and merged just before use, with added [YAML Tags](https://lifedox.github.io/granular-configuration-language/doc-spec/yaml.html) that run lazily for added functionality, and plugin support for creating custom YAML Tags.
38
+
39
+ Some use cases:
40
+
41
+ - You are writing a library to help connect to some databases. You want users to easily changes settings and defined databases by name.
42
+ - Conceptual Example:
43
+ - Library Code:
44
+ ```python
45
+ CONFIG = LazyLoadConfiguration(
46
+ Path(__file___).parent / "config.yaml",
47
+ "./database-util-config.yaml",
48
+ "~/configs/database-util-config.yaml",
49
+ base_path="database-util",
50
+ env_location_var_name="ORG_COMMON_CONFIG_LOCATIONS",
51
+ )
52
+ ```
53
+ - Library configuration:
54
+ ```yaml
55
+ database-util:
56
+ common_settings:
57
+ use_decimal: true
58
+ encryption_type: secure
59
+ databases: {} # Empty mapping, for users define
60
+ ```
61
+ - User application configuration:
62
+ ```yaml
63
+ database-util:
64
+ common_settings:
65
+ use_decimal: false
66
+ databases:
67
+ datebase1:
68
+ location: http://somewhere
69
+ user: !Mask ${DB_USERNAME}
70
+ password: !Mask ${DB_PASSWORD}
71
+ ```
72
+ - You are deploying an application that has multiple deployment types with specific settings.
73
+ - Conceptual Example:
74
+ - Library Code:
75
+ ```python
76
+ CONFIG = LazyLoadConfiguration(
77
+ Path(__file___).parent / "config.yaml",
78
+ "./database-util-config.yaml",
79
+ base_path="app",
80
+ )
81
+ ```
82
+ - Base configuration:
83
+ ```yaml
84
+ app:
85
+ log_as: really cool app name
86
+ log_to: nowhere
87
+ ```
88
+ - AWS Lambda deploy:
89
+ ```yaml
90
+ app:
91
+ log_to: std_out
92
+ ```
93
+ - Server deploy:
94
+ ```yaml
95
+ app:
96
+ log_to: !Sub file://var/log/${$.app.log_as}.log
97
+ ```
98
+ - You are writing a [`pytest`](https://docs.pytest.org/en/stable/) plugin that create test data using named fixtures configured by the user.
99
+ - Conceptual Examples:
100
+ - Library Code:
101
+ ```python
102
+ CONFIG = LazyLoadConfiguration(
103
+ Path(__file___).parent / "fixture_config.yaml",
104
+ *Path().rglob("fixture_config.yaml"),
105
+ base_path="fixture-gen",
106
+ ).config
107
+ #
108
+ for name, spec in CONFIG.fixtures:
109
+ generate_fixture(name, spec)
110
+ ```
111
+ - Library configuration:
112
+ ```yaml
113
+ fixture-gen:
114
+ fixtures: {} # Empty mapping, for users define
115
+ ```
116
+ - User application configuration:
117
+ ```yaml
118
+ fixture-gen:
119
+ fixtures:
120
+ fixture1:
121
+ api: does something
122
+ ```
123
+
124
+ ## Why the long name?
125
+
126
+ - It's "granular" because you can specify settings across multiple files at a fine granularity for overriding values.
127
+ - It is meant for trusted "configuration" files.
128
+ - Including "language" make it clear that this is not the source of configuration.
129
+ - A valid piece of feedback was that it sounded like it was the source for configuration, not the processing of generic configuration files.
130
+ - "Format" sounded weirder than "language".
131
+
@@ -0,0 +1,104 @@
1
+ # `granular-configuration-language`
2
+
3
+ [![Coverage badge](https://raw.githubusercontent.com/lifedox/granular-configuration-language/python-coverage-comment-action-data/badge.svg)](https://github.com/lifedox/granular-configuration-language/tree/python-coverage-comment-action-data) ![Testing workflow](https://github.com/lifedox/granular-configuration-language/actions/workflows/testing.yaml/badge.svg?event=push) ![codeql workflow](https://github.com/lifedox/granular-configuration-language/actions/workflows/codeql-analysis.yaml/badge.svg?event=push)
4
+
5
+ > ⚠️ **This library is meant for trusted configuration files.** ⚠️
6
+
7
+ Get started or read more at the [documentation site](https://lifedox.github.io/granular-configuration-language/doc-spec/getting_started.html).
8
+
9
+ ## Why does this exist?
10
+
11
+ This library exists to allow your code use YAML as a configuration language for internal and external parties, allowing configuration to be crafted from multiple sources and merged just before use, with added [YAML Tags](https://lifedox.github.io/granular-configuration-language/doc-spec/yaml.html) that run lazily for added functionality, and plugin support for creating custom YAML Tags.
12
+
13
+ Some use cases:
14
+
15
+ - You are writing a library to help connect to some databases. You want users to easily changes settings and defined databases by name.
16
+ - Conceptual Example:
17
+ - Library Code:
18
+ ```python
19
+ CONFIG = LazyLoadConfiguration(
20
+ Path(__file___).parent / "config.yaml",
21
+ "./database-util-config.yaml",
22
+ "~/configs/database-util-config.yaml",
23
+ base_path="database-util",
24
+ env_location_var_name="ORG_COMMON_CONFIG_LOCATIONS",
25
+ )
26
+ ```
27
+ - Library configuration:
28
+ ```yaml
29
+ database-util:
30
+ common_settings:
31
+ use_decimal: true
32
+ encryption_type: secure
33
+ databases: {} # Empty mapping, for users define
34
+ ```
35
+ - User application configuration:
36
+ ```yaml
37
+ database-util:
38
+ common_settings:
39
+ use_decimal: false
40
+ databases:
41
+ datebase1:
42
+ location: http://somewhere
43
+ user: !Mask ${DB_USERNAME}
44
+ password: !Mask ${DB_PASSWORD}
45
+ ```
46
+ - You are deploying an application that has multiple deployment types with specific settings.
47
+ - Conceptual Example:
48
+ - Library Code:
49
+ ```python
50
+ CONFIG = LazyLoadConfiguration(
51
+ Path(__file___).parent / "config.yaml",
52
+ "./database-util-config.yaml",
53
+ base_path="app",
54
+ )
55
+ ```
56
+ - Base configuration:
57
+ ```yaml
58
+ app:
59
+ log_as: really cool app name
60
+ log_to: nowhere
61
+ ```
62
+ - AWS Lambda deploy:
63
+ ```yaml
64
+ app:
65
+ log_to: std_out
66
+ ```
67
+ - Server deploy:
68
+ ```yaml
69
+ app:
70
+ log_to: !Sub file://var/log/${$.app.log_as}.log
71
+ ```
72
+ - You are writing a [`pytest`](https://docs.pytest.org/en/stable/) plugin that create test data using named fixtures configured by the user.
73
+ - Conceptual Examples:
74
+ - Library Code:
75
+ ```python
76
+ CONFIG = LazyLoadConfiguration(
77
+ Path(__file___).parent / "fixture_config.yaml",
78
+ *Path().rglob("fixture_config.yaml"),
79
+ base_path="fixture-gen",
80
+ ).config
81
+ #
82
+ for name, spec in CONFIG.fixtures:
83
+ generate_fixture(name, spec)
84
+ ```
85
+ - Library configuration:
86
+ ```yaml
87
+ fixture-gen:
88
+ fixtures: {} # Empty mapping, for users define
89
+ ```
90
+ - User application configuration:
91
+ ```yaml
92
+ fixture-gen:
93
+ fixtures:
94
+ fixture1:
95
+ api: does something
96
+ ```
97
+
98
+ ## Why the long name?
99
+
100
+ - It's "granular" because you can specify settings across multiple files at a fine granularity for overriding values.
101
+ - It is meant for trusted "configuration" files.
102
+ - Including "language" make it clear that this is not the source of configuration.
103
+ - A valid piece of feedback was that it sounded like it was the source for configuration, not the processing of generic configuration files.
104
+ - "Format" sounded weirder than "language".
@@ -0,0 +1,12 @@
1
+ # isort:skip_file
2
+ import granular_configuration_language.yaml.classes # Needs to import before _configuration to prevent circular import
3
+ from granular_configuration_language._configuration import Configuration, MutableConfiguration
4
+ from granular_configuration_language._lazy_load_configuration import (
5
+ LazyLoadConfiguration,
6
+ LazyLoadConfiguration as LLC,
7
+ MutableLazyLoadConfiguration,
8
+ )
9
+ from granular_configuration_language._merge import merge # depends on LazyLoadConfiguration
10
+
11
+ from granular_configuration_language._json import json_default
12
+ from granular_configuration_language.yaml import Masked, Placeholder
@@ -0,0 +1,23 @@
1
+ from __future__ import annotations
2
+
3
+ import collections.abc as tabc
4
+
5
+
6
+ class BasePathPart(str):
7
+ pass
8
+
9
+
10
+ class BasePath(tuple[BasePathPart]):
11
+ pass
12
+
13
+
14
+ def read_base_path(base_path: str | tabc.Sequence[str] | None) -> BasePath:
15
+ if isinstance(base_path, str):
16
+ if base_path.startswith("/"):
17
+ return BasePath(map(BasePathPart, filter(None, base_path.split("/"))))
18
+ else:
19
+ return BasePath((BasePathPart(base_path),))
20
+ elif base_path:
21
+ return BasePath(map(BasePathPart, base_path))
22
+ else:
23
+ return BasePath()
@@ -0,0 +1,94 @@
1
+ from __future__ import annotations
2
+
3
+ import collections.abc as tabc
4
+ import typing as typ
5
+ from functools import partial
6
+ from itertools import chain
7
+ from pathlib import Path
8
+
9
+ from granular_configuration_language import Configuration
10
+ from granular_configuration_language._load import load_file
11
+ from granular_configuration_language._s import setter_secret
12
+ from granular_configuration_language._utils import consume
13
+ from granular_configuration_language.yaml import LazyRoot
14
+ from granular_configuration_language.yaml.load import obj_pairs_func
15
+
16
+ _C = typ.TypeVar("_C", bound=Configuration)
17
+
18
+
19
+ def _merge_into_base(configuration_type: typ.Type[_C], base_dict: _C, from_dict: _C) -> None:
20
+ for key, value in from_dict._raw_items():
21
+ if isinstance(value, configuration_type) and (key in base_dict):
22
+ if base_dict.exists(key):
23
+ new_dict = base_dict[key]
24
+ else: # If Placeholder
25
+ new_dict = configuration_type()
26
+
27
+ if isinstance(new_dict, configuration_type):
28
+ _merge_into_base(configuration_type, new_dict, value)
29
+ value = new_dict
30
+
31
+ base_dict._private_set(key, value, setter_secret)
32
+
33
+
34
+ def _merge(configuration_type: typ.Type[_C], base_config: _C, configs: tabc.Iterable[_C]) -> _C:
35
+ consume(map(partial(_merge_into_base, configuration_type, base_config), configs))
36
+ return base_config
37
+
38
+
39
+ def _load_configs_from_locations(
40
+ configuration_type: typ.Type[_C], locations: tabc.Iterable[Path], lazy_root: LazyRoot, mutable: bool
41
+ ) -> tabc.Iterator[_C]:
42
+ def configuration_only(
43
+ configs: tabc.Iterable[_C | typ.Any],
44
+ ) -> tabc.Iterator[_C]:
45
+ for config in configs:
46
+ if isinstance(config, configuration_type):
47
+ yield config
48
+
49
+ _load_file = partial(load_file, lazy_root=lazy_root, mutable=mutable)
50
+ return configuration_only(map(_load_file, locations))
51
+
52
+
53
+ def _inject(*new: Configuration, configs: tabc.Iterator[_C], in_front: bool) -> tabc.Iterator[_C]:
54
+ cast = typ.cast(_C, new)
55
+ if in_front:
56
+ return chain(cast, configs)
57
+ else:
58
+ return chain(configs, cast)
59
+
60
+
61
+ def _inject_configs(
62
+ configs: tabc.Iterator[_C],
63
+ *,
64
+ before: Configuration | None,
65
+ after: Configuration | None,
66
+ ) -> tabc.Iterator[_C]:
67
+ if before and isinstance(before, Configuration):
68
+ configs = _inject(before, configs=configs, in_front=True)
69
+
70
+ if after and isinstance(after, Configuration):
71
+ configs = _inject(after, configs=configs, in_front=False)
72
+
73
+ return configs
74
+
75
+
76
+ def build_configuration(
77
+ locations: tabc.Iterable[Path],
78
+ mutable: bool,
79
+ *,
80
+ inject_before: Configuration | None,
81
+ inject_after: Configuration | None,
82
+ ) -> Configuration:
83
+ configuration_type = obj_pairs_func(mutable)
84
+ base_config = configuration_type()
85
+ lazy_root = LazyRoot.with_root(base_config)
86
+
87
+ valid_configs = _inject_configs(
88
+ _load_configs_from_locations(configuration_type, locations, lazy_root, mutable),
89
+ before=inject_before,
90
+ after=inject_after,
91
+ )
92
+ merged_config = _merge(configuration_type, base_config, valid_configs)
93
+
94
+ return merged_config
@@ -0,0 +1,105 @@
1
+ from __future__ import annotations
2
+
3
+ import collections.abc as tabc
4
+ import dataclasses
5
+ import operator as op
6
+ import typing as typ
7
+ from collections import deque
8
+ from functools import cached_property, reduce
9
+ from threading import Lock
10
+ from weakref import WeakValueDictionary
11
+
12
+ from granular_configuration_language._base_path import BasePath, read_base_path
13
+ from granular_configuration_language._build import build_configuration
14
+ from granular_configuration_language._configuration import Configuration
15
+ from granular_configuration_language._locations import Locations
16
+
17
+
18
+ @dataclasses.dataclass(frozen=False, eq=False, kw_only=True)
19
+ class SharedConfigurationReference:
20
+ _locations: Locations
21
+ _mutable_config: bool
22
+ _inject_before: Configuration | None = None
23
+ _inject_after: Configuration | None = None
24
+ __lock: Lock | None = dataclasses.field(repr=False, compare=False, init=False, default_factory=Lock)
25
+ __notes: deque[NoteOfIntentToRead] = dataclasses.field(repr=False, compare=False, init=False, default_factory=deque)
26
+
27
+ def register(self, note: NoteOfIntentToRead) -> None:
28
+ self.__notes.append(note)
29
+
30
+ def __clear_notes(self, caller: NoteOfIntentToRead) -> None:
31
+ while self.__notes:
32
+ note = self.__notes.pop()
33
+ if note is not caller:
34
+ note._config
35
+
36
+ def build(self, caller: NoteOfIntentToRead) -> Configuration:
37
+ # Making cached_property thread-safe
38
+ if self.__lock:
39
+ with self.__lock:
40
+ self.__config
41
+ self.__lock = None
42
+ self.__clear_notes(caller)
43
+
44
+ return self.__config
45
+
46
+ @cached_property
47
+ def __config(self) -> Configuration:
48
+ return build_configuration(
49
+ self._locations, self._mutable_config, inject_after=self._inject_after, inject_before=self._inject_before
50
+ )
51
+
52
+
53
+ @dataclasses.dataclass(frozen=False, eq=False, kw_only=True)
54
+ class NoteOfIntentToRead:
55
+ _base_path: BasePath
56
+ _config_ref: SharedConfigurationReference
57
+
58
+ def __post_init__(self) -> None:
59
+ self._config_ref.register(self)
60
+
61
+ @property
62
+ def config(self) -> Configuration:
63
+ config = self._config
64
+ if isinstance(config, Exception):
65
+ raise config
66
+ else:
67
+ return config
68
+
69
+ @cached_property
70
+ def _config(self) -> Configuration | Exception:
71
+ config = self._config_ref.build(self)
72
+ try:
73
+ return reduce(op.getitem, self._base_path, config)
74
+ except Exception as e:
75
+ return e
76
+ finally:
77
+ del self._config_ref
78
+
79
+
80
+ store: typ.Final[WeakValueDictionary[Locations, SharedConfigurationReference]] = WeakValueDictionary()
81
+
82
+
83
+ def prepare_to_load_configuration(
84
+ *,
85
+ locations: Locations,
86
+ base_path: str | tabc.Sequence[str] | None,
87
+ mutable_configuration: bool,
88
+ disable_cache: bool,
89
+ inject_before: Configuration | None,
90
+ inject_after: Configuration | None,
91
+ ) -> NoteOfIntentToRead:
92
+ if disable_cache or mutable_configuration or inject_after or inject_before:
93
+ shared_config_ref = SharedConfigurationReference(
94
+ _locations=locations,
95
+ _mutable_config=mutable_configuration,
96
+ _inject_after=inject_after,
97
+ _inject_before=inject_before,
98
+ )
99
+ elif locations not in store:
100
+ shared_config_ref = SharedConfigurationReference(_locations=locations, _mutable_config=mutable_configuration)
101
+ store[locations] = shared_config_ref
102
+ else:
103
+ shared_config_ref = store[locations]
104
+
105
+ return NoteOfIntentToRead(_base_path=read_base_path(base_path), _config_ref=shared_config_ref)