Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Parse buildout config into constraints and requirements. See
# -*- coding: utf-8 -*-
# from configparser import ConfigParser
from zc.buildout import buildout
import re
# We read this buildout config file:
configfile = "buildout.cfg"
# We cannot use configparser, because buildout configs contain 'duplicate' options:
# $ grep -i chameleon versions.cfg
# Chameleon = 3.6.2
# chameleon = 3.6.2
# So we use the buildout configparser:
config = buildout.Buildout(configfile, [])
# Get the constraints from the version pins.
# Ah, the versions get set directly on the config, nice!
# Note: this works like a dictionary, but is a class 'zc.buildout.buildout.Options'.
versions = config.versions
constraints_file = "parsed-constraints.txt"
with open(constraints_file, "w") as cfile:
cfile.write("# Constraints parsed from {}\n".format(configfile))
for package, version in sorted(versions.items()):
cfile.write("{}=={}\n".format(package, version))
print("Wrote versions as constraints to {}.".format(constraints_file))
# Now for the requirements.
# We must avoid automatically downloading eggs, so we use the raw config.
config = config._raw
reqs = set(["zc.buildout"])
extensions = config["buildout"].get("extensions", "").split()
# Regular expression for ${test:eggs}.
# Probably needs some tweaks, but works for now.
re_option_reference = re.compile(r"\${(.*):(.*)}")
def get_option_reference(text):
# When text is ${test:eggs}, return ("test", "eggs").
match = re_option_reference.match(text)
if match is None:
return match.groups()
def read_eggs(section, option="eggs"):
# Get the eggs. Tricky is that these can be split on one line or on multiple lines.
# And there can be extras, either "package[extra]" or "package [extra]",
# making splitting harder.
result = set()
for line in config[section].get(option, "").splitlines():
line = line.strip()
if not line:
# Get rid of spaces in front of an [extra].
line = "[".join([part.strip() for part in line.split("[")])
for egg in line.split():
# Because we use the raw buildout config, we may have eggs like this:
# ${test:eggs}
ref = get_option_reference(egg)
if not ref:
other_section, other_option = ref
if other_section == ".":
other_section = section
eggs = read_eggs(other_section, other_option)
return result
for section in config.keys():
data = config[section]
recipe = data.get("recipe")
if recipe:
# Get the eggs options.
requirements_file = "parsed-requirements.txt"
with open(requirements_file, "w") as rfile:
rfile.write("# Requirements parsed from {}\n".format(configfile))
for package in sorted(reqs):
print("Wrote requirements to {}.".format(requirements_file))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment