python-simpleconf 0.8.2__tar.gz → 0.9.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 (18) hide show
  1. {python_simpleconf-0.8.2 → python_simpleconf-0.9.0}/PKG-INFO +41 -1
  2. {python_simpleconf-0.8.2 → python_simpleconf-0.9.0}/README.md +35 -0
  3. {python_simpleconf-0.8.2 → python_simpleconf-0.9.0}/pyproject.toml +6 -1
  4. {python_simpleconf-0.8.2 → python_simpleconf-0.9.0}/simpleconf/__init__.py +1 -1
  5. {python_simpleconf-0.8.2 → python_simpleconf-0.9.0}/simpleconf/loaders/__init__.py +26 -0
  6. {python_simpleconf-0.8.2 → python_simpleconf-0.9.0}/simpleconf/loaders/env.py +19 -2
  7. {python_simpleconf-0.8.2 → python_simpleconf-0.9.0}/simpleconf/loaders/ini.py +19 -4
  8. {python_simpleconf-0.8.2 → python_simpleconf-0.9.0}/simpleconf/loaders/json.py +19 -2
  9. {python_simpleconf-0.8.2 → python_simpleconf-0.9.0}/simpleconf/loaders/toml.py +22 -4
  10. {python_simpleconf-0.8.2 → python_simpleconf-0.9.0}/simpleconf/loaders/yaml.py +20 -2
  11. {python_simpleconf-0.8.2 → python_simpleconf-0.9.0}/simpleconf/utils.py +56 -1
  12. {python_simpleconf-0.8.2 → python_simpleconf-0.9.0}/LICENSE +0 -0
  13. {python_simpleconf-0.8.2 → python_simpleconf-0.9.0}/simpleconf/caster.py +0 -0
  14. {python_simpleconf-0.8.2 → python_simpleconf-0.9.0}/simpleconf/config.py +0 -0
  15. {python_simpleconf-0.8.2 → python_simpleconf-0.9.0}/simpleconf/exceptions.py +0 -0
  16. {python_simpleconf-0.8.2 → python_simpleconf-0.9.0}/simpleconf/loaders/dict.py +0 -0
  17. {python_simpleconf-0.8.2 → python_simpleconf-0.9.0}/simpleconf/loaders/osenv.py +0 -0
  18. {python_simpleconf-0.8.2 → python_simpleconf-0.9.0}/simpleconf/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-simpleconf
3
- Version: 0.8.2
3
+ Version: 0.9.0
4
4
  Summary: Simple configuration management with python.
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -19,11 +19,16 @@ Provides-Extra: all
19
19
  Provides-Extra: async
20
20
  Provides-Extra: env
21
21
  Provides-Extra: ini
22
+ Provides-Extra: jinja2
23
+ Provides-Extra: liquid
24
+ Provides-Extra: liquidpy
22
25
  Provides-Extra: toml
23
26
  Provides-Extra: yaml
24
27
  Requires-Dist: aiofiles (>=23.0.0) ; extra == "async"
25
28
  Requires-Dist: diot (>=0.3.2,<0.4.0)
26
29
  Requires-Dist: iniconfig (>=2.0,<3.0) ; extra == "ini" or extra == "all"
30
+ Requires-Dist: jinja2 (>=3.0,<4.0) ; extra == "jinja2"
31
+ Requires-Dist: liquidpy (>=0.8,<0.9) ; extra == "liquid" or extra == "liquidpy"
27
32
  Requires-Dist: panpath (>=0.4.8,<0.5.0)
28
33
  Requires-Dist: python-dotenv (>=1.1,<2.0) ; extra == "env" or extra == "all"
29
34
  Requires-Dist: pyyaml (>=6,<7) ; extra == "yaml" or extra == "all"
@@ -64,6 +69,7 @@ pip install python-simpleconf[all]
64
69
  - Profile support
65
70
  - Simple APIs
66
71
  - Async loading support
72
+ - Templated configuration files support (jinja2 and liquid)
67
73
 
68
74
  ## Usage
69
75
 
@@ -311,3 +317,37 @@ Values are casted by `ast.literal_eval()`.
311
317
 
312
318
  `@toml:a = 1` -> `{"a": 1}`
313
319
 
320
+ ### Templated configuration files
321
+
322
+ `jinja2` and `liquid` templating engines are supported. The templating engine is determined by the file extension, which can be either the primary or secondary suffix. For example, `config.toml.j2` and `config.j2.toml` are both treated as TOML files with Jinja2 templating.
323
+
324
+ `config.toml.j2`
325
+
326
+ ```toml
327
+ a = {{ 1 + 2 }}
328
+ ```
329
+
330
+ ```python
331
+ from simpleconf import Config
332
+
333
+ conf = Config.load('config.toml.j2')
334
+ # conf.a == 3
335
+ ```
336
+
337
+ `config.liq.toml`
338
+
339
+ ```toml
340
+ {% for i in range(1, 3) %}
341
+ [section{{i}}]
342
+ a = {{i}}
343
+ {% endfor %}
344
+ ```
345
+
346
+ ```python
347
+ from simpleconf import Config
348
+
349
+ conf = Config.load('config.liq.toml')
350
+ # conf.section1.a == 1
351
+ # conf.section2.a == 2
352
+ ```
353
+
@@ -29,6 +29,7 @@ pip install python-simpleconf[all]
29
29
  - Profile support
30
30
  - Simple APIs
31
31
  - Async loading support
32
+ - Templated configuration files support (jinja2 and liquid)
32
33
 
33
34
  ## Usage
34
35
 
@@ -275,3 +276,37 @@ Values are casted by `ast.literal_eval()`.
275
276
  #### TOML caster
276
277
 
277
278
  `@toml:a = 1` -> `{"a": 1}`
279
+
280
+ ### Templated configuration files
281
+
282
+ `jinja2` and `liquid` templating engines are supported. The templating engine is determined by the file extension, which can be either the primary or secondary suffix. For example, `config.toml.j2` and `config.j2.toml` are both treated as TOML files with Jinja2 templating.
283
+
284
+ `config.toml.j2`
285
+
286
+ ```toml
287
+ a = {{ 1 + 2 }}
288
+ ```
289
+
290
+ ```python
291
+ from simpleconf import Config
292
+
293
+ conf = Config.load('config.toml.j2')
294
+ # conf.a == 3
295
+ ```
296
+
297
+ `config.liq.toml`
298
+
299
+ ```toml
300
+ {% for i in range(1, 3) %}
301
+ [section{{i}}]
302
+ a = {{i}}
303
+ {% endfor %}
304
+ ```
305
+
306
+ ```python
307
+ from simpleconf import Config
308
+
309
+ conf = Config.load('config.liq.toml')
310
+ # conf.section1.a == 1
311
+ # conf.section2.a == 2
312
+ ```
@@ -4,7 +4,7 @@ build-backend = "poetry.masonry.api"
4
4
 
5
5
  [tool.poetry]
6
6
  name = "python-simpleconf"
7
- version = "0.8.2"
7
+ version = "0.9.0"
8
8
  description = "Simple configuration management with python."
9
9
  authors = [ "pwwang <pwwang@pwwang.com>",]
10
10
  license = "MIT"
@@ -26,6 +26,8 @@ rtoml = {version = "^0.12", optional = true, platform = "linux"}
26
26
  tomli = {version = "^2.0", optional = true, markers = 'sys_platform != "linux"'}
27
27
  iniconfig = {version = "^2.0", optional = true}
28
28
  aiofiles = {version = ">=23.0.0", optional = true}
29
+ jinja2 = {version = "^3.0", optional = true}
30
+ liquidpy = {version = "^0.8", optional = true}
29
31
 
30
32
  [tool.poetry.extras]
31
33
  ini = [ "iniconfig" ]
@@ -33,6 +35,9 @@ env = [ "python-dotenv"]
33
35
  yaml = [ "pyyaml"]
34
36
  toml = [ "rtoml", "tomli"]
35
37
  async = [ "aiofiles"]
38
+ jinja2 = [ "jinja2"]
39
+ liquid = [ "liquidpy"]
40
+ liquidpy = [ "liquidpy"]
36
41
  all = [ "iniconfig", "python-dotenv", "pyyaml", "rtoml", "tomli"]
37
42
 
38
43
  [tool.poetry.group.dev.dependencies]
@@ -1,3 +1,3 @@
1
1
  from .config import Config, ProfileConfig
2
2
 
3
- __version__ = "0.8.2"
3
+ __version__ = "0.9.0"
@@ -132,3 +132,29 @@ class NoConvertingPathMixin(ABC):
132
132
  async def a_loading(self, conf: Any, ignore_nonexist: bool) -> Dict[str, Any]:
133
133
  """Asynchronously load the configuration from a toml file"""
134
134
  return self.loading(conf, ignore_nonexist) # type: ignore[attr-defined]
135
+
136
+
137
+ class LoaderModifierMixin(ABC):
138
+ """Loader mixin class with content modifier"""
139
+
140
+ def _modifier(self, content: str | bytes) -> str | bytes:
141
+ """Modify the content of the configuration file before loading"""
142
+ return content
143
+
144
+
145
+ class J2ModifierMixin(LoaderModifierMixin):
146
+ """Loader mixin class with Jinja2 content modifier"""
147
+
148
+ def _modifier(self, content: str | bytes) -> str | bytes:
149
+ """Modify the content of the configuration file before loading"""
150
+ from jinja2 import Template
151
+ return Template(content).render()
152
+
153
+
154
+ class LiqModifierMixin(LoaderModifierMixin):
155
+ """Loader mixin class with Liquid content modifier"""
156
+
157
+ def _modifier(self, content: str | bytes) -> str | bytes:
158
+ """Modify the content of the configuration file before loading"""
159
+ from liquid import Liquid
160
+ return Liquid(content, from_file=False, mode="wild").render()
@@ -16,12 +16,18 @@ from ..caster import (
16
16
  json_caster,
17
17
  toml_caster,
18
18
  )
19
- from . import Loader, NoConvertingPathMixin
19
+ from . import (
20
+ Loader,
21
+ NoConvertingPathMixin,
22
+ LoaderModifierMixin,
23
+ J2ModifierMixin,
24
+ LiqModifierMixin,
25
+ )
20
26
 
21
27
  dotenv = require_package("dotenv")
22
28
 
23
29
 
24
- class EnvLoader(Loader):
30
+ class EnvLoader(Loader, LoaderModifierMixin):
25
31
  """Env file loader"""
26
32
 
27
33
  CASTERS = [
@@ -46,6 +52,7 @@ class EnvLoader(Loader):
46
52
 
47
53
  conf = self.__class__._convert_path(conf)
48
54
  content = conf.read_text() # so that cloud paths work
55
+ content = self._modifier(content)
49
56
  return dotenv.dotenv_values(stream=io.StringIO(content))
50
57
 
51
58
  async def a_loading(self, conf, ignore_nonexist):
@@ -56,6 +63,7 @@ class EnvLoader(Loader):
56
63
  content = await content
57
64
  if isinstance(content, bytes):
58
65
  content = content.decode()
66
+ content = self._modifier(content)
59
67
  return dotenv.dotenv_values(stream=io.StringIO(content))
60
68
 
61
69
  if not await self._a_exists(conf, ignore_nonexist):
@@ -63,6 +71,7 @@ class EnvLoader(Loader):
63
71
 
64
72
  conf = self.__class__._convert_path(conf)
65
73
  content = await conf.a_read_text() # so that cloud paths work
74
+ content = self._modifier(content)
66
75
  return dotenv.dotenv_values(stream=io.StringIO(content))
67
76
 
68
77
  @classmethod
@@ -89,3 +98,11 @@ class EnvsLoader(NoConvertingPathMixin, EnvLoader): # type: ignore[misc]
89
98
  def loading(self, conf: Any, ignore_nonexist: bool = False) -> Dict[str, Any]:
90
99
  """Load the configuration from a .env file"""
91
100
  return dotenv.dotenv_values(stream=io.StringIO(conf))
101
+
102
+
103
+ class EnvJ2Loader(EnvLoader, J2ModifierMixin):
104
+ """Env file loader with Jinja2 support"""
105
+
106
+
107
+ class EnvLiqLoader(EnvLoader, LiqModifierMixin):
108
+ """Env file loader with Liquid support"""
@@ -17,12 +17,18 @@ from ..caster import (
17
17
  json_caster,
18
18
  toml_caster,
19
19
  )
20
- from . import Loader, NoConvertingPathMixin
20
+ from . import (
21
+ Loader,
22
+ NoConvertingPathMixin,
23
+ LoaderModifierMixin,
24
+ J2ModifierMixin,
25
+ LiqModifierMixin,
26
+ )
21
27
 
22
28
  iniconfig = require_package("iniconfig")
23
29
 
24
30
 
25
- class IniLoader(Loader):
31
+ class IniLoader(Loader, LoaderModifierMixin):
26
32
  """Ini-like file loader"""
27
33
 
28
34
  CASTERS = [
@@ -47,7 +53,7 @@ class IniLoader(Loader):
47
53
 
48
54
  conf = self.__class__._convert_path(conf)
49
55
  content = conf.read_text()
50
-
56
+ content = self._modifier(content)
51
57
  return iniconfig.IniConfig(conf, content).sections
52
58
 
53
59
  async def a_loading(self, conf: Any, ignore_nonexist: bool) -> Dict[str, Any]:
@@ -58,6 +64,7 @@ class IniLoader(Loader):
58
64
  content = await content
59
65
  if isinstance(content, bytes):
60
66
  content = content.decode()
67
+ content = self._modifier(content)
61
68
  return iniconfig.IniConfig("<config>", content).sections
62
69
 
63
70
  if not await self._a_exists(conf, ignore_nonexist):
@@ -65,7 +72,7 @@ class IniLoader(Loader):
65
72
 
66
73
  conf = self.__class__._convert_path(conf)
67
74
  content = await conf.a_read_text()
68
-
75
+ content = self._modifier(content)
69
76
  return iniconfig.IniConfig(conf, content).sections
70
77
 
71
78
  @classmethod
@@ -112,3 +119,11 @@ class InisLoader(NoConvertingPathMixin, IniLoader): # type: ignore[misc]
112
119
  def loading(self, conf: Any, ignore_nonexist: bool) -> Dict[str, Any]:
113
120
  """Load the configuration from an ini-like file"""
114
121
  return iniconfig.IniConfig("<config>", conf).sections
122
+
123
+
124
+ class IniJ2Loader(IniLoader, J2ModifierMixin):
125
+ """Ini-like file loader with Jinja2 support"""
126
+
127
+
128
+ class IniLiqLoader(IniLoader, LiqModifierMixin):
129
+ """Ini-like file loader with Liquid support"""
@@ -1,10 +1,16 @@
1
1
  import json
2
2
  from typing import Any, Awaitable, Dict
3
3
 
4
- from . import Loader, NoConvertingPathMixin
4
+ from . import (
5
+ Loader,
6
+ NoConvertingPathMixin,
7
+ LoaderModifierMixin,
8
+ J2ModifierMixin,
9
+ LiqModifierMixin,
10
+ )
5
11
 
6
12
 
7
- class JsonLoader(Loader):
13
+ class JsonLoader(Loader, LoaderModifierMixin):
8
14
  """Json file loader"""
9
15
 
10
16
  def loading(self, conf: Any, ignore_nonexist: bool) -> Dict[str, Any]:
@@ -18,6 +24,7 @@ class JsonLoader(Loader):
18
24
 
19
25
  conf = self.__class__._convert_path(conf)
20
26
  content = conf.read_text()
27
+ content = self._modifier(content)
21
28
  return json.loads(content)
22
29
 
23
30
  async def a_loading(self, conf: Any, ignore_nonexist: bool) -> Dict[str, Any]:
@@ -28,6 +35,7 @@ class JsonLoader(Loader):
28
35
  content = await content
29
36
  if isinstance(content, bytes):
30
37
  content = content.decode()
38
+ content = self._modifier(content)
31
39
  return json.loads(content)
32
40
 
33
41
  if not await self._a_exists(conf, ignore_nonexist):
@@ -35,6 +43,7 @@ class JsonLoader(Loader):
35
43
 
36
44
  conf = self.__class__._convert_path(conf)
37
45
  content = await conf.a_read_text()
46
+ content = self._modifier(content)
38
47
  return json.loads(content)
39
48
 
40
49
 
@@ -44,3 +53,11 @@ class JsonsLoader(NoConvertingPathMixin, JsonLoader): # type: ignore[misc]
44
53
  def loading(self, conf: Any, ignore_nonexist: bool) -> Dict[str, Any]:
45
54
  """Load the configuration from a json file"""
46
55
  return json.loads(conf)
56
+
57
+
58
+ class JsonJ2Loader(JsonLoader, J2ModifierMixin):
59
+ """Json file loader with Jinja2 support"""
60
+
61
+
62
+ class JsonLiqLoader(JsonLoader, LiqModifierMixin):
63
+ """Json file loader with Liquid support"""
@@ -5,12 +5,18 @@ from ..caster import (
5
5
  none_caster,
6
6
  null_caster,
7
7
  )
8
- from . import Loader, NoConvertingPathMixin
8
+ from . import (
9
+ Loader,
10
+ NoConvertingPathMixin,
11
+ LoaderModifierMixin,
12
+ J2ModifierMixin,
13
+ LiqModifierMixin,
14
+ )
9
15
 
10
16
  toml = require_package("rtoml", "tomllib", "tomli")
11
17
 
12
18
 
13
- class TomlLoader(Loader):
19
+ class TomlLoader(Loader, LoaderModifierMixin):
14
20
  """Toml file loader"""
15
21
 
16
22
  CASTERS = [
@@ -22,6 +28,7 @@ class TomlLoader(Loader):
22
28
  """Load the configuration from a toml file"""
23
29
  if hasattr(conf, "read"):
24
30
  content = conf.read()
31
+ content = self._modifier(content)
25
32
  return toml.loads(content)
26
33
 
27
34
  if not self._exists(conf, ignore_nonexist):
@@ -30,9 +37,10 @@ class TomlLoader(Loader):
30
37
  conf = self.__class__._convert_path(conf)
31
38
  content = conf.read_bytes()
32
39
  try:
33
- return toml.loads(content)
34
- except TypeError:
40
+ return toml.loads(self._modifier(content))
41
+ except Exception: # TypeError, TomlParsingError
35
42
  content = content.decode()
43
+ content = self._modifier(content)
36
44
  return toml.loads(content)
37
45
 
38
46
  async def a_loading(self, conf: Any, ignore_nonexist: bool) -> Dict[str, Any]:
@@ -43,6 +51,7 @@ class TomlLoader(Loader):
43
51
  content = await content
44
52
  if isinstance(content, bytes):
45
53
  content = content.decode()
54
+ content = self._modifier(content)
46
55
  return toml.loads(content)
47
56
 
48
57
  if not await self._a_exists(conf, ignore_nonexist):
@@ -50,6 +59,7 @@ class TomlLoader(Loader):
50
59
 
51
60
  conf = self.__class__._convert_path(conf)
52
61
  content = await conf.a_read_bytes()
62
+ content = self._modifier(content)
53
63
  try:
54
64
  return toml.loads(content)
55
65
  except TypeError:
@@ -63,3 +73,11 @@ class TomlsLoader(NoConvertingPathMixin, TomlLoader): # type: ignore[misc]
63
73
  def loading(self, conf: Any, ignore_nonexist: bool) -> Dict[str, Any]:
64
74
  """Load the configuration from a toml file"""
65
75
  return toml.loads(conf)
76
+
77
+
78
+ class TomlJ2Loader(TomlLoader, J2ModifierMixin):
79
+ """Toml file loader with Jinja2 support"""
80
+
81
+
82
+ class TomlLiqLoader(TomlLoader, LiqModifierMixin):
83
+ """Toml file loader with Liquid support"""
@@ -1,18 +1,25 @@
1
1
  from typing import Any, Dict, Awaitable
2
2
 
3
- from . import Loader, NoConvertingPathMixin
3
+ from . import (
4
+ Loader,
5
+ NoConvertingPathMixin,
6
+ LoaderModifierMixin,
7
+ J2ModifierMixin,
8
+ LiqModifierMixin,
9
+ )
4
10
  from ..utils import require_package
5
11
 
6
12
  yaml = require_package("yaml")
7
13
 
8
14
 
9
- class YamlLoader(Loader):
15
+ class YamlLoader(Loader, LoaderModifierMixin):
10
16
  """Yaml file loader"""
11
17
 
12
18
  def loading(self, conf: Any, ignore_nonexist: bool) -> Dict[str, Any]:
13
19
  """Load the configuration from a yaml file"""
14
20
  if hasattr(conf, "read"):
15
21
  content = conf.read()
22
+ content = self._modifier(content)
16
23
  return yaml.load(content, Loader=yaml.FullLoader)
17
24
 
18
25
  if not self._exists(conf, ignore_nonexist):
@@ -20,6 +27,7 @@ class YamlLoader(Loader):
20
27
 
21
28
  conf = self.__class__._convert_path(conf)
22
29
  content = conf.read_text()
30
+ content = self._modifier(content)
23
31
  return yaml.load(content, Loader=yaml.FullLoader)
24
32
 
25
33
  async def a_loading(self, conf: Any, ignore_nonexist: bool) -> Dict[str, Any]:
@@ -30,6 +38,7 @@ class YamlLoader(Loader):
30
38
  content = await content
31
39
  if isinstance(content, bytes):
32
40
  content = content.decode()
41
+ content = self._modifier(content)
33
42
  return yaml.load(content, Loader=yaml.FullLoader)
34
43
 
35
44
  if not await self._a_exists(conf, ignore_nonexist):
@@ -37,6 +46,7 @@ class YamlLoader(Loader):
37
46
 
38
47
  conf = self.__class__._convert_path(conf)
39
48
  content = await conf.a_read_text()
49
+ content = self._modifier(content)
40
50
  return yaml.load(content, Loader=yaml.FullLoader)
41
51
 
42
52
 
@@ -46,3 +56,11 @@ class YamlsLoader(NoConvertingPathMixin, YamlLoader): # type: ignore[misc]
46
56
  def loading(self, conf: Any, ignore_nonexist: bool) -> Dict[str, Any]:
47
57
  """Load the configuration from a yaml file"""
48
58
  return yaml.load(conf, Loader=yaml.FullLoader)
59
+
60
+
61
+ class YamlJ2Loader(YamlLoader, J2ModifierMixin):
62
+ """Yaml file loader with Jinja2 support"""
63
+
64
+
65
+ class YamlLiqLoader(YamlLoader, LiqModifierMixin):
66
+ """Yaml file loader with Liquid support"""
@@ -12,13 +12,28 @@ POOL_KEY = "_SIMPLECONF_POOL"
12
12
  META_KEY = "_SIMPLECONF_META"
13
13
 
14
14
 
15
- def config_to_ext(conf: Any) -> str:
15
+ def config_to_ext(conf: Any, secondary: bool = True) -> str:
16
16
  """Find the extension(flag) of the configuration"""
17
17
  if isinstance(conf, dict):
18
18
  return "dict"
19
19
 
20
20
  conf = Path(conf)
21
21
  out = conf.suffix.lstrip(".").lower()
22
+ if out in ('j2', 'jinja2', 'jinja'):
23
+ # x.toml.j2
24
+ return config_to_ext(conf.stem) + '.j2'
25
+ if out in ('liq', 'liquid'):
26
+ # x.toml.liq
27
+ return config_to_ext(conf.stem) + '.liq'
28
+
29
+ if secondary:
30
+ secondary_suffix = conf.with_suffix("").suffix.lstrip(".").lower()
31
+ # x.j2.toml
32
+ if secondary_suffix in ('j2', 'jinja2', 'jinja'):
33
+ return config_to_ext(conf, secondary=False) + '.j2'
34
+ if secondary_suffix in ('liq', 'liquid'):
35
+ return config_to_ext(conf, secondary=False) + '.liq'
36
+
22
37
  if not out and conf.name.lower().endswith("rc"):
23
38
  out = "rc"
24
39
 
@@ -48,6 +63,14 @@ def get_loader(ext: str | Loader) -> Loader:
48
63
  from .loaders.env import EnvLoader
49
64
  return EnvLoader()
50
65
 
66
+ if ext == "env.j2":
67
+ from .loaders.env import EnvJ2Loader
68
+ return EnvJ2Loader()
69
+
70
+ if ext == "env.liq":
71
+ from .loaders.env import EnvLiqLoader
72
+ return EnvLiqLoader()
73
+
51
74
  if ext == "envs":
52
75
  from .loaders.env import EnvsLoader
53
76
  return EnvsLoader()
@@ -56,6 +79,14 @@ def get_loader(ext: str | Loader) -> Loader:
56
79
  from .loaders.ini import IniLoader
57
80
  return IniLoader()
58
81
 
82
+ if ext == "ini.j2":
83
+ from .loaders.ini import IniJ2Loader
84
+ return IniJ2Loader()
85
+
86
+ if ext == "ini.liq":
87
+ from .loaders.ini import IniLiqLoader
88
+ return IniLiqLoader()
89
+
59
90
  if ext == "inis":
60
91
  from .loaders.ini import InisLoader
61
92
  return InisLoader()
@@ -64,6 +95,14 @@ def get_loader(ext: str | Loader) -> Loader:
64
95
  from .loaders.json import JsonLoader
65
96
  return JsonLoader()
66
97
 
98
+ if ext == "json.j2":
99
+ from .loaders.json import JsonJ2Loader
100
+ return JsonJ2Loader()
101
+
102
+ if ext == "json.liq":
103
+ from .loaders.json import JsonLiqLoader
104
+ return JsonLiqLoader()
105
+
67
106
  if ext == "jsons":
68
107
  from .loaders.json import JsonsLoader
69
108
  return JsonsLoader()
@@ -76,6 +115,14 @@ def get_loader(ext: str | Loader) -> Loader:
76
115
  from .loaders.toml import TomlLoader
77
116
  return TomlLoader()
78
117
 
118
+ if ext == "toml.j2":
119
+ from .loaders.toml import TomlJ2Loader
120
+ return TomlJ2Loader()
121
+
122
+ if ext == "toml.liq":
123
+ from .loaders.toml import TomlLiqLoader
124
+ return TomlLiqLoader()
125
+
79
126
  if ext == "tomls":
80
127
  from .loaders.toml import TomlsLoader
81
128
  return TomlsLoader()
@@ -84,6 +131,14 @@ def get_loader(ext: str | Loader) -> Loader:
84
131
  from .loaders.yaml import YamlLoader
85
132
  return YamlLoader()
86
133
 
134
+ if ext == "yaml.j2":
135
+ from .loaders.yaml import YamlJ2Loader
136
+ return YamlJ2Loader()
137
+
138
+ if ext == "yaml.liq":
139
+ from .loaders.yaml import YamlLiqLoader
140
+ return YamlLiqLoader()
141
+
87
142
  if ext == "yamls":
88
143
  from .loaders.yaml import YamlsLoader
89
144
  return YamlsLoader()