Getting Started¶
Installation¶
Install pybeerxml from PyPI:
Or with uv:
Parsing a file¶
Pass the path to a .beerxml file to Parser.parse(). A single file can contain multiple recipes; the method always returns a list.
from pybeerxml import Parser
parser = Parser()
recipes = parser.parse("/path/to/recipe.beerxml")
recipe = recipes[0]
print(recipe.name) # e.g. "Simcoe IPA"
print(recipe.brewer) # e.g. "Joe Smith"
Parsing from a string¶
If you already have the XML content in memory, use parse_from_string():
from pybeerxml import Parser
xml = open("recipe.beerxml").read()
parser = Parser()
recipes = parser.parse_from_string(xml)
Calculated vs. stored values¶
BeerXML files may or may not include pre-calculated values for OG, FG, IBU, ABV, and colour. pybeerxml always exposes both:
| Property | Stored value | Calculated fallback |
|---|---|---|
recipe.og |
From XML if present | recipe.og_calculated |
recipe.fg |
From XML if present | recipe.fg_calculated |
recipe.ibu |
From XML if present | recipe.ibu_calculated |
recipe.abv |
From XML if present | recipe.abv_calculated |
recipe.color |
From XML if present | recipe.color_calculated |
The plain properties (recipe.og, recipe.ibu, etc.) return the stored XML value when available, and automatically fall back to the calculated value otherwise. The _calculated variants always compute from ingredients regardless.
# Uses stored OG from XML, or calculates from fermentables if missing
print(recipe.og)
# Always calculated from the fermentable bill
print(recipe.og_calculated)
# Gravity in degrees Plato
print(recipe.og_plato)
print(recipe.og_calculated_plato)
Working with ingredients¶
recipe = recipes[0]
for hop in recipe.hops:
print(f"{hop.name}: {hop.alpha}% AA, {hop.amount * 1000:.0f}g, {hop.time:.0f} min")
for fermentable in recipe.fermentables:
print(f"{fermentable.name}: {fermentable.amount}kg ({fermentable.addition})")
for yeast in recipe.yeasts:
print(f"{yeast.name}: {yeast.attenuation}% attenuation")
for misc in recipe.miscs:
print(f"{misc.name}: {misc.use}")
Mash steps¶
if recipe.mash:
print(recipe.mash.name)
for step in recipe.mash.steps:
print(f" {step.name}: {step.step_temp}°C for {step.step_time} min")
IBU methods¶
By default, ibu_calculated uses the Tinseth formula. The Rager formula is available directly on each hop:
hop = recipe.hops[0]
ibu_tinseth = hop.bitterness("tinseth", recipe.og_calculated, recipe.batch_size)
ibu_rager = hop.bitterness("rager", recipe.og_calculated, recipe.batch_size)
Development setup¶
Clone the repo and install dependencies with uv:
Run the test suite:
Lint, format, and type-check before submitting a pull request: