Для французского javascript js мы использовали другую кодировку:
<script src="./js/MyScript.js" type="text/javascript" charset="iso-8859-1"></script>
Нет, YAML не включает никаких выражений «import» или «include».
Если вы используете версию YAML Symfony , это возможно, например:
imports:
- { resource: sub-directory/file.yml }
- { resource: sub-directory/another-file.yml }
Расширяя ответ @ Josh_Bode, вот мое собственное решение PyYAML, которое имеет преимущество в том, что является автономным подклассом yaml.Loader
. Он не зависит ни от глобальных переменных уровня модуля, ни от изменения глобального состояния модуля 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
Я думаю, что решение, используемое @ maxy-B, выглядит великолепно. Однако для меня это не удалось с вложенными включениями. Например, если config_1.yaml включает config_2.yaml, который включает config_3.yaml, возникла проблема с загрузчиком. Однако, если вы просто указываете новый класс загрузчика на себя при загрузке, он работает! В частности, если мы заменим старую функцию _include очень немного измененной версией:
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
После размышлений я согласен с другими комментариями, что вложенная загрузка не подходит для yaml в целом, поскольку входной поток может не быть файлом, но это очень полезно!
Включения не поддерживаются напрямую в YAML, насколько я знаю, вам придется самостоятельно создать механизм, однако, как правило, это легко сделать.
Я использовал YAML в качестве языка конфигурации в своих приложениях на Python, и в этом случае часто определяю такое соглашение:
>>> main.yml <<<
includes: [ wibble.yml, wobble.yml]
Затем в своем (Python) коде я делаю:
import yaml
cfg = yaml.load(open("main.yml"))
for inc in cfg.get("includes", []):
cfg.update(yaml.load(open(inc)))
Единственным недостатком является то, что переменные во включениях всегда будут переопределять переменные в main, и нет никакого способа изменить этот приоритет, изменив место, где в файле main.yml присутствует оператор "includes:" .
В несколько ином аспекте, YAML не поддерживает включения, поскольку они на самом деле не спроектированы так, как исключительно разметка на основе файлов. Что будет означать включение, если вы получите его в ответ на запрос AJAX? ?
Может быть, это может вдохновить вас, попробуйте привести в соответствие с соглашениями 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
С Symfony его обработка yaml косвенно позволит вам вкладывать файлы yaml. Хитрость заключается в том, чтобы использовать опцию parameters
. Например:
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%"
Результат будет таким же как:
whatever:
thing:
option: "value"
foo:
- "bar"
- "baz"
other_thing:
option: "value"
foo:
- "bar"
- "baz"
Вероятно, это не было поддержано, когда был задан вопрос, но вы можете импортировать другой файл YAML в один:
imports: [/your_location_to_yaml_file/Util.area.yaml]
Хотя у меня нет ссылки в Интернете, но это работает для меня.
Ваш вопрос не требует Python-решения, но вот тот, который использует PyYAML .
PyYAML позволяет подключать пользовательские конструкторы (например, !include
) к загрузчику YAML. Я включил корневой каталог, который можно настроить так, чтобы это решение поддерживало относительные и абсолютные ссылки на файлы.
Вот решение на основе классов, которое позволяет избежать глобальной корневой переменной моего исходного ответа.
См. gist для получения аналогичного, более надежного решения Python 3, которое использует метакласс для регистрации пользовательского конструктора.
import yaml
import os
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)
Пример:
foo.yaml
a: 1
b:
- 1.43
- 543.55
c: !include bar.yaml
bar.yaml
- 3.6
- [1, 2, 3]
Теперь файлы могут быть загружены с помощью:
>>> 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]]}
К сожалению, YAML не предоставляет этого в своем стандарте.
Но если вы используете Ruby, есть гем, обеспечивающий запрашиваемую вами функциональность путем расширения библиотеки ruby YAML: https://github.com/entwanderer/yaml_extend
Для пользователей Python вы можете попробовать pyyaml-include .
pip install pyyaml-include
import yaml
from yamlinclude import YamlIncludeConstructor
YamlIncludeConstructor.add_to_loader_class(loader_class=yaml.FullLoader, base_dir='/your/conf/dir')
with open('0.yaml') as f:
data = yaml.load(f, Loader=yaml.FullLoader)
print(data)
Предположим, у нас есть такие файлы YAML :
├── 0.yaml
└── include.d
├── 1.yaml
└── 2.yaml
1.yaml
Содержимое]: name: "1"
2.yaml
Содержимое: name: "2"
На верхнем уровне:
Если 0.yaml
было:
!include include.d/1.yaml
Мы получим:
{"name": "1"}
В отображении:
Если 0.yaml
было:
file1: !include include.d/1.yaml
file2: !include include.d/2.yaml
Мы получим:
file1:
name: "1"
file2:
name: "2"
В последовательности:
Если 0.yaml
было:
files:
- !include include.d/1.yaml
- !include include.d/2.yaml
Мы получим:
files:
- name: "1"
- name: "2"
ℹ Примечание :
Имя файла может быть либо абсолютным (например,
/usr/conf/1.5/Make.yml
), либо относительным (например,../../cfg/img.yml
).
Имя файла может содержать подстановочные знаки в стиле оболочки. Данные, загруженные из файла (ов), найденных с помощью подстановочных знаков, будут установлены в последовательности.
Если 0.yaml
было:
files: !include include.d/*.yaml
Мы получим:
files:
- name: "1"
- name: "2"
ℹ Примечание :
- Для
Python>=3.5
, если аргументrecursive
тега!include
YAML равенtrue
, шаблон“**”
будет соответствовать любым файлам и нулю или более каталогов и подкаталогов.- Использование шаблона
“**”
в больших деревьях каталогов может потребовать чрезмерного количества времени из-за рекурсивного поиска.
Чтобы включить аргумент recursive
, мы напишем тег !include
в режиме Mapping
или Sequence
:
Sequence
режим: !include [tests/data/include.d/**/*.yaml, true]
Mapping
: !include {pathname: tests/data/include.d/**/*.yaml, recursive: true}
Стандарт YML не делает , определяют способ сделать это. И эта проблема не ограничивает себя YML. JSON имеет те же ограничения.
Много приложений, которые используют YML или JSON, базировались, конфигурации сталкиваются с этой проблемой в конечном счете. И когда это происходит, они составляют свою собственную конвенцию .
, например, для определений API чванства:
$ref: 'file.yml'
, например, для докера составляют конфигурации:
services:
app:
extends:
file: docker-compose.base.yml
Я делаю некоторые примеры для Вашей ссылки.
import yaml
main_yaml = """
Package:
- !include _shape_yaml
- !include _path_yaml
"""
_shape_yaml = """
# Define
Rectangle: &id_Rectangle
name: Rectangle
width: &Rectangle_width 20
height: &Rectangle_height 10
area: !product [*Rectangle_width, *Rectangle_height]
Circle: &id_Circle
name: Circle
radius: &Circle_radius 5
area: !product [*Circle_radius, *Circle_radius, pi]
# Setting
Shape:
property: *id_Rectangle
color: red
"""
_path_yaml = """
# Define
Root: &BASE /path/src/
Paths:
a: &id_path_a !join [*BASE, a]
b: &id_path_b !join [*BASE, b]
# Setting
Path:
input_file: *id_path_a
"""
# define custom tag handler
def yaml_import(loader, node):
other_yaml_file = loader.construct_scalar(node)
return yaml.load(eval(other_yaml_file), Loader=yaml.SafeLoader)
def yaml_product(loader, node):
import math
list_data = loader.construct_sequence(node)
result = 1
pi = math.pi
for val in list_data:
result *= eval(val) if isinstance(val, str) else val
return result
def yaml_join(loader, node):
seq = loader.construct_sequence(node)
return ''.join([str(i) for i in seq])
def main():
# register the tag handler
yaml.SafeLoader.add_constructor(tag='!include', constructor=yaml_import)
yaml.SafeLoader.add_constructor(tag='!product', constructor=yaml_product)
yaml.SafeLoader.add_constructor(tag='!join', constructor=yaml_join)
config = yaml.load(main_yaml, Loader=yaml.SafeLoader)
pk_shape, pk_path = config['Package']
pk_shape, pk_path = pk_shape['Shape'], pk_path['Path']
print(f"shape name: {pk_shape['property']['name']}")
print(f"shape area: {pk_shape['property']['area']}")
print(f"shape color: {pk_shape['color']}")
print(f"input file: {pk_path['input_file']}")
if __name__ == '__main__':
main()
производит
shape name: Rectangle
shape area: 200
shape color: red
input file: /path/src/a