lkml icon indicating copy to clipboard operation
lkml copied to clipboard

Fix boolean value parsing to return Python booleans

Open owlas opened this issue 5 months ago • 0 comments

Summary

This PR fixes #15 by implementing proper boolean parsing in the LKML parser. Boolean values like yes, no, true, and false are now correctly converted to Python bool objects instead of remaining as strings.

Changes

Core Implementation

  • Modified DictVisitor.visit_token() in lkml/simple.py to:

    • Check if token is quoted (using QuotedSyntaxToken) - if so, preserve as string
    • Convert unquoted boolean strings (yes/no/true/false) to Python bool objects
    • Handle case-insensitive matching for boolean values
  • Updated DictParser.parse_any() to handle boolean type during serialization:

    • Added bool to accepted types
    • Convert Python booleans back to yes/no for LKML output

Test Coverage

Added comprehensive tests in tests/test_simple.py:

  • test_boolean_parsing_yes_no - Basic yes/no conversion
  • test_boolean_parsing_true_false - Basic true/false conversion
  • test_boolean_parsing_case_insensitive - Case insensitive handling
  • test_boolean_parsing_non_boolean_strings - Non-boolean strings remain unchanged
  • test_boolean_round_trip - Round-trip serialization works correctly
  • test_quoted_boolean_strings_remain_strings - Quoted values remain as strings
  • test_mixed_quoted_unquoted_booleans - Mixed quoted/unquoted in same structure
  • test_filter_with_quoted_boolean_values - Filter blocks with quoted booleans
  • test_case_sensitivity_with_quoted_values - Case preservation for quoted values
  • test_edge_cases_quoted_vs_unquoted - Edge cases and empty strings

Examples

Before (incorrect):

>>> import lkml
>>> parsed = lkml.load('view: test { dimension: field { hidden: yes } }')
>>> parsed['views'][0]['dimensions'][0]['hidden']
'yes'  # String, not boolean\!

After (correct):

>>> import lkml
>>> parsed = lkml.load('view: test { dimension: field { hidden: yes } }')
>>> parsed['views'][0]['dimensions'][0]['hidden']
True  # Proper Python boolean

>>> # Quoted values remain as strings
>>> parsed = lkml.load('view: test { dimension: field { label: "yes" } }')
>>> parsed['views'][0]['dimensions'][0]['label']
'yes'  # String, as expected

Behavior Summary

  • hidden: yesTrue (bool)
  • primary_key: noFalse (bool)
  • suggestions: trueTrue (bool)
  • can_filter: falseFalse (bool)
  • label: "yes""yes" (str)
  • value: "true""true" (str)
  • ✅ Case insensitive for unquoted values
  • ✅ Case preserved for quoted values
  • ✅ Round-trip serialization works correctly

Fixes #15

owlas avatar Jul 14 '25 19:07 owlas