Search code examples
pythonjsonvisual-studio-codeintellisensetype-hinting

Type Validation Based on the Contents of a JSON file


This is similar to this question, except here the valid arguments are contained in a json file.

In Python using VSCode, I'd like to define a type which is based on the contents of a JSON file. Is such a thing possible?

For example,

data.json contains data for three object: an_option, another_option, and yet_another_option.

{
   "data": [
        {
            "id":"an_option",
            "property":"value"
        },
        {
            "id":"another_option",
            "property":"value"
        },
        {
            "id":"yet_another_option",
            "property":"value"
        }
    ]
}

In main.py, I have defined a type that allows Intellisence to suggest these options when writing data of that type. Intellisense suggests my options as parameters when calling test_function.

from typing import Literal


MyType = Literal[
    "an_option",
    "another_option",
    "yet_another_option"
]

def test_function(option_chosen: MyType):
    print(option_chosen)
    pass

test_function("an_option")

However, then the names for my options must be replicated in multiple files, which seems cumbersome. I would like the MyType type to update when I add entries to the data list from data.json. For example, something like

from typing import Literal
import json 
file = open('data.json', 'r')
data = json.load(file)
list_of_options_with_properties = data["data"]
option_name_list = list()
for option in  list_of_options_with_properties:
    option_name_list.append(option["id"])
MyType = Literal[tuple(option_name_list)]
def test_function(option_chosen: MyType):
    print(option_chosen)
    pass

test_function("an_option")

Unfortunately, this does not help autocomplete becuase option_name_list is not defined until runtime. Is it possible to have Intellisense to read the contents of the json file to define a type?


Solution

  • Directly with Python's type system, this isn't feasible due to the nature of static type checkers operating before code execution. However, you can craft a solution using a code generation tool. You could write a script that reads your JSON file and automatically updates a module with the relevant Literal type.

    # codegen.py
    import json
    from typing import List
    
    def generate_literal_type_from_json(filename: str) -> List[str]:
        with open(filename, 'r') as f:
            data = json.load(f)
        options = [item["id"] for item in data["data"]]
        return options
    
    if __name__ == "__main__":
        options = generate_literal_type_from_json("data.json")
        with open("generated_types.py", "w") as f:
            f.write(f"from typing import Literal\n\n")
            f.write(f"MyType = Literal{tuple(options)}\n")
    
    

    Run this script, and it will produce a generated_types.py file. You can then use the MyType type in your main code from this generated file.