Skip to content

Commit bbf73dd

Browse files
committed
Added recursive searching of submodules and updated the readme
1 parent 4ca8355 commit bbf73dd

File tree

3 files changed

+56
-14
lines changed

3 files changed

+56
-14
lines changed

README.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,23 @@
1-
# pydantic-to-typescript
1+
# pydantic-to-typescript
2+
3+
A simple CLI tool for converting pydantic models into typescript interfaces. Useful for any scenario in which python and javascript applications are interacting, since it allows you to have a single source of truth for type definitions.
4+
5+
This tool requires that you have the lovely json2ts CLI utility installed. Instructions can be found here: https://www.npmjs.com/package/json-schema-to-typescript
6+
7+
### Installation
8+
```bash
9+
$ pip install pydantic-to-typescript
10+
```
11+
12+
### Command-line usage
13+
14+
|Prop |Description|
15+
|:--------|:-----------|
16+
|`--module` |name of the python module you would like to convert. All the pydantic models within it will be converted to typescript interfaces. Discoverable submodules will also be checked. Ex: 'pydantic2ts.examples.pydantic_models'|
17+
|`--output` |name of the file the typescript definitions should be written to. Ex: '/frontend/api-types.ts'|
18+
|`--json2ts-cmd` |optional, the command used to invoke json2ts. The default is 'json2ts'. Specify this if you have it installed in a strange location and need to provide the exact path (ex: /myproject/node_modules/bin/json2ts)|
19+
20+
Example:
21+
```bash
22+
$ pydantic2ts --module pydantic2ts.examples.pydantic_models --output output.ts
23+
```

pydantic2ts/cli/script.py

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
import os
55
import shutil
66
from pydantic import BaseModel, create_model
7-
from typing import Type, Dict, Any
7+
from tempfile import mkdtemp
8+
from typing import Type, Dict, Any, List
89
from types import ModuleType
910

1011

@@ -18,18 +19,34 @@ def remove_property_titles(schema: Dict[str, Any], model: Type[BaseModel]) -> No
1819
prop.pop('title', None)
1920

2021

21-
def extract_pydantic_models(module: ModuleType):
22+
def not_private(obj) -> bool:
23+
"""Return true if an object does not seem to be private"""
24+
return not getattr(obj, '__name__', '').startswith('_')
25+
26+
27+
def is_submodule(obj, module_name: str) -> bool:
28+
"""Return true if an object is a submodule"""
29+
return not_private(obj) and inspect.ismodule(obj) and getattr(obj, '__name__', '').startswith(f'{module_name}.')
30+
31+
32+
def is_pydantic_model(obj) -> bool:
33+
"""Return true if an object is a subclass of pydantic's BaseModel"""
34+
return not_private(obj) and inspect.isclass(obj) and issubclass(obj, BaseModel) and obj != BaseModel
35+
36+
37+
def extract_pydantic_models(module: ModuleType) -> List[Type[BaseModel]]:
2238
"""
2339
Given a module, return a list of the pydantic models contained within it.
2440
"""
2541
models = []
26-
for name in dir(module):
27-
obj = getattr(module, name)
28-
if (not name.startswith('_'))\
29-
and inspect.isclass(obj)\
30-
and issubclass(obj, BaseModel)\
31-
and obj != BaseModel:
32-
models.append(obj)
42+
module_name = module.__name__
43+
44+
for _, model in inspect.getmembers(module, is_pydantic_model):
45+
models.append(model)
46+
47+
for _, submodule in inspect.getmembers(module, lambda obj: is_submodule(obj, module_name)):
48+
models.extend(extract_pydantic_models(submodule))
49+
3350
return models
3451

3552

@@ -83,7 +100,10 @@ def main(
83100
master_model = create_model('_Master_', **{m.__name__: (m, ...) for m in models})
84101
master_model.Config.schema_extra = staticmethod(remove_property_titles)
85102

86-
with open('schema.json', 'w') as f:
103+
schema_dir = mkdtemp()
104+
schema_file_path = os.path.join(schema_dir, 'schema.json')
105+
106+
with open(schema_file_path, 'w') as f:
87107
f.write(master_model.schema_json(indent=2))
88108

89109
banner_comment = '\n'.join([
@@ -94,8 +114,8 @@ def main(
94114
'*/',
95115
])
96116

97-
os.system(f'{json2ts_cmd} -i schema.json -o {output} --bannerComment "{banner_comment}"')
98-
os.remove('schema.json')
117+
os.system(f'{json2ts_cmd} -i {schema_file_path} -o {output} --bannerComment "{banner_comment}"')
118+
shutil.rmtree(schema_dir)
99119
remove_master_model_from_output(output)
100120

101121

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def readme():
2020

2121
setup(
2222
name='pydantic-to-typescript',
23-
version='1.0.1',
23+
version='1.0.2',
2424
description='Convert pydantic models to typescript interfaces',
2525
license='MIT',
2626
long_description=readme(),

0 commit comments

Comments
 (0)