Creating Custom Tag in PyYAML

后端 未结 4 1432
轮回少年
轮回少年 2021-01-05 08:31

I\'m trying to use Python\'s PyYAML to create a custom tag that will allow me to retrieve environment variables with my YAML.

import os
import yaml

class En         


        
4条回答
  •  佛祖请我去吃肉
    2021-01-05 09:16

    I'd like to share how I resolved this as an addendum to the great answers above provided by Anthon and Fredrick Brennan. Thank you for your help.

    In my opinion, the PyYAML document isn't real clear as to when you might want to add a constructor via a class (or "metaclass magic" as described in the doc), which may involve re-defining from_yaml and to_yaml, or simply adding a constructor using yaml.add_constructor.

    In fact, the doc states:

    You may define your own application-specific tags. The easiest way to do it is to define a subclass of yaml.YAMLObject

    I would argue the opposite is true for simpler use-cases. Here's how I managed to implement my custom tag.

    config/__init__.py

    import yaml
    import os
    
    environment = os.environ.get('PYTHON_ENV', 'development')
    
    def __env_constructor(loader, node):
        value = loader.construct_scalar(node)
        return os.environ.get(value)
    
    yaml.add_constructor(u'!ENV', __env_constructor)
    
    # Load and Parse Config
    __defaults      = open('config/defaults.yaml', 'r').read()
    __env_config    = open('config/%s.yaml' % environment, 'r').read()
    __yaml_contents = ''.join([__defaults, __env_config])
    __parsed_yaml   = yaml.safe_load(__yaml_contents)
    
    settings = __parsed_yaml[environment]
    

    With this, I can now have a seperate yaml for each environment using an env PTYHON_ENV (default.yaml, development.yaml, test.yaml, production.yaml). And each can now reference ENV variables.

    Example default.yaml:

    defaults: &default
      app:
        host: '0.0.0.0'
        port: 500
    

    Example production.yaml:

    production:
      <<: *defaults
      app:
        host: !ENV APP_HOST
        port: !ENV APP_PORT
    

    To use:

    from config import settings
    """
    If PYTHON_ENV == 'production', prints value of APP_PORT
    If PYTHON_ENV != 'production', prints default 5000
    """
    print(settings['app']['port'])
    

提交回复
热议问题