You don't have a custom loader, you have the standard RoundTripLoader for
which you extended the number of tags it can process by adding
a constructor for the tag !include. The name _custom_ruaml_loader is
misleading, just include print(type(Loader)) at the end of the
file and you'll see you have an instance of the normal RoundTripLoader.
It is unclear where Yaml comes from and what is the significance of
you using ubelt, so I can't comment on that. Apart from that I'll
assume you have a file a.yaml looking like:
a: 'xyz'
b: !include b.yaml # or [b.yaml] but I have no idea what Yaml.coerce does with that
and a file b.yaml:
c: !include c.yaml
d: 42
and a file c.yaml:
e: 2011-10-02
f: enough
You can still add constructors for additional tags, and
you can also still add custom constructors.
The constructors are added on a RoundTripConstructor/SafeConstructor/Constructor,
or on a subclass of those ( a Loader is nothing else but a composite of
the various stages of YAML loading, including a Constructor or one of its
subclasses):
import sys
import pathlib
import ruamel.yaml
def _construct_include_tag(constructor, node):
yaml = ruamel.yaml.YAML() # make a new instance, although you could get the YAML
# instance from the constructor argument
external_fpath = Path(node.value)
if not external_fpath.exists():
raise IOError(f'Included external yaml file {external_fpath} '
'does not exist')
res = yaml.load(external_fpath)
return res
ruamel.yaml.constructor.RoundTripConstructor.add_constructor('!include', _construct_include_tag)
file_in = Path('a.yaml')
yaml = ruamel.yaml.YAML()
yaml.preserve_quotes = True
data = yaml.load(file_in)
yaml.dump(data, sys.stdout)
which gives:
a: 'xyz'
b:
c:
e: 2011-10-02
f: enough
d: 42
Please note that ruamel.yaml can round-trip a.yaml, without an added constructor
but that will not do anything special with !include, apart from preserving it.
As you can see you don't need a custom constructor. But the above affects all files
loaded with the RoundTripConstructor, in this case including the loading
of b.yaml as a result of encountering !include. And if you don't want that, you can
add your own Constructor subclass and add the constructor for tag '!include' to
that, without affecting the RoundTripConstructor:
import sys
import pathlib
import ruamel.yaml
class IncludeConstructor(ruamel.yaml.RoundTripConstructor):
pass
def _construct_include_tag(constructor, node):
yaml = ruamel.yaml.YAML() # make a new instance, although you could get the YAML
# instance from the constructor argument
external_fpath = Path(node.value)
if not external_fpath.exists():
raise IOError(f'Included external yaml file {external_fpath} '
'does not exist')
res = yaml.load(external_fpath)
return res
IncludeConstructor.add_constructor('!include', _construct_include_tag)
file_in = Path('a.yaml')
yaml = ruamel.yaml.YAML()
yaml.Constructor = IncludeConstructor
yaml.preserve_quotes = True
data = yaml.load(file_in)
yaml.dump(data, sys.stdout)
which gives:
a: 'xyz'
b:
c: !include c.yaml
d: 42
because the default RoundTripLoader is not affected the instance of YAML() in
the function _construct_include_tag is not affected and c.yaml is never
loaded.
This was done with ruamel.yaml==0.17.32 with Python 3.11.4 on macOS.