Skip to main content

An extensible config file format

Project description

Yay is a non-strict lazily evaluated configuration language. It combines YAML-like data declarations with lazy python expressions. We really hope you like this, please let us know if it’s useful.

It works with python 2.6+, 3.2+ and pypy 1.9.

Yay is very new software, and although we have tested it a lot it is certainly full of bugs. Please subscribe to the yaybu-dev mailing list, which you can find on

All documentation can be found at which includes a tour of the language and an explanation of the grammar.

If you are interested in this project, you should probably check out Yaybu, which uses Yay to drive a complete system deployment and management system. You can find Yaybu at

Changelog for yay

3.1.1 (2013-11-06)

  • Nothing changed yet.

3.1.0 (2013-11-06)

  • Now supported on python 3.2 and python 3.3 (in addition to 2.6, 2.7 and pypy).

  • Many fixes to YAML multiline block mode

  • Lots of test coverage improvements

  • Remove unnecessary support for Extended Slices

  • Command line output is prettier

3.0.1 (2013-09-04)

  • Nothing changed yet.

3.0 (2013-09-04)

  • This is a full rewrite on top of the ply parsing framework.

0.0.62 (2012-09-27)

  • Try importing first as it might yield a hit and save checking the internets for things.

0.0.61 (2012-09-15)

  • An earlier version added validation to stop you replacing lists with mappings or vice versa. However the implementation didn’t consider that you might extend a mapping that comes from a lookup.

0.0.60 (2012-09-15)

  • Fix filter node. It was previously unaware of its parent when expanded and this stopped its children from being able to look up values in the global namespace.

0.0.59 (2012-09-01)

  • Fix setting GPG_TTY.

0.0.58 (2012-08-15)

  • Capture GPG failures and throw exceptions. Unfortunately without talking to GPG over the status fd we can’t get much information about what went wrong.

  • Run in batch mode. This stops GPG from ignoring the stdout/stderr redirection but means you now MUST use a GPG agent as it can no longer prompt for a passphrase.

  • GPG_TTY is set - this means gpg-agent can actually prompt now.

  • Fix Config.add when using nested dictionaries.

0.0.57 (2012-08-02)

  • Add new lazy lookup method to the root config object. This is useful if you are building a lazy iterator over a dictionary key:

    def example_iterator(self, expression):
            for item in expression.expand():
                resolved = item.resolve()
        except CreateObjectError:
            # You can get line and column from the item object!
        except yay.errors.Error:
            # Ideal place to wrap Yay errors in your apps exceptions

    You can see here that the API lets you defer any messy “key not found” exceptions until the iterator needs to access they key - and the iterator already has apropriate exception handling.

  • Add additional test cases for backwards compatibility

0.0.56 (2012-07-31)

  • Code paths are now decorated with tracing context. This __context__ metadata gives a sense of what yay was doing when it went off the handle.

0.0.55 (2012-07-25)

  • Fix PackageIndex to not warn() on missing package - we capture this ourselves!

0.0.54 (2012-07-22)

  • Line number and file information should be available for nodes that have been cloned leading to much more useful error output.

  • Index errors on Sequence nodes will be properly dealt with

  • Treating a sequence as a mapping node will trigger an appropriate Yay error.

0.0.53 (2012-07-21)

  • Allow python code using yay to set default configuration for openers

0.0.52 (2012-07-21)

  • Fix regression in package:// for existing users..

0.0.51 (2012-07-19)

  • Introduce a lazily evaluated mechanism for expanding the search path:

      - package://some.egg/

    Any subsequent .import statements will use the modified search path.

  • If a package specified either as part of a .include or as part of a .search is not available on sys.path then an attempt will be made to install it.

  • Can pass config to opener backends using .config. For example, you might want to have the package:// opener to use your internal package repository:

                index: https://my-python-mirror/simple/
                username: joe
                password: penguin55

    You can use variable subsitution and to define the password in a GPG encrypted yay file or to push the variable from application that is using Yay.

  • Yay openers support basic auth in the url. If used with .include then use of secrets to concatenate to hide the password is suggsted:

    # Define ${password} in a GPG armored file
    .include: home://.credentials.yay.gpg
      - https://username:${password}
  • .include and .search may be a single scalar value rather than a list if you desire.

0.0.50 (2012-07-10)

  • Allow expressions to return an empty dictionary:

    result: ${site.vhosts else {}}

0.0.49 (2012-06-27)

  • Added a string replacement function allowing people to do things like:

    something: jolly good show
    slug: ${replace(something, " ", "-")}

    thereafter the value of slug will be jolly-good-show.

0.0.48 (2012-04-07)

  • Support in operator during foreach:

    result.foreach v in somevalues if v in somelist: ${v}.

0.0.47 (2012-04-06)

  • Allow expressions to contain literal strings and floats:

    result: ${site.description else "No description"}
    result2: ${3.212121}
  • Allow expressions to return an empty list:

    result: ${site.vhosts else []}
  • Allow equality checks of undefined keys to pass or fail appropriately without raising a NoMatching exception:

        a: 1
        c: 2
    res1.foreach p in data if data[p].a = 2: ${p}
    res2.foreach p in data if data[p].c != 1: ${p}

    res1 will be empty as neither part1 nor part2 have an a field with a value of 2, and res2 will contain part1 and part2 as neither have a c field wth a value of 1.

  • Add an undefined that raises a NoMatch exception when resolved. This allows one to filter by whether or not a key is present in a mapping.


    a: 1


    c: 2

    res.foreach p in data if data[p].c = undefined: ${p}

    res will contain part1 as part1 doesn’t have a c field.

0.0.46 (2012-04-04)

  • Fix else expression inside an .include

0.0.45 (2012-03-22)

  • You can now pass an etag to the open method of Openers. This will throw a NotModified exception if etag matching shows the request URI is unchanged. This can be used to implement caching strategies and reduce the amount of data that yay pulls over network connections:

    >>> from yay.openers import Openers
    >>> fp = Openers().open("/tmp/somefile, etag="1234535462356456fa")
    NotModified: File '/tmp/somefile' not modified
  • All openers will now have an etag property. This property can be None if a suitable etag cannot be provided. Otherwise it should be some hash that guarantees the state of the file so that later requests can just ask “has it changed since it was in this state”. To get an etag:

    >>> from yay.openers import Openers
    >>> fp = Openers().open("/tmp/somefile")
    >>> print fp.etag

0.0.44 (2012-03-17)

  • Fix .select when value that is resolved is a complicated expression.

  • Better validation when you accidentally mix up list and mapping types.

0.0.43 (2012-02-12)

  • Fix handling of paths on searchpath without a scheme.

0.0.42 (2012-01-01)

  • Change how Django boxing works so that a .bind isn’t required for the boxing mechanism to work. This allows raw Django mdoels to be pushed into Yay using the .add API from 0.0.41.

0.0.41 (2011-12-21)

  • You can inject Python objects into the Yay Config object, as long as Yay knows how to box them. If you had a Yay file like this:

    hello: abc
    result: ${hello}

    And the following python code:

    from yay.config import Config
    c = Config()

    Then the following assertion is true:

    c.get() == dict(hello="xyz", result="xyz")

0.0.40 (2011-12-13)

  • Fixes calling macros defined in other files.

0.0.39 (2011-11-30)

  • Fix string builder so that configs containing secrets can be pickled.

0.0.38 (2011-11-22)

  • This is a bugfix release to improve existing features, especially boxing of foreign data.

  • Move more code away from direct use of Boxed to

  • Wrap any dictionary objects in a Mapping object so they can be merged with other mappings that come from within Yay.

0.0.37 (2011-11-20)

  • Brown paper bag release to remove stray print.

0.0.36 (2011-11-20)

  • There is now a home:// URL handler. For my laptop this means:

    import yay

    Is equivalent to:

    import yay
  • Improve searchpath handling to better cope with URIs.

  • Add support for else keyword in expressions as per YEP2:

        bar: ${foo.baz else 52}

    This is useful for providing sensible defaults in your recipes.

  • The loop block is now autoflattening. This means that constructs that yield lists of lists will be turned into 1-dimensional lists by .foreach. For example:

      - - - a
          - b
        - c
      - d
    someotherlist.foreach var in somelist: ${var}

    The list someotherlist would now contain:

     - a
     - b
     - c
     - d

    The previous behaviour can be obtained with the chain modifier:

    somelist.foreach var in somelist chain: ${var}
  • Add a simple macro language. This is highly EXPERIMENTAL and subject to frequent syntax tweaks. You can define a reusable block of configuration using the .define statement. An example of this in Yaybu to create a reusable virtualenv step:

    .define Virtualenv:
      - Directory:
          name: ${venv.location}
          owner: ${venv.owner}
      - Execute:
          name: create-virtualenv-${venv.location}
          command: virtualenv --no-site-packages ${venv.location}
          creates: ${venv.location}/bin/pip
      - Execute:
          name: install-requirements-${venv.location}
          command: ${venv.location}/bin/pip install -r ${venv.requirements}

    The macro is callable inline as part of an expression. For example:

      .foreach venv in virtualenvs: ${Virtualenv!}

    Equally you can do this:

    .define Virtualenv:
      - Directory:
          name: ${location}
          owner: ${owner}
    # SNIP
          location: /tmp/example
          owner: dave
          requirements: /tmp/requirements.txt

0.0.35 (2011-11-12)

  • This release refactors ProtectedString to make it more useful outside Yay. Instead of importing yay.protectedstring.ProtectedString you should import yay.String

  • You can now pass strings to the constructor and they will automatically be wrapped. Previously you had to pass StringPart’s to the constructor. That is now an internal implementation detail.

  • There is now an extend() function. This accepts lists that contains a mixture of raw strings and other String objects:

    s = String("echo")
    y = String("supersekritpassword", secret=True)
    s.extend(["a", "b", y, "d"])
  • There is now an as_list() function. This returns all the parts you have added to a string. This is useful if you are building a command line to pass to subprocess:

    s.String(["someprogram", "--pasword"])
    # Log the obfuscated version but execute with the real password
    p = subprocess.Popen(s.as_list(secret=False), cwd="/")
  • There is now a secret call in Yay for exercising secret Yay without needing to use GPG. This is mainly for test purposes, but might be useful if you have strings which arent closely guarded secrets yet you dont want them show in logs. To use it you do something like this:

      - Checkout:
          name: /checkouts/mycode
          scm_password.secret: mypassword

    This example is from Yaybu and would stop Yaybu from logging your SVN password.

0.0.34 (2011-11-10)

  • Compose now reuses Opener() rather than creating a new one for each stream loaded.

0.0.33 (2011-11-10)

  • You can now pass searchpath to the Config object.

0.0.32 (2011-11-10)

  • Working package:// imports

0.0.31 (2011-11-10)

  • Better absolute path handling

0.0.30 (2011-11-10)

  • Any stream objects returned by the Openers() API now have a len property.

  • The internal Openers() API can now load from any package on the Python path:

    fp = Openers().open("package://some.egg/hello.txt")

0.0.29 (2011-11-10)

  • The internal Openers() API now has support for a search path:

    fp = Openers(searchpath=['file:///home/john', '']).open("foo.txt")
  • The Openers() API now supports https://

0.0.28 (2011-11-07)

  • Fully remove all Mapping default crud

0.0.27 (2011-11-03)

  • Fix ${django.SomeModel} to properly resolve

0.0.26 (2011-11-03)

  • Add support for a Django DataStore

  • Allow objects exposed from Python, including simple method calls

0.0.25 (2011-10-28)

  • Fix regression in .append

0.0.24 (2011-10-22)

  • Can now foreach over a mapping

  • Add .foreach x in y if x.z = a

  • Add ‘.with expression as foo:’

  • Add chain and nochain mode to foreach. chain is the default.

  • Major refactoring, context variable is no longer needed to resolve the graph

  • The ‘semi_resolve’ API is now more correctly named ‘expand’

0.0.23 (2011-07-26)

  • $$ escapes $ - so $${foo} is treated as a string, not a variable lookup

0.0.22 (2011-07-19)

  • Remove spurious debug messages

  • Fix .import on a .foreach

0.0.21 (2011-07-19)

  • New .include directive that can use variables and appear anywhere in file

0.0.20 (2011-06-29)

  • Change ProtectedString to inherit from basestring

0.0.19 (2011-06-29)

  • Concept of a protected yay file, where any strings that end up containing secrets will be obscured

  • ProtectedString that handles concatenation of protected and unprocted strings

  • GPG used to decrypt .yay.gpg files, any variables they spawn are protected

0.0.18 (2011-06-10)

  • Brown paper bag to remove stray debugging scaffold

0.0.17 (2011-06-10)

  • Fix appending to None

  • Fix list access where list has already been partially resolved

0.0.16 (2011-06-10)

  • Add range tests, fixed range() as a ${} expansion

  • Added sum() (but no syntax sugar)

  • Allow index operations against filters

  • Fix iterating over Flatten() nodes

  • Fix iterating over ForEach() nodes

  • Add test for empty documents that got away

0.0.15 (2011-05-18)

  • Raise an error if field access is invalid

  • Add a LanguageError for displaying helpful messages when yay files are wrong

  • It is now a bug if we raise an error that doesn’t descend from yay.errors.Error

0.0.14 (2011-05-12)

  • Don’t break on empty (e.g. {})

0.0.13 (2011-03-06)

  • Fix Append nodes

0.0.12 (2011-03-03)

  • Export that method ;)

0.0.11 (2011-03-03)

  • Don’t be unicode unless needed

  • Add a dump() method

0.0.10 (2011-02-22)

  • Don’t chomp whitespace in bracketed_expression

0.0.9 (2011-02-22)

  • Don’t chomp whitespace in templated_string

0.0.8 (2011-02-18)

  • Replace ‘foreach bar as foo’ with ‘foreach foo in bar’

0.0.7 (2011-02-16)

  • Numbers starting 0 are treated as base 8.

0.0.6 (2011-02-13)

  • Avoid unicode mapping keys where possible - they break kwargs in Py2.x

0.0.5 (2011-02-09)

  • Add support for nested foreach

  • At least for now, support a ruby style .flatten

0.0.4 (2011-02-04)

  • {foo} can be interpreted as a map by YAML. For now, we will use ${foo} to avoid this

  • Function calls to python are implemented - there is now range()

  • There is now a key so switch statements can be used

0.0.3 (2011-01-24)

  • Egg packaging fixes

0.0.2 (2011-01-24)

  • Lots more unittests for expression evaluation and expression parsing

  • Drop dependency on OrderedDict


  • This is still pre-release, no API or language stability guarantees

  • Variable resolving is now done in expression tree without a seperate resolve stage

  • Uses pyparsing to parse {foo[bar][@.baz > 5]} type syntax


  • This is a pre-alpha release to experiment with what we can and can’t do.

  • New PyYAML Loader subclass for getting ordered maps without ugly !!omap markup

  • Resolves {} markup within YAML nodes

  • Lazily evaluates .copy, .append and .remove instructions

Project details

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution (101.1 kB view hashes)

Uploaded source

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page