Skip to content

Parsers API#

Chef artifact parsing modules for recipes, templates, resources, attributes, and more.

Overview#

The souschef.parsers package contains specialized parsers for different Chef artifact types. Each parser extracts structured data from Ruby-based Chef code.


Parser Modules#

The following parsers are available:

  • Recipe Parser (souschef.parsers.recipe) - Parse Chef recipes
  • Template Parser (souschef.parsers.template) - Extract ERB template variables
  • Resource Parser (souschef.parsers.resource) - Parse custom resources
  • Attributes Parser (souschef.parsers.attributes) - Parse attribute files
  • Metadata Parser (souschef.parsers.metadata) - Parse cookbook metadata
  • InSpec Parser (souschef.parsers.inspec) - Parse InSpec profiles
  • Habitat Parser (souschef.parsers.habitat) - Parse Habitat plans
  • Ansible Inventory Parser (souschef.parsers.ansible_inventory) - Parse Ansible inventory files and environments (NEW)

For usage examples and patterns, see the Examples Guide.


Common Parsing Patterns#

Ruby AST Parsing#

All parsers use regular expressions and Ruby syntax analysis:

import re
from pathlib import Path

class RecipeParser:
    """Parse Chef recipes."""

    RESOURCE_PATTERN = re.compile(
        r'^\s*(\w+)\s+([\'\"]([^'\"]+)[\'\"]|[\w-]+)\s+do',
        re.MULTILINE
    )

    def parse(self, content: str) -> dict:
        """Extract resources from recipe."""
        resources = []
        for match in self.RESOURCE_PATTERN.finditer(content):
            resource_type = match.group(1)
            resource_name = match.group(3) or match.group(2)
            resources.append({
                'type': resource_type,
                'name': resource_name
            })
        return {'resources': resources}

Error Handling#

def parse_file(filepath: str) -> dict:
    """Parse file with error handling."""
    try:
        with open(filepath, 'r') as f:
            content = f.read()
        return parse(content)
    except FileNotFoundError:
        raise ValueError(f"File not found: {filepath}")
    except UnicodeDecodeError:
        raise ValueError(f"Invalid encoding in file: {filepath}")
    except Exception as e:
        raise ValueError(f"Parse error: {e}")

Usage Examples#

Recipe Parsing#

from souschef.parsers.recipe import parse_recipe_file

# Parse a recipe
result = parse_recipe_file('recipes/default.rb')

# Access parsed data
for resource in result['resources']:
    print(f"{resource['type']}: {resource['name']}")

Template Variable Extraction#

from souschef.parsers.template import parse_template_file

# Parse ERB template
result = parse_template_file('templates/config.erb')

# List all variables
for var in result['variables']:
    print(f"Variable: {var}")

Attribute Parsing#

from souschef.parsers.attributes import parse_attributes_file

# Parse attributes
result = parse_attributes_file('attributes/default.rb')

# Access attributes
for attr in result['attributes']:
    print(f"{attr['precedence']}['{attr['key']}']
 = {attr['value']}")

Parser Performance#

Optimization Tips#

  1. Compile Patterns Once: Compile regex patterns at module level
  2. Stream Large Files: Use iterators for large files
  3. Cache Results: Cache parsed results when processing multiple times
  4. Parallel Processing: Use multiprocessing for batch operations
from concurrent.futures import ProcessPoolExecutor
from pathlib import Path

def parse_cookbook_parallel(cookbook_path: str) -> dict:
    """Parse all recipes in parallel."""
    recipes = list(Path(cookbook_path).glob('recipes/*.rb'))

    with ProcessPoolExecutor() as executor:
        results = executor.map(parse_recipe_file, recipes)

    return {'recipes': list(results)}

Testing Parsers#

See test suite for parser testing examples:


See Also#