dynaconf 3.2.6__tar.gz → 3.2.7__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.
- {dynaconf-3.2.6 → dynaconf-3.2.7}/CHANGELOG.md +21 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/PKG-INFO +1 -2
- dynaconf-3.2.7/dynaconf/VERSION +1 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/base.py +33 -2
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/cli.py +79 -35
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/loaders/__init__.py +69 -33
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/loaders/env_loader.py +29 -13
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/loaders/ini_loader.py +17 -2
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/loaders/json_loader.py +17 -2
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/loaders/py_loader.py +10 -2
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/loaders/redis_loader.py +9 -2
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/loaders/toml_loader.py +17 -3
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/loaders/yaml_loader.py +17 -2
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/utils/__init__.py +50 -7
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/utils/inspect.py +9 -2
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/utils/parse_conf.py +54 -1
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/validator.py +25 -7
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/click/_termui_impl.py +3 -3
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/click/core.py +1 -1
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/click/types.py +1 -1
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/ruamel/yaml/error.py +6 -7
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/ruamel/yaml/events.py +6 -11
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/ruamel/yaml/scalarstring.py +2 -3
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/ruamel/yaml/tokens.py +18 -19
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf.egg-info/PKG-INFO +1 -2
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf.egg-info/entry_points.txt +0 -1
- dynaconf-3.2.6/dynaconf/VERSION +0 -1
- {dynaconf-3.2.6 → dynaconf-3.2.7}/3.x-release-notes.md +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/CONTRIBUTING.md +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/CONTRIBUTORS.md +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/LICENSE +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/MANIFEST.in +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/README.md +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/__init__.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/constants.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/contrib/__init__.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/contrib/django_dynaconf_v2.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/contrib/flask_dynaconf.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/default_settings.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/hooking.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/loaders/base.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/loaders/vault_loader.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/strategies/__init__.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/strategies/filtering.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/test_settings.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/utils/boxing.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/utils/files.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/utils/functional.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/validator_conditions.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/__init__.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/box/__init__.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/box/box.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/box/box_list.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/box/config_box.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/box/converters.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/box/exceptions.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/box/from_file.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/box/shorthand_box.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/click/__init__.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/click/_bashcomplete.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/click/_compat.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/click/_textwrap.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/click/_unicodefun.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/click/_winconsole.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/click/decorators.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/click/exceptions.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/click/formatting.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/click/globals.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/click/parser.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/click/termui.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/click/testing.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/click/utils.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/dotenv/__init__.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/dotenv/cli.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/dotenv/compat.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/dotenv/ipython.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/dotenv/main.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/dotenv/parser.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/dotenv/version.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/ruamel/__init__.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/ruamel/yaml/__init__.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/ruamel/yaml/anchor.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/ruamel/yaml/comments.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/ruamel/yaml/compat.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/ruamel/yaml/composer.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/ruamel/yaml/configobjwalker.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/ruamel/yaml/constructor.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/ruamel/yaml/cyaml.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/ruamel/yaml/dumper.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/ruamel/yaml/emitter.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/ruamel/yaml/loader.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/ruamel/yaml/main.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/ruamel/yaml/nodes.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/ruamel/yaml/parser.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/ruamel/yaml/reader.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/ruamel/yaml/representer.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/ruamel/yaml/resolver.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/ruamel/yaml/scalarbool.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/ruamel/yaml/scalarfloat.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/ruamel/yaml/scalarint.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/ruamel/yaml/scanner.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/ruamel/yaml/serializer.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/ruamel/yaml/setup.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/ruamel/yaml/timestamp.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/ruamel/yaml/util.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/toml/__init__.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/toml/decoder.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/toml/encoder.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/toml/ordered.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/toml/tz.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/tomllib/__init__.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/tomllib/_parser.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/tomllib/_re.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/tomllib/_types.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf/vendor/tomllib/_writer.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf.egg-info/SOURCES.txt +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf.egg-info/dependency_links.txt +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf.egg-info/not-zip-safe +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf.egg-info/requires.txt +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/dynaconf.egg-info/top_level.txt +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/setup.cfg +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/setup.py +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/vendor_licenses/box-LICENSE.txt +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/vendor_licenses/click-LICENSE.rst +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/vendor_licenses/licenses.sh +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/vendor_licenses/python-dotenv-LICENSE.txt +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/vendor_licenses/ruamel.yaml-LICENSE.txt +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/vendor_licenses/toml-LICENSE.txt +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/vendor_licenses/tomli-LICENSE.txt +0 -0
- {dynaconf-3.2.6 → dynaconf-3.2.7}/vendor_licenses/vendor_versions.txt +0 -0
|
@@ -2,6 +2,27 @@ Changelog
|
|
|
2
2
|
=========
|
|
3
3
|
|
|
4
4
|
<!-- insertion marker -->
|
|
5
|
+
## [3.2.7](https://github.com/dynaconf/dynaconf/releases/tag/3.2.7) - 2025-01-21
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
- lazy validator's default value would evaluate early (#1198). *By Pedro Brochado*.
|
|
10
|
+
- Fixed an error that would raise when using get_history() with lazy values (#1184) (#1185). *By Pedro Brochado*.
|
|
11
|
+
- Fixed Redis loader when ENV prefix is `None`.
|
|
12
|
+
- Populate object method now takes `internal` attribute to filter out internal variables.
|
|
13
|
+
- On CLI `json.dumps` defaults to `repr` for types that cannot be serialized.
|
|
14
|
+
- Added an identifier to validator calls of `set` method
|
|
15
|
+
- Fix django app discovery using DJANGO_SETTINGS_MODULE variable
|
|
16
|
+
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
- Added `@insert` token to call `list.insert`
|
|
20
|
+
- Allow env loader to load from multiple prefixes
|
|
21
|
+
- Allow multiple composable current environments
|
|
22
|
+
- Track more data on `load_file` method
|
|
23
|
+
- Added `--json` to dynaconf list CLI
|
|
24
|
+
|
|
25
|
+
|
|
5
26
|
## [3.2.6](https://github.com/dynaconf/dynaconf/releases/tag/3.2.6) - 2024-07-19
|
|
6
27
|
|
|
7
28
|
## [3.2.5](https://github.com/pedro-psb/dynaconf/releases/tag/3.2.5) - 2024-03-18
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: dynaconf
|
|
3
|
-
Version: 3.2.
|
|
3
|
+
Version: 3.2.7
|
|
4
4
|
Summary: The dynamic configurator for your Python Project
|
|
5
5
|
Home-page: https://github.com/dynaconf/dynaconf
|
|
6
6
|
Author: Bruno Rocha
|
|
@@ -180,4 +180,3 @@ Main discussions happens on [Discussions Tab](https://github.com/dynaconf/dynaco
|
|
|
180
180
|
If you are looking for something similar to Dynaconf to use in your Rust projects: https://github.com/rubik/hydroconf
|
|
181
181
|
|
|
182
182
|
And a special thanks to [Caneco](https://twitter.com/caneco) for the logo.
|
|
183
|
-
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.2.7
|
|
@@ -968,6 +968,15 @@ class Settings:
|
|
|
968
968
|
self.store.get(key, None) if not isinstance(parsed, Lazy) else None
|
|
969
969
|
)
|
|
970
970
|
|
|
971
|
+
if getattr(parsed, "_dynaconf_insert", False):
|
|
972
|
+
# `@insert` calls insert in a list by index
|
|
973
|
+
if existing and isinstance(existing, list):
|
|
974
|
+
source_metadata = source_metadata._replace(merged=True)
|
|
975
|
+
existing.insert(parsed.index, parsed.unwrap())
|
|
976
|
+
parsed = existing
|
|
977
|
+
else:
|
|
978
|
+
parsed = [parsed.unwrap()]
|
|
979
|
+
|
|
971
980
|
if getattr(parsed, "_dynaconf_del", None):
|
|
972
981
|
self.unset(key, force=True) # `@del` in a first level var.
|
|
973
982
|
return
|
|
@@ -1227,14 +1236,27 @@ class Settings:
|
|
|
1227
1236
|
validate = self.get("VALIDATE_ON_UPDATE_FOR_DYNACONF")
|
|
1228
1237
|
|
|
1229
1238
|
env = (env or self.current_env).upper()
|
|
1239
|
+
|
|
1230
1240
|
files = ensure_a_list(path)
|
|
1231
1241
|
if files:
|
|
1242
|
+
# Using inspect take the filename and line number of the caller
|
|
1243
|
+
# to be used in the source_metadata
|
|
1244
|
+
frame = inspect.currentframe()
|
|
1245
|
+
caller = inspect.getouterframes(frame)[1]
|
|
1232
1246
|
already_loaded = set()
|
|
1233
1247
|
for _filename in files:
|
|
1234
1248
|
# load_file() will handle validation later
|
|
1235
1249
|
with suppress(ValidationError):
|
|
1250
|
+
source_metadata = SourceMetadata(
|
|
1251
|
+
loader=f"load_file@{caller.filename}:{caller.lineno}",
|
|
1252
|
+
identifier=_filename,
|
|
1253
|
+
env=env,
|
|
1254
|
+
)
|
|
1236
1255
|
if py_loader.try_to_load_from_py_module_name(
|
|
1237
|
-
obj=self,
|
|
1256
|
+
obj=self,
|
|
1257
|
+
name=_filename,
|
|
1258
|
+
silent=True,
|
|
1259
|
+
identifier=source_metadata,
|
|
1238
1260
|
):
|
|
1239
1261
|
# if it was possible to load from module name
|
|
1240
1262
|
# continue the loop.
|
|
@@ -1265,6 +1287,11 @@ class Settings:
|
|
|
1265
1287
|
|
|
1266
1288
|
# load_file() will handle validation later
|
|
1267
1289
|
with suppress(ValidationError):
|
|
1290
|
+
source_metadata = SourceMetadata(
|
|
1291
|
+
loader=f"load_file@{caller.filename}:{caller.lineno}",
|
|
1292
|
+
identifier=path,
|
|
1293
|
+
env=env,
|
|
1294
|
+
)
|
|
1268
1295
|
settings_loader(
|
|
1269
1296
|
obj=self,
|
|
1270
1297
|
env=env,
|
|
@@ -1272,6 +1299,7 @@ class Settings:
|
|
|
1272
1299
|
key=key,
|
|
1273
1300
|
filename=path,
|
|
1274
1301
|
validate=validate,
|
|
1302
|
+
identifier=source_metadata,
|
|
1275
1303
|
)
|
|
1276
1304
|
already_loaded.add(path)
|
|
1277
1305
|
|
|
@@ -1357,7 +1385,7 @@ class Settings:
|
|
|
1357
1385
|
value = self.get_fresh(key)
|
|
1358
1386
|
return value is True or value in true_values
|
|
1359
1387
|
|
|
1360
|
-
def populate_obj(self, obj, keys=None, ignore=None):
|
|
1388
|
+
def populate_obj(self, obj, keys=None, ignore=None, internal=False):
|
|
1361
1389
|
"""Given the `obj` populate it using self.store items.
|
|
1362
1390
|
|
|
1363
1391
|
:param obj: An object to be populated, a class instance.
|
|
@@ -1366,6 +1394,9 @@ class Settings:
|
|
|
1366
1394
|
"""
|
|
1367
1395
|
keys = keys or self.keys()
|
|
1368
1396
|
for key in keys:
|
|
1397
|
+
if not internal:
|
|
1398
|
+
if key in UPPER_DEFAULT_SETTINGS:
|
|
1399
|
+
continue
|
|
1369
1400
|
key = upperfy(key)
|
|
1370
1401
|
if ignore and key in ignore:
|
|
1371
1402
|
continue
|
|
@@ -1,22 +1,25 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import importlib
|
|
4
|
+
import inspect as python_inspect
|
|
4
5
|
import json
|
|
5
6
|
import os
|
|
6
7
|
import pprint
|
|
7
8
|
import sys
|
|
8
9
|
import warnings
|
|
9
10
|
import webbrowser
|
|
11
|
+
from contextlib import redirect_stdout
|
|
10
12
|
from contextlib import suppress
|
|
11
13
|
from pathlib import Path
|
|
12
|
-
from typing import TYPE_CHECKING
|
|
13
14
|
|
|
14
15
|
from dynaconf import constants
|
|
15
16
|
from dynaconf import default_settings
|
|
16
17
|
from dynaconf import LazySettings
|
|
17
18
|
from dynaconf import loaders
|
|
18
19
|
from dynaconf import settings as legacy_settings
|
|
20
|
+
from dynaconf.base import Settings
|
|
19
21
|
from dynaconf.loaders.py_loader import get_module
|
|
22
|
+
from dynaconf.utils import prepare_json
|
|
20
23
|
from dynaconf.utils import upperfy
|
|
21
24
|
from dynaconf.utils.files import read_file
|
|
22
25
|
from dynaconf.utils.functional import empty
|
|
@@ -33,9 +36,6 @@ from dynaconf.vendor import click
|
|
|
33
36
|
from dynaconf.vendor import toml
|
|
34
37
|
from dynaconf.vendor import tomllib
|
|
35
38
|
|
|
36
|
-
if TYPE_CHECKING: # pragma: no cover
|
|
37
|
-
from dynaconf.base import Settings # noqa: F401
|
|
38
|
-
|
|
39
39
|
os.environ["PYTHONIOENCODING"] = "utf-8"
|
|
40
40
|
|
|
41
41
|
CWD = None
|
|
@@ -55,6 +55,8 @@ def set_settings(ctx, instance=None):
|
|
|
55
55
|
settings = None
|
|
56
56
|
|
|
57
57
|
_echo_enabled = ctx.invoked_subcommand not in ["get", "inspect", None]
|
|
58
|
+
if "--json" in click.get_os_args():
|
|
59
|
+
_echo_enabled = False
|
|
58
60
|
|
|
59
61
|
if instance is not None:
|
|
60
62
|
if ctx.invoked_subcommand in ["init"]:
|
|
@@ -79,19 +81,16 @@ def set_settings(ctx, instance=None):
|
|
|
79
81
|
)
|
|
80
82
|
elif "DJANGO_SETTINGS_MODULE" in os.environ: # pragma: no cover
|
|
81
83
|
sys.path.insert(0, os.path.abspath(os.getcwd()))
|
|
82
|
-
|
|
83
|
-
# Django extension v2
|
|
84
|
-
from django.conf import settings # noqa
|
|
85
|
-
import dynaconf # noqa: F401
|
|
86
|
-
import django
|
|
84
|
+
import django # noqa
|
|
87
85
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
86
|
+
django.setup() # ensure django is setup to avoid AppRegistryNotReady
|
|
87
|
+
settings_module = import__django_settings(
|
|
88
|
+
os.environ["DJANGO_SETTINGS_MODULE"]
|
|
89
|
+
)
|
|
90
|
+
for member in python_inspect.getmembers(settings_module):
|
|
91
|
+
if isinstance(member[1], (LazySettings, Settings)):
|
|
92
|
+
settings = member[1]
|
|
93
|
+
break
|
|
95
94
|
|
|
96
95
|
if settings is not None and _echo_enabled:
|
|
97
96
|
click.echo(
|
|
@@ -117,6 +116,18 @@ def set_settings(ctx, instance=None):
|
|
|
117
116
|
settings = LazySettings()
|
|
118
117
|
|
|
119
118
|
|
|
119
|
+
def import__django_settings(django_settings_module):
|
|
120
|
+
"""Import the Django settings module from the string importable path."""
|
|
121
|
+
try:
|
|
122
|
+
with redirect_stdout(None):
|
|
123
|
+
module = importlib.import_module(django_settings_module)
|
|
124
|
+
except ImportError as e:
|
|
125
|
+
raise click.UsageError(e)
|
|
126
|
+
except FileNotFoundError:
|
|
127
|
+
return
|
|
128
|
+
return module
|
|
129
|
+
|
|
130
|
+
|
|
120
131
|
def import_settings(dotted_path):
|
|
121
132
|
"""Import settings instance from python dotted path.
|
|
122
133
|
|
|
@@ -130,8 +141,10 @@ def import_settings(dotted_path):
|
|
|
130
141
|
raise click.UsageError(
|
|
131
142
|
f"invalid path to settings instance: {dotted_path}"
|
|
132
143
|
)
|
|
144
|
+
|
|
133
145
|
try:
|
|
134
|
-
|
|
146
|
+
with redirect_stdout(None):
|
|
147
|
+
module = importlib.import_module(module)
|
|
135
148
|
except ImportError as e:
|
|
136
149
|
raise click.UsageError(e)
|
|
137
150
|
except FileNotFoundError:
|
|
@@ -485,7 +498,7 @@ def get(key, default, env, unparse):
|
|
|
485
498
|
result = unparse_conf_data(result)
|
|
486
499
|
|
|
487
500
|
if isinstance(result, (dict, list, tuple)):
|
|
488
|
-
result = json.dumps(result, sort_keys=True)
|
|
501
|
+
result = json.dumps(prepare_json(result), sort_keys=True, default=repr)
|
|
489
502
|
|
|
490
503
|
click.echo(result, nl=False)
|
|
491
504
|
|
|
@@ -530,7 +543,24 @@ def get(key, default, env, unparse):
|
|
|
530
543
|
default=False,
|
|
531
544
|
help="Output file is flat (do not include [env] name)",
|
|
532
545
|
)
|
|
533
|
-
|
|
546
|
+
@click.option(
|
|
547
|
+
"--json",
|
|
548
|
+
"_json",
|
|
549
|
+
"-j",
|
|
550
|
+
is_flag=True,
|
|
551
|
+
default=False,
|
|
552
|
+
help="Prints out data serialized as JSON",
|
|
553
|
+
)
|
|
554
|
+
def _list(
|
|
555
|
+
env,
|
|
556
|
+
key,
|
|
557
|
+
more,
|
|
558
|
+
loader,
|
|
559
|
+
_all=False,
|
|
560
|
+
output=None,
|
|
561
|
+
flat=False,
|
|
562
|
+
_json=False,
|
|
563
|
+
):
|
|
534
564
|
"""
|
|
535
565
|
Lists user defined settings or all (including internal configs).
|
|
536
566
|
|
|
@@ -552,14 +582,15 @@ def _list(env, key, more, loader, _all=False, output=None, flat=False):
|
|
|
552
582
|
if cur_env == "main":
|
|
553
583
|
flat = True
|
|
554
584
|
|
|
555
|
-
|
|
556
|
-
click.
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
585
|
+
if not _json:
|
|
586
|
+
click.echo(
|
|
587
|
+
click.style(
|
|
588
|
+
f"Working in {cur_env} environment ",
|
|
589
|
+
bold=True,
|
|
590
|
+
bg="bright_blue",
|
|
591
|
+
fg="bright_white",
|
|
592
|
+
)
|
|
561
593
|
)
|
|
562
|
-
)
|
|
563
594
|
|
|
564
595
|
if not loader:
|
|
565
596
|
data = settings.as_dict(env=env, internal=_all)
|
|
@@ -585,14 +616,20 @@ def _list(env, key, more, loader, _all=False, output=None, flat=False):
|
|
|
585
616
|
return f"{key}{data_type} {value}"
|
|
586
617
|
|
|
587
618
|
if not key:
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
619
|
+
if not _json:
|
|
620
|
+
datalines = "\n".join(
|
|
621
|
+
format_setting(k, v)
|
|
622
|
+
for k, v in data.items()
|
|
623
|
+
if k not in data.get("RENAMED_VARS", [])
|
|
624
|
+
)
|
|
625
|
+
(click.echo_via_pager if more else click.echo)(datalines)
|
|
594
626
|
if output:
|
|
595
|
-
loaders.write(output, data, env=not flat and cur_env)
|
|
627
|
+
loaders.write(output, prepare_json(data), env=not flat and cur_env)
|
|
628
|
+
if _json:
|
|
629
|
+
json_data = json.dumps(
|
|
630
|
+
prepare_json(data), sort_keys=True, default=repr
|
|
631
|
+
)
|
|
632
|
+
click.echo(json_data, nl=False)
|
|
596
633
|
else:
|
|
597
634
|
key = upperfy(key)
|
|
598
635
|
|
|
@@ -605,9 +642,16 @@ def _list(env, key, more, loader, _all=False, output=None, flat=False):
|
|
|
605
642
|
click.secho("Key not found", bg="red", fg="white", err=True)
|
|
606
643
|
return
|
|
607
644
|
|
|
608
|
-
|
|
645
|
+
if not _json:
|
|
646
|
+
click.echo(format_setting(key, value))
|
|
609
647
|
if output:
|
|
610
|
-
loaders.write(
|
|
648
|
+
loaders.write(
|
|
649
|
+
output, prepare_json({key: value}), env=not flat and cur_env
|
|
650
|
+
)
|
|
651
|
+
if _json:
|
|
652
|
+
click.echo(
|
|
653
|
+
json.dumps(prepare_json({key: value}), default=repr), nl=True
|
|
654
|
+
)
|
|
611
655
|
|
|
612
656
|
if env:
|
|
613
657
|
settings.setenv()
|
|
@@ -12,6 +12,7 @@ from dynaconf.loaders import json_loader
|
|
|
12
12
|
from dynaconf.loaders import py_loader
|
|
13
13
|
from dynaconf.loaders import toml_loader
|
|
14
14
|
from dynaconf.loaders import yaml_loader
|
|
15
|
+
from dynaconf.loaders.base import SourceMetadata
|
|
15
16
|
from dynaconf.utils import deduplicate
|
|
16
17
|
from dynaconf.utils import ensure_a_list
|
|
17
18
|
from dynaconf.utils.boxing import DynaBox
|
|
@@ -222,6 +223,7 @@ def settings_loader(
|
|
|
222
223
|
key=None,
|
|
223
224
|
filename=None,
|
|
224
225
|
validate=False,
|
|
226
|
+
identifier="settings_loader",
|
|
225
227
|
):
|
|
226
228
|
"""Loads from defined settings module
|
|
227
229
|
|
|
@@ -231,6 +233,8 @@ def settings_loader(
|
|
|
231
233
|
:param silent: Boolean to raise loading errors
|
|
232
234
|
:param key: Load a single key if provided
|
|
233
235
|
:param filename: optional filename to override the settings_module
|
|
236
|
+
:param validate: If True validate the loaded data
|
|
237
|
+
:param identifier: A string or SourceMetadata to identify the loader
|
|
234
238
|
"""
|
|
235
239
|
if filename is None:
|
|
236
240
|
settings_module = settings_module or obj.settings_module
|
|
@@ -295,6 +299,9 @@ def settings_loader(
|
|
|
295
299
|
continue
|
|
296
300
|
|
|
297
301
|
if mod_file.endswith(loader["ext"]):
|
|
302
|
+
if isinstance(identifier, str):
|
|
303
|
+
# ensure it is always loader name
|
|
304
|
+
identifier = loader["name"].lower()
|
|
298
305
|
loader["loader"].load(
|
|
299
306
|
obj,
|
|
300
307
|
filename=mod_file,
|
|
@@ -302,6 +309,7 @@ def settings_loader(
|
|
|
302
309
|
silent=silent,
|
|
303
310
|
key=key,
|
|
304
311
|
validate=validate,
|
|
312
|
+
identifier=identifier,
|
|
305
313
|
)
|
|
306
314
|
continue
|
|
307
315
|
|
|
@@ -314,45 +322,73 @@ def settings_loader(
|
|
|
314
322
|
|
|
315
323
|
# must be Python file or module
|
|
316
324
|
# load from default defined module settings.py or .secrets.py if exists
|
|
317
|
-
py_loader.load(
|
|
325
|
+
py_loader.load(
|
|
326
|
+
obj, mod_file, key=key, validate=validate, identifier=identifier
|
|
327
|
+
)
|
|
318
328
|
|
|
319
329
|
# load from the current env e.g: development_settings.py
|
|
330
|
+
# counting on the case where env is a comma separated string
|
|
320
331
|
env = env or obj.current_env
|
|
321
|
-
if
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
332
|
+
if env and isinstance(env, str):
|
|
333
|
+
for env_name in env.split(","):
|
|
334
|
+
load_from_env_named_file(
|
|
335
|
+
obj, env_name, key, validate, identifier, mod_file
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def load_from_env_named_file(obj, env, key, validate, identifier, mod_file):
|
|
340
|
+
"""Load from env named file e.g: development_settings.py"""
|
|
341
|
+
if mod_file.endswith(".py"):
|
|
342
|
+
if ".secrets.py" == mod_file:
|
|
343
|
+
tmpl = ".{0}_{1}{2}"
|
|
344
|
+
mod_file = "secrets.py"
|
|
334
345
|
else:
|
|
335
|
-
|
|
336
|
-
|
|
346
|
+
tmpl = "{0}_{1}{2}"
|
|
347
|
+
|
|
348
|
+
dirname = os.path.dirname(mod_file)
|
|
349
|
+
filename, extension = os.path.splitext(os.path.basename(mod_file))
|
|
350
|
+
new_filename = tmpl.format(env.lower(), filename, extension)
|
|
351
|
+
env_mod_file = os.path.join(dirname, new_filename)
|
|
352
|
+
global_filename = tmpl.format("global", filename, extension)
|
|
353
|
+
global_mod_file = os.path.join(dirname, global_filename)
|
|
354
|
+
else:
|
|
355
|
+
parts = mod_file.rsplit(".", 1)
|
|
356
|
+
if len(parts) > 1:
|
|
357
|
+
head, tail = parts
|
|
358
|
+
else:
|
|
359
|
+
head, tail = None, parts[0]
|
|
360
|
+
tail = env_mod_file = f"{env.lower()}_{tail}"
|
|
337
361
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
validate=validate,
|
|
345
|
-
)
|
|
362
|
+
if head:
|
|
363
|
+
env_mod_file = f"{head}.{tail}"
|
|
364
|
+
global_mod_file = f"{head}.global_{tail}"
|
|
365
|
+
else:
|
|
366
|
+
env_mod_file = tail
|
|
367
|
+
global_mod_file = f"global_{tail}"
|
|
346
368
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
369
|
+
source_metadata = SourceMetadata(
|
|
370
|
+
loader="py",
|
|
371
|
+
identifier=identifier,
|
|
372
|
+
env=env,
|
|
373
|
+
)
|
|
374
|
+
py_loader.load(
|
|
375
|
+
obj,
|
|
376
|
+
env_mod_file,
|
|
377
|
+
identifier=source_metadata,
|
|
378
|
+
silent=True,
|
|
379
|
+
key=key,
|
|
380
|
+
validate=validate,
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
# load from global_settings.py
|
|
384
|
+
py_loader.load(
|
|
385
|
+
obj,
|
|
386
|
+
global_mod_file,
|
|
387
|
+
identifier="py_global",
|
|
388
|
+
silent=True,
|
|
389
|
+
key=key,
|
|
390
|
+
validate=validate,
|
|
391
|
+
)
|
|
356
392
|
|
|
357
393
|
|
|
358
394
|
def enable_external_loaders(obj):
|
|
@@ -15,34 +15,50 @@ with suppress(ImportError, FileNotFoundError):
|
|
|
15
15
|
|
|
16
16
|
DOTENV_IMPORTED = True
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
IDENTIFIER_PREFIX = "env"
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
def load(
|
|
21
|
+
def load(
|
|
22
|
+
obj, env=None, silent=True, key=None, validate=False, identifier="global"
|
|
23
|
+
):
|
|
22
24
|
"""Loads envvars with prefixes:
|
|
23
25
|
|
|
24
26
|
`DYNACONF_` (default global) or `$(ENVVAR_PREFIX_FOR_DYNACONF)_`
|
|
27
|
+
|
|
28
|
+
if envvar_prefix is set to:
|
|
29
|
+
str: -> load {str}_*
|
|
30
|
+
str,str1 -> load [{str}_, {str1}_]
|
|
31
|
+
False -> load *
|
|
32
|
+
None -> return not loading anything
|
|
33
|
+
|
|
25
34
|
"""
|
|
26
35
|
global_prefix = obj.get("ENVVAR_PREFIX_FOR_DYNACONF")
|
|
36
|
+
if global_prefix is None:
|
|
37
|
+
return
|
|
38
|
+
|
|
27
39
|
if global_prefix is False or global_prefix.upper() != "DYNACONF":
|
|
28
40
|
load_from_env(
|
|
29
41
|
obj,
|
|
30
42
|
"DYNACONF",
|
|
31
43
|
key,
|
|
32
44
|
silent,
|
|
33
|
-
|
|
45
|
+
f"{IDENTIFIER_PREFIX}_{identifier}",
|
|
34
46
|
validate=validate,
|
|
35
47
|
)
|
|
36
48
|
|
|
37
49
|
# Load the global env if exists and overwrite everything
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
50
|
+
# if the prefix is separated by comma then load all prefixes
|
|
51
|
+
# counting on the case where global_prefix is set to None, False or ""
|
|
52
|
+
prefixes = global_prefix.split(",") if global_prefix else [global_prefix]
|
|
53
|
+
for prefix in prefixes:
|
|
54
|
+
load_from_env(
|
|
55
|
+
obj,
|
|
56
|
+
prefix,
|
|
57
|
+
key,
|
|
58
|
+
silent,
|
|
59
|
+
f"{IDENTIFIER_PREFIX}_{identifier}",
|
|
60
|
+
validate=validate,
|
|
61
|
+
)
|
|
46
62
|
|
|
47
63
|
|
|
48
64
|
def load_from_env(
|
|
@@ -50,7 +66,7 @@ def load_from_env(
|
|
|
50
66
|
prefix=False,
|
|
51
67
|
key=None,
|
|
52
68
|
silent=False,
|
|
53
|
-
identifier=
|
|
69
|
+
identifier=IDENTIFIER_PREFIX,
|
|
54
70
|
env=False, # backwards compatibility bc renamed param
|
|
55
71
|
validate=False,
|
|
56
72
|
):
|
|
@@ -66,7 +82,7 @@ def load_from_env(
|
|
|
66
82
|
env_ = f"{prefix}_"
|
|
67
83
|
|
|
68
84
|
# set source metadata
|
|
69
|
-
source_metadata = SourceMetadata(identifier,
|
|
85
|
+
source_metadata = SourceMetadata(identifier, prefix, "global")
|
|
70
86
|
|
|
71
87
|
# Load a single environment variable explicitly.
|
|
72
88
|
if key:
|
|
@@ -5,6 +5,7 @@ from pathlib import Path
|
|
|
5
5
|
from dynaconf import default_settings
|
|
6
6
|
from dynaconf.constants import INI_EXTENSIONS
|
|
7
7
|
from dynaconf.loaders.base import BaseLoader
|
|
8
|
+
from dynaconf.loaders.base import SourceMetadata
|
|
8
9
|
from dynaconf.utils import object_merge
|
|
9
10
|
|
|
10
11
|
try:
|
|
@@ -13,7 +14,15 @@ except ImportError: # pragma: no cover
|
|
|
13
14
|
ConfigObj = None
|
|
14
15
|
|
|
15
16
|
|
|
16
|
-
def load(
|
|
17
|
+
def load(
|
|
18
|
+
obj,
|
|
19
|
+
env=None,
|
|
20
|
+
silent=True,
|
|
21
|
+
key=None,
|
|
22
|
+
filename=None,
|
|
23
|
+
validate=False,
|
|
24
|
+
identifier="ini",
|
|
25
|
+
):
|
|
17
26
|
"""
|
|
18
27
|
Reads and loads in to "obj" a single key or all keys from source file.
|
|
19
28
|
|
|
@@ -28,10 +37,16 @@ def load(obj, env=None, silent=True, key=None, filename=None, validate=False):
|
|
|
28
37
|
BaseLoader.warn_not_installed(obj, "ini")
|
|
29
38
|
return
|
|
30
39
|
|
|
40
|
+
# when load_file function is called directly it comes with module and line number
|
|
41
|
+
if isinstance(identifier, SourceMetadata) and identifier.loader.startswith(
|
|
42
|
+
"load_file"
|
|
43
|
+
):
|
|
44
|
+
identifier = identifier.loader
|
|
45
|
+
|
|
31
46
|
loader = BaseLoader(
|
|
32
47
|
obj=obj,
|
|
33
48
|
env=env,
|
|
34
|
-
identifier=
|
|
49
|
+
identifier=identifier,
|
|
35
50
|
extensions=INI_EXTENSIONS,
|
|
36
51
|
file_reader=lambda fileobj: ConfigObj(fileobj).dict(),
|
|
37
52
|
string_reader=lambda strobj: ConfigObj(strobj.split("\n")).dict(),
|
|
@@ -6,6 +6,7 @@ from pathlib import Path
|
|
|
6
6
|
from dynaconf import default_settings
|
|
7
7
|
from dynaconf.constants import JSON_EXTENSIONS
|
|
8
8
|
from dynaconf.loaders.base import BaseLoader
|
|
9
|
+
from dynaconf.loaders.base import SourceMetadata
|
|
9
10
|
from dynaconf.utils import object_merge
|
|
10
11
|
from dynaconf.utils.parse_conf import try_to_encode
|
|
11
12
|
|
|
@@ -15,7 +16,15 @@ except ImportError: # pragma: no cover
|
|
|
15
16
|
commentjson = None
|
|
16
17
|
|
|
17
18
|
|
|
18
|
-
def load(
|
|
19
|
+
def load(
|
|
20
|
+
obj,
|
|
21
|
+
env=None,
|
|
22
|
+
silent=True,
|
|
23
|
+
key=None,
|
|
24
|
+
filename=None,
|
|
25
|
+
validate=False,
|
|
26
|
+
identifier="json",
|
|
27
|
+
):
|
|
19
28
|
"""
|
|
20
29
|
Reads and loads in to "obj" a single key or all keys from source file.
|
|
21
30
|
|
|
@@ -35,10 +44,16 @@ def load(obj, env=None, silent=True, key=None, filename=None, validate=False):
|
|
|
35
44
|
file_reader = json.load
|
|
36
45
|
string_reader = json.loads
|
|
37
46
|
|
|
47
|
+
# when load_file function is called directly it comes with module and line number
|
|
48
|
+
if isinstance(identifier, SourceMetadata) and identifier.loader.startswith(
|
|
49
|
+
"load_file"
|
|
50
|
+
):
|
|
51
|
+
identifier = identifier.loader
|
|
52
|
+
|
|
38
53
|
loader = BaseLoader(
|
|
39
54
|
obj=obj,
|
|
40
55
|
env=env,
|
|
41
|
-
identifier=
|
|
56
|
+
identifier=identifier,
|
|
42
57
|
extensions=JSON_EXTENSIONS,
|
|
43
58
|
file_reader=file_reader,
|
|
44
59
|
string_reader=string_reader,
|
|
@@ -37,7 +37,12 @@ def load(
|
|
|
37
37
|
return
|
|
38
38
|
|
|
39
39
|
# setup SourceMetadata (for inspecting)
|
|
40
|
-
|
|
40
|
+
if isinstance(identifier, SourceMetadata):
|
|
41
|
+
loader_identifier = SourceMetadata(
|
|
42
|
+
identifier.loader, mod.__name__, identifier.env
|
|
43
|
+
)
|
|
44
|
+
else:
|
|
45
|
+
loader_identifier = SourceMetadata(identifier, mod.__name__, "global")
|
|
41
46
|
|
|
42
47
|
load_from_python_object(
|
|
43
48
|
obj, mod, settings_module, key, loader_identifier, validate=validate
|
|
@@ -88,7 +93,10 @@ def try_to_load_from_py_module_name(
|
|
|
88
93
|
ctx = suppress(ImportError, TypeError) if silent else suppress()
|
|
89
94
|
|
|
90
95
|
# setup SourceMetadata (for inspecting)
|
|
91
|
-
|
|
96
|
+
if isinstance(identifier, SourceMetadata):
|
|
97
|
+
loader_identifier = identifier
|
|
98
|
+
else:
|
|
99
|
+
loader_identifier = SourceMetadata(identifier, name, "global")
|
|
92
100
|
|
|
93
101
|
with ctx:
|
|
94
102
|
mod = importlib.import_module(str(name))
|