phillipdupuis / pydantic-to-typescript

CLI Tool for converting pydantic models into typescript definitions
MIT License
285 stars 48 forks source link

Repeat executions use cached module #7

Closed captaincoordinates closed 2 years ago

captaincoordinates commented 3 years ago

I'm calling generate_typescript_defs repeatedly within a Python script in response to file changes (via watchdog). The goal is to have any changes to my Pydantic models automatically propagated to their TypeScript counterparts, which will then cause a "watch" build (e.g. Angular's ng serve) to highlight all TypeScript references that require update.

Watchdog is correctly executing generate_typescript_defs in response to each Pydantic model change, but pydantic2ts appears to use a cache of the module acquired during its first run. No matter how I change the Pydantic model the generated TypeScript is always the same, until I kill the process and restart. Is there any way I can force pydantic2ts to reload the module each time generate_typescript_defs is called, without starting a new Python process each time?

My module references are in the dot-separated format, not file paths, and looking at the source for import_module I had hoped to simply call importlib.reload(module) prior to each call to generate_typescript_defs, but this did not make a difference. Any suggestions would be very much appreciated.

phillipdupuis commented 3 years ago

Ahh this is a really cool use case! I like it. I'll write some tests to try and figure out a clean way to optionally reload modules each time

That said, one solution that might work now would be to have generate_typescript_defs use a fresh interpreter each time. I know you said you'd like to avoid starting a new python process each time, but I'm assuming that was in reference to the watchdog process? (correct me if I'm wrong)

If you'd like to try that, I think calling this instead of generate_typescript_defs may work:

from multiprocessing import Process

def run_pydantic2ts(module, output):
    p = Process(target=generate_typescript_defs, args=(module, output))
    p.start()
    p.join()

Note: if it doesn't work, it may be because it's using 'fork' to start new processes rather than 'spawn'. It seems like that behavior is dependent on OS and python version. If that's the case, you can manually set the method to 'spawn': https://docs.python.org/3/library/multiprocessing.html#contexts-and-start-methods

captaincoordinates commented 3 years ago

Thanks for the feedback. In the end I used Popen to start a new process each time a watchdog event occurred. The approach works well and I've been happy with pydantic-to-typescript's functionality - much appreciated work.