tutorial - ¿Cómo puedo incluir un archivo YAML dentro de otro?
yaml usos (10)
Ampliando la respuesta de @ Josh_Bode, aquí está mi propia solución PyYAML, que tiene la ventaja de ser una subclase independiente de yaml.Loader
. No depende de ningún nivel global de módulo, o de la modificación del estado global del módulo yaml
.
import yaml, os
class IncludeLoader(yaml.Loader):
"""
yaml.Loader subclass handles "!include path/to/foo.yml" directives in config
files. When constructed with a file object, the root path for includes
defaults to the directory containing the file, otherwise to the current
working directory. In either case, the root path can be overridden by the
`root` keyword argument.
When an included file F contain its own !include directive, the path is
relative to F''s location.
Example:
YAML file /home/frodo/one-ring.yml:
---
Name: The One Ring
Specials:
- resize-to-wearer
Effects:
- !include path/to/invisibility.yml
YAML file /home/frodo/path/to/invisibility.yml:
---
Name: invisibility
Message: Suddenly you disappear!
Loading:
data = IncludeLoader(open(''/home/frodo/one-ring.yml'', ''r'')).get_data()
Result:
{''Effects'': [{''Message'': ''Suddenly you disappear!'', ''Name'':
''invisibility''}], ''Name'': ''The One Ring'', ''Specials'':
[''resize-to-wearer'']}
"""
def __init__(self, *args, **kwargs):
super(IncludeLoader, self).__init__(*args, **kwargs)
self.add_constructor(''!include'', self._include)
if ''root'' in kwargs:
self.root = kwargs[''root'']
elif isinstance(self.stream, file):
self.root = os.path.dirname(self.stream.name)
else:
self.root = os.path.curdir
def _include(self, loader, node):
oldRoot = self.root
filename = os.path.join(self.root, loader.construct_scalar(node))
self.root = os.path.dirname(filename)
data = yaml.load(open(filename, ''r''))
self.root = oldRoot
return data
Así que tengo dos archivos YAML, "A" y "B" y quiero que el contenido de A se inserte dentro de B, ya sea empalmado en la estructura de datos existente, como una matriz, o como elemento secundario de un elemento, como el valor para una cierta clave hash.
¿Es esto posible en absoluto? ¿Cómo? Si no es así, ¿algún puntero a una referencia normativa?
Con Symfony , su manejo de yaml indirectamente te permitirá anidar archivos yaml. El truco es hacer uso de la opción de parameters
. p.ej:
common.yml
parameters:
yaml_to_repeat:
option: "value"
foo:
- "bar"
- "baz"
config.yml
imports:
- { resource: common.yml }
whatever:
thing: "%yaml_to_repeat%"
other_thing: "%yaml_to_repeat%"
El resultado será el mismo que:
whatever:
thing:
option: "value"
foo:
- "bar"
- "baz"
other_thing:
option: "value"
foo:
- "bar"
- "baz"
Creo que la solución utilizada por @ maxy-B se ve muy bien. Sin embargo, no tuvo éxito para mí con inclusiones anidadas. Por ejemplo, si config_1.yaml incluye config_2.yaml, que incluye config_3.yaml, hubo un problema con el cargador. Sin embargo, si simplemente apunta la nueva clase de cargador a sí mismo durante la carga, ¡funciona! Específicamente, si reemplazamos la antigua función _include con la versión ligeramente modificada:
def _include(self, loader, node):
oldRoot = self.root
filename = os.path.join(self.root, loader.construct_scalar(node))
self.root = os.path.dirname(filename)
data = yaml.load(open(filename, ''r''), loader = IncludeLoader)
self.root = oldRoot
return data
Después de reflexionar, estoy de acuerdo con los otros comentarios, que la carga anidada no es apropiada para yaml en general, ya que la secuencia de entrada puede no ser un archivo, ¡pero es muy útil!
Desafortunadamente YAML no proporciona esto en su estándar.
Pero si está utilizando Ruby, hay una gema que proporciona la funcionalidad que solicita al ampliar la biblioteca YAML de ruby: https://github.com/entwanderer/yaml_extend
Las aplicaciones no están directamente soportadas en yaml, por lo que sé, tendrá que proporcionar un mecanismo, sin embargo, esto es generalmente fácil de hacer.
He usado yaml como lenguaje de configuración en mis aplicaciones de Python, y en este caso a menudo defino una convensión como esta:
>>> main.yml <<<
includes: [ wibble.yml, wobble.yml]
Luego en mi código (python) hago:
import yaml
cfg = yaml.load(open("main.yml"))
for inc in cfg.get("includes", []):
cfg.update(yaml.load(open(inc)))
El único inconveniente es que las variables en los incluidos siempre anularán las variables en main, y no hay forma de cambiar esa prioridad cambiando donde aparece la declaración "includes:" en el archivo main.yml.
En un punto ligeramente diferente, yaml no soporta, ya que no está realmente diseñado como un marcado exclusivamente basado en archivos. ¿Qué significaría una inclusión si lo obtuviera en respuesta a una solicitud ajax?
No, YAML no incluye ningún tipo de declaración de "importación" o "inclusión".
Probablemente no fue compatible cuando se hizo la pregunta, pero puede importar otro archivo YAML en uno:
imports: [/your_location_to_yaml_file/Util.area.yaml]
Aunque no tengo ninguna referencia en línea, pero esto funciona para mí.
Si estás usando la versión de Symfony de YAML , esto es posible, así:
imports:
- { resource: sub-directory/file.yml }
- { resource: sub-directory/another-file.yml }
Su pregunta no solicita una solución de Python, pero aquí hay una que usa PyYAML .
PyYAML le permite adjuntar constructores personalizados (como !include
Include) al cargador YAML. He incluido un directorio raíz que puede configurarse para que esta solución admita referencias de archivos relativas y absolutas.
Solución basada en clase
Aquí hay una solución basada en clase, que evita la variable raíz global de mi respuesta original.
Vea esta gist para una solución Python 3 similar y más robusta que utiliza una metaclase para registrar el constructor personalizado.
import yaml
import os.path
class Loader(yaml.SafeLoader):
def __init__(self, stream):
self._root = os.path.split(stream.name)[0]
super(Loader, self).__init__(stream)
def include(self, node):
filename = os.path.join(self._root, self.construct_scalar(node))
with open(filename, ''r'') as f:
return yaml.load(f, Loader)
Loader.add_constructor(''!include'', Loader.include)
Un ejemplo:
foo.yaml
a: 1
b:
- 1.43
- 543.55
c: !include bar.yaml
bar.yaml
- 3.6
- [1, 2, 3]
Ahora los archivos se pueden cargar usando:
>>> with open(''foo.yaml'', ''r'') as f:
>>> data = yaml.load(f, Loader)
>>> data
{''a'': 1, ''b'': [1.43, 543.55], ''c'': [3.6, [1, 2, 3]]}
Tal vez esto pueda inspirarte, intenta alinearte con las convenciones jbb:
https://docs.openstack.org/infra/jenkins-job-builder/definition.html#inclusion-tags
- job: name: test-job-include-raw-1 builders: - shell: !include-raw: include-raw001-hello-world.sh