Skip to content

gftools builder crashes with TypeError in ninja_syntax.py when processing nested list variables (e.g., splitItalic: true) #1172

@aaronbell

Description

@aaronbell

While assisting @RosaWagner with investigating https://github.com/zalando/sans/actions/runs/21861926252/job/63093057085?pr=42, I found an error in gftools' treatment of nested list variables.

Description

When running gftools builder on a config.yaml file that includes operations generating complex metadata fixes—such as splitItalic: true or - operation: fix—the build process crashes while attempting to generate the Ninja build file.

The builder is internally passing a Python list of lists (representing the table fixes) directly to the Ninja writer. Because ninja-syntax expects a flat list of strings to join, it throws a TypeError when it encounters the nested lists.

Proposed Resolution

The gftools builder needs to intercept these complex internal data structures and safely serialize them into JSON strings before passing them to the Ninja writer payload.

--
I was able to resolve this locally via modifying the ninja_syntax.py file to serialize the data, but the fix should happen in gftools directly.

        if isinstance(value, list):
            try:
                value = ' '.join(filter(None, value))
            except TypeError:
                # Bypass gftools bug: safely serialize nested lists into a quoted JSON string
                import json
                import shlex
                value = shlex.quote(json.dumps(value))

Gemini suggests modifying: https://github.com/googlefonts/gftools/blob/main/Lib/gftools/builder/operations/__init__.py

@property
    def variables(self):
        return {k: v for k, v in self.original.items() if k != "needs"}

to

@property
    def variables(self):
        import json
        import shlex
        
        vars_dict = {}
        for k, v in self.original.items():
            if k == "needs":
                continue
            # If the value is a complex structure (like a list of lists), 
            # safely serialize it so ninja-syntax doesn't crash on .join()
            if isinstance(v, (list, dict)) and any(isinstance(i, (list, dict)) for i in (v if isinstance(v, list) else v.values())):
                vars_dict[k] = shlex.quote(json.dumps(v))
            else:
                vars_dict[k] = v
        return vars_dict

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions