TypeDAL 4.7.2__tar.gz → 4.8.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {typedal-4.7.2 → typedal-4.8.1}/CHANGELOG.md +17 -0
- {typedal-4.7.2 → typedal-4.8.1}/PKG-INFO +19 -4
- {typedal-4.7.2 → typedal-4.8.1}/README.md +1 -0
- {typedal-4.7.2 → typedal-4.8.1}/docs/10_advanced_apis.md +53 -0
- {typedal-4.7.2 → typedal-4.8.1}/docs/6_migrations.md +12 -0
- {typedal-4.7.2 → typedal-4.8.1}/docs/7_configuration.md +2 -0
- {typedal-4.7.2 → typedal-4.8.1}/pyproject.toml +29 -5
- {typedal-4.7.2 → typedal-4.8.1}/src/typedal/__about__.py +1 -1
- {typedal-4.7.2 → typedal-4.8.1}/src/typedal/__init__.py +2 -1
- {typedal-4.7.2 → typedal-4.8.1}/src/typedal/cli.py +105 -5
- {typedal-4.7.2 → typedal-4.8.1}/src/typedal/config.py +3 -2
- {typedal-4.7.2 → typedal-4.8.1}/src/typedal/core.py +34 -1
- {typedal-4.7.2 → typedal-4.8.1}/src/typedal/define.py +3 -2
- {typedal-4.7.2 → typedal-4.8.1}/src/typedal/enum_helpers.py +6 -0
- {typedal-4.7.2 → typedal-4.8.1}/src/typedal/fields.py +2 -4
- {typedal-4.7.2 → typedal-4.8.1}/src/typedal/mixins.py +29 -90
- typedal-4.8.1/src/typedal/serializers/typescript.py +79 -0
- {typedal-4.7.2 → typedal-4.8.1}/src/typedal/tables.py +216 -5
- {typedal-4.7.2 → typedal-4.8.1}/tests/test_cli.py +42 -4
- {typedal-4.7.2 → typedal-4.8.1}/tests/test_config.py +27 -1
- {typedal-4.7.2 → typedal-4.8.1}/tests/test_main.py +6 -2
- {typedal-4.7.2 → typedal-4.8.1}/tests/test_mypy.py +14 -14
- {typedal-4.7.2 → typedal-4.8.1}/tests/test_query_builder.py +1 -4
- {typedal-4.7.2 → typedal-4.8.1}/tests/test_table.py +1 -1
- typedal-4.8.1/tests/test_typescript.py +190 -0
- typedal-4.7.2/.crush/.gitignore +0 -1
- typedal-4.7.2/.crush/init +0 -0
- typedal-4.7.2/.crush/logs/crush.log +0 -207
- {typedal-4.7.2 → typedal-4.8.1}/.github/workflows/su6.yml +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/.gitignore +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/.readthedocs.yml +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/coverage.svg +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/docs/1_getting_started.md +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/docs/2_defining_tables.md +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/docs/3_building_queries.md +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/docs/4_relationships.md +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/docs/5_py4web.md +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/docs/8_mixins.md +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/docs/9_memoization.md +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/docs/css/code_blocks.css +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/docs/index.md +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/docs/requirements.txt +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/example_new.py +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/example_old.py +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/mkdocs.yml +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/src/typedal/caching.py +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/src/typedal/constants.py +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/src/typedal/for_py4web.py +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/src/typedal/for_web2py.py +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/src/typedal/helpers.py +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/src/typedal/py.typed +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/src/typedal/query_builder.py +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/src/typedal/relationships.py +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/src/typedal/rows.py +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/src/typedal/serializers/as_json.py +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/src/typedal/types.py +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/src/typedal/web2py_py4web_shared.py +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/tasks.py +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/tests/__init__.py +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/tests/configs/simple.toml +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/tests/configs/valid.env +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/tests/configs/valid.toml +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/tests/py314_tests.py +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/tests/test_docs_examples.py +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/tests/test_helpers.py +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/tests/test_json.py +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/tests/test_mixins.py +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/tests/test_orm.py +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/tests/test_py4web.py +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/tests/test_relationships.py +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/tests/test_row.py +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/tests/test_stats.py +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/tests/test_web2py.py +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/tests/test_xx_others.py +0 -0
- {typedal-4.7.2 → typedal-4.8.1}/tests/timings.py +0 -0
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
<!--next-version-placeholder-->
|
|
4
4
|
|
|
5
|
+
## v4.8.1 (2026-05-10)
|
|
6
|
+
|
|
7
|
+
### Fix
|
|
8
|
+
|
|
9
|
+
* Extend from empty type-safe `DALProtocol` during type-checking to make some LSPs happier ([`d93a361`](https://github.com/trialandsuccess/TypeDAL/commit/d93a361e9caa6c9b94d6fbfa78644c8ff5e2bc23))
|
|
10
|
+
|
|
11
|
+
## v4.8.0 (2026-04-23)
|
|
12
|
+
|
|
13
|
+
### Feature
|
|
14
|
+
|
|
15
|
+
* TypeDAL 4.8: type... script? ([#9](https://github.com/trialandsuccess/TypeDAL/issues/9)) ([`9a688c0`](https://github.com/trialandsuccess/TypeDAL/commit/9a688c012ddcb77207b5879b7c66c6c2fee547c0))
|
|
16
|
+
|
|
17
|
+
### Fix
|
|
18
|
+
|
|
19
|
+
* **uuid:** Don't try to store NULL uuid as 'None' ([`29f8606`](https://github.com/trialandsuccess/TypeDAL/commit/29f8606972110b5472a42806c6c0e5b66f50095a))
|
|
20
|
+
* Ensure `InvalidEnumValue` is falsey ([`21fea08`](https://github.com/trialandsuccess/TypeDAL/commit/21fea086e3219d47615f7b9539caf4d5f5c48327))
|
|
21
|
+
|
|
5
22
|
## v4.7.2 (2026-04-20)
|
|
6
23
|
|
|
7
24
|
### Fix
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: TypeDAL
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.8.1
|
|
4
4
|
Summary: Typing support for PyDAL
|
|
5
5
|
Project-URL: Documentation, https://typedal.readthedocs.io/
|
|
6
6
|
Project-URL: Issues, https://github.com/trialandsuccess/TypeDAL/issues
|
|
@@ -15,7 +15,7 @@ Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
|
15
15
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
16
16
|
Requires-Python: >=3.12
|
|
17
17
|
Requires-Dist: configurable-json<2
|
|
18
|
-
Requires-Dist: configuraptor<3,>=2.0
|
|
18
|
+
Requires-Dist: configuraptor<3,>=2.1.0
|
|
19
19
|
Requires-Dist: dill<1
|
|
20
20
|
Requires-Dist: legacy-cgi; python_version >= '3.13'
|
|
21
21
|
Requires-Dist: pydal>=20251012.3
|
|
@@ -24,12 +24,19 @@ Requires-Dist: python-slugify<9
|
|
|
24
24
|
Provides-Extra: all
|
|
25
25
|
Requires-Dist: edwh-migrate[full]>=0.8.0; extra == 'all'
|
|
26
26
|
Requires-Dist: py4web; extra == 'all'
|
|
27
|
-
Requires-Dist: pydal2sql[all]>=1.
|
|
27
|
+
Requires-Dist: pydal2sql[all]>=1.3.5; extra == 'all'
|
|
28
28
|
Requires-Dist: pydantic<3; extra == 'all'
|
|
29
29
|
Requires-Dist: questionary; extra == 'all'
|
|
30
30
|
Requires-Dist: tabulate; extra == 'all'
|
|
31
31
|
Requires-Dist: tomlkit; extra == 'all'
|
|
32
32
|
Requires-Dist: typer<0.19,>=0.18; extra == 'all'
|
|
33
|
+
Requires-Dist: typtyp<1; extra == 'all'
|
|
34
|
+
Provides-Extra: cli
|
|
35
|
+
Requires-Dist: pydal2sql>=1.3.5; extra == 'cli'
|
|
36
|
+
Requires-Dist: questionary; extra == 'cli'
|
|
37
|
+
Requires-Dist: tabulate; extra == 'cli'
|
|
38
|
+
Requires-Dist: tomlkit; extra == 'cli'
|
|
39
|
+
Requires-Dist: typer<0.19,>=0.18; extra == 'cli'
|
|
33
40
|
Provides-Extra: dev
|
|
34
41
|
Requires-Dist: contextlib-chdir; extra == 'dev'
|
|
35
42
|
Requires-Dist: ewok; extra == 'dev'
|
|
@@ -47,13 +54,20 @@ Requires-Dist: types-requests; extra == 'dev'
|
|
|
47
54
|
Requires-Dist: types-tabulate; extra == 'dev'
|
|
48
55
|
Provides-Extra: migrations
|
|
49
56
|
Requires-Dist: edwh-migrate>=0.8.0; extra == 'migrations'
|
|
50
|
-
Requires-Dist: pydal2sql>=1.
|
|
57
|
+
Requires-Dist: pydal2sql>=1.3.5; extra == 'migrations'
|
|
51
58
|
Requires-Dist: questionary; extra == 'migrations'
|
|
52
59
|
Requires-Dist: tabulate; extra == 'migrations'
|
|
53
60
|
Requires-Dist: tomlkit; extra == 'migrations'
|
|
54
61
|
Requires-Dist: typer<0.19,>=0.18; extra == 'migrations'
|
|
55
62
|
Provides-Extra: py4web
|
|
56
63
|
Requires-Dist: py4web; extra == 'py4web'
|
|
64
|
+
Provides-Extra: typescript
|
|
65
|
+
Requires-Dist: pydal2sql>=1.3.5; extra == 'typescript'
|
|
66
|
+
Requires-Dist: questionary; extra == 'typescript'
|
|
67
|
+
Requires-Dist: tabulate; extra == 'typescript'
|
|
68
|
+
Requires-Dist: tomlkit; extra == 'typescript'
|
|
69
|
+
Requires-Dist: typer<0.19,>=0.18; extra == 'typescript'
|
|
70
|
+
Requires-Dist: typtyp<1; extra == 'typescript'
|
|
57
71
|
Description-Content-Type: text/markdown
|
|
58
72
|
|
|
59
73
|
# TypeDAL
|
|
@@ -149,6 +163,7 @@ typedal --help
|
|
|
149
163
|
- `migrations.fake`: Mark one or more migrations as completed in the database without executing the SQL code.
|
|
150
164
|
- `migrations.generate`: Run `pydal2sql` based on the TypeDAL configuration.
|
|
151
165
|
- `migrations.run`: Run `edwh-migrate` based on the TypeDAL configuration.
|
|
166
|
+
- `typescript.generate`: Generate TypeScript interfaces from TypeDAL models.
|
|
152
167
|
- `setup`: Interactively setup a `[tool.typedal]` entry in the local `pyproject.toml`.
|
|
153
168
|
|
|
154
169
|
### Configuration
|
|
@@ -91,6 +91,7 @@ typedal --help
|
|
|
91
91
|
- `migrations.fake`: Mark one or more migrations as completed in the database without executing the SQL code.
|
|
92
92
|
- `migrations.generate`: Run `pydal2sql` based on the TypeDAL configuration.
|
|
93
93
|
- `migrations.run`: Run `edwh-migrate` based on the TypeDAL configuration.
|
|
94
|
+
- `typescript.generate`: Generate TypeScript interfaces from TypeDAL models.
|
|
94
95
|
- `setup`: Interactively setup a `[tool.typedal]` entry in the local `pyproject.toml`.
|
|
95
96
|
|
|
96
97
|
### Configuration
|
|
@@ -74,3 +74,56 @@ MyTable.reorder_fields(MyTable.id, MyTable.name, keep_others=False)
|
|
|
74
74
|
```
|
|
75
75
|
|
|
76
76
|
This is useful when you want deterministic field order for SQL generation, inspection, or exports.
|
|
77
|
+
|
|
78
|
+
## TypeScript schema generation
|
|
79
|
+
|
|
80
|
+
TypeDAL can generate TypeScript types from your models.
|
|
81
|
+
|
|
82
|
+
Install the optional dependency first:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
uv pip install TypeDAL[typescript] # or typedal[all]
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### From Python APIs
|
|
89
|
+
|
|
90
|
+
Generate schema for one model:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
ts = User.as_typescript()
|
|
94
|
+
print(ts)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Generate schema for all currently defined models on a database instance:
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
ts = db.as_typescript()
|
|
101
|
+
print(ts)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Generate schema for a subset of models:
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
ts = db.as_typescript("User", "Post")
|
|
108
|
+
# or:
|
|
109
|
+
ts = db.as_typescript(User, Post)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### From the CLI
|
|
113
|
+
|
|
114
|
+
Generate TypeScript from your configured table definitions:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
typedal typescript.generate
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Useful variants:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
typedal typescript.generate path/to/models.py
|
|
124
|
+
typedal typescript.generate --tables User --tables Post
|
|
125
|
+
typedal typescript.generate --output-file src/types/typedal.ts
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Configuration details for `typescript.generate` (including `typescript_output`) are documented in
|
|
129
|
+
[7. Configuration](./7_configuration.md).
|
|
@@ -88,3 +88,15 @@ You can however overwrite the behavior as defined in the config. See all options
|
|
|
88
88
|
```bash
|
|
89
89
|
typedal migrations.run --help
|
|
90
90
|
```
|
|
91
|
+
|
|
92
|
+
## Related: TypeScript generation
|
|
93
|
+
|
|
94
|
+
If you also want TypeScript types from your TypeDAL models, use:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
typedal typescript.generate
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
This command is separate from migrations, but uses the same style of TypeDAL config and model discovery.
|
|
101
|
+
See [10. Advanced APIs](./10_advanced_apis.md#typescript-schema-generation) for usage,
|
|
102
|
+
and [7. Configuration](./7_configuration.md) for config options such as `typescript_output`.
|
|
@@ -50,6 +50,7 @@ fake_migrate = false
|
|
|
50
50
|
database_to_restore = "data/backup.sql"
|
|
51
51
|
tables = ["users", "posts"]
|
|
52
52
|
noop = false
|
|
53
|
+
typescript_output = "src/types/typedal.ts"
|
|
53
54
|
```
|
|
54
55
|
|
|
55
56
|
### Generating Migrations (pydal2sql)
|
|
@@ -62,6 +63,7 @@ noop = false
|
|
|
62
63
|
- **`function`**: Function name containing your `db.define()` calls (default: `"define_tables"`)
|
|
63
64
|
- **`tables`**: Specific tables to generate migrations for (optional; usually set via CLI instead)
|
|
64
65
|
- **`noop`**: Don't write to output (usually set via CLI)
|
|
66
|
+
- **`typescript_output`**: Output path used by `typedal typescript.generate` (optional)
|
|
65
67
|
|
|
66
68
|
### Running Migrations (edwh-migrate)
|
|
67
69
|
|
|
@@ -28,7 +28,7 @@ classifiers = [
|
|
|
28
28
|
dependencies = [
|
|
29
29
|
"pydal >= 20251012.3", # core
|
|
30
30
|
"dill < 1", # caching
|
|
31
|
-
"configuraptor >= 2.0
|
|
31
|
+
"configuraptor >= 2.1.0, < 3", # config
|
|
32
32
|
"Configurable-JSON < 2", # json dumping
|
|
33
33
|
"python-slugify < 9",
|
|
34
34
|
"legacy-cgi; python_version >= '3.13'",
|
|
@@ -40,24 +40,48 @@ py4web = [
|
|
|
40
40
|
"py4web",
|
|
41
41
|
]
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
cli = [
|
|
44
44
|
"typer >=0.18, <0.19",
|
|
45
45
|
"tabulate",
|
|
46
|
-
"
|
|
46
|
+
"questionary",
|
|
47
|
+
"tomlkit",
|
|
48
|
+
"pydal2sql>=1.3.5",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
migrations = [
|
|
52
|
+
"tabulate",
|
|
53
|
+
"pydal2sql>=1.3.5",
|
|
47
54
|
"edwh-migrate>=0.8.0",
|
|
55
|
+
# cli:
|
|
56
|
+
"typer >=0.18, <0.19",
|
|
57
|
+
"tabulate",
|
|
48
58
|
"questionary",
|
|
49
59
|
"tomlkit",
|
|
60
|
+
"pydal2sql>=1.3.5",
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
typescript = [
|
|
64
|
+
"typtyp < 1",
|
|
65
|
+
# cli:
|
|
66
|
+
"typer >=0.18, <0.19",
|
|
67
|
+
"tabulate",
|
|
68
|
+
"questionary",
|
|
69
|
+
"tomlkit",
|
|
70
|
+
"pydal2sql>=1.3.5",
|
|
50
71
|
]
|
|
51
72
|
|
|
52
73
|
all = [
|
|
53
74
|
"py4web",
|
|
54
|
-
"
|
|
75
|
+
"typtyp < 1",
|
|
55
76
|
"tabulate",
|
|
56
|
-
"pydal2sql[all]>=1.2.0",
|
|
57
77
|
"edwh-migrate[full]>=0.8.0",
|
|
58
78
|
"pydantic < 3",
|
|
79
|
+
# cli:
|
|
80
|
+
"typer >=0.18, <0.19",
|
|
81
|
+
"tabulate",
|
|
59
82
|
"questionary",
|
|
60
83
|
"tomlkit",
|
|
84
|
+
"pydal2sql[all]>=1.3.5",
|
|
61
85
|
]
|
|
62
86
|
|
|
63
87
|
dev = [
|
|
@@ -6,7 +6,7 @@ from .core import TypeDAL
|
|
|
6
6
|
from .fields import TypedField
|
|
7
7
|
from .helpers import sql_expression
|
|
8
8
|
from .query_builder import QueryBuilder
|
|
9
|
-
from .relationships import Relationship, relationship
|
|
9
|
+
from .relationships import Ref, Relationship, relationship
|
|
10
10
|
from .rows import PaginatedRows, TypedRows
|
|
11
11
|
from .tables import TypedTable
|
|
12
12
|
|
|
@@ -20,6 +20,7 @@ except ImportError: # pragma: no cover
|
|
|
20
20
|
__all__ = [
|
|
21
21
|
"PaginatedRows",
|
|
22
22
|
"QueryBuilder",
|
|
23
|
+
"Ref",
|
|
23
24
|
"Relationship",
|
|
24
25
|
"TypeDAL",
|
|
25
26
|
"TypedField",
|
|
@@ -17,7 +17,6 @@ from .helpers import match_strings
|
|
|
17
17
|
from .types import AnyDict
|
|
18
18
|
|
|
19
19
|
try:
|
|
20
|
-
import edwh_migrate
|
|
21
20
|
import pydal2sql # noqa: F401
|
|
22
21
|
import questionary
|
|
23
22
|
import rich
|
|
@@ -27,12 +26,16 @@ try:
|
|
|
27
26
|
except ImportError as e:
|
|
28
27
|
# ImportWarning is hidden by default
|
|
29
28
|
warnings.warn(
|
|
30
|
-
"`
|
|
29
|
+
"`cli` extra not installed. Please run `pip install typedal[cli]` "
|
|
30
|
+
"(or `typedal[migrations]`, `typedal[typescript]`, `typedal[all]`) "
|
|
31
|
+
"to fix this.",
|
|
31
32
|
source=e,
|
|
32
33
|
category=RuntimeWarning,
|
|
33
34
|
)
|
|
34
35
|
exit(127) # command not found
|
|
35
36
|
|
|
37
|
+
from typing import Never
|
|
38
|
+
|
|
36
39
|
from pydal2sql.typer_support import IS_DEBUG, with_exit_code
|
|
37
40
|
from pydal2sql.types import (
|
|
38
41
|
DBType_Option,
|
|
@@ -41,7 +44,11 @@ from pydal2sql.types import (
|
|
|
41
44
|
Tables_Option,
|
|
42
45
|
)
|
|
43
46
|
from pydal2sql_core import core_alter, core_create, core_stub
|
|
44
|
-
from
|
|
47
|
+
from pydal2sql_core.cli_support import (
|
|
48
|
+
RenderContext,
|
|
49
|
+
find_file_contents,
|
|
50
|
+
render_schema_from_code,
|
|
51
|
+
)
|
|
45
52
|
|
|
46
53
|
from . import caching
|
|
47
54
|
from .__about__ import __version__
|
|
@@ -86,6 +93,11 @@ questionary_types: dict[typing.Hashable, Optional[AnyDict]] = {
|
|
|
86
93
|
"message": "Python file where migrations will be written to.",
|
|
87
94
|
"file_filter": lambda file: "." not in file or file.endswith(".py"),
|
|
88
95
|
},
|
|
96
|
+
"typescript_output": {
|
|
97
|
+
"type": "path",
|
|
98
|
+
"message": "File where generated TypeScript will be written to.",
|
|
99
|
+
"file_filter": lambda file: "." not in file or file.endswith(".ts"),
|
|
100
|
+
},
|
|
89
101
|
# disabled props:
|
|
90
102
|
"pyproject": None, # internal
|
|
91
103
|
"noop": None, # only for debugging
|
|
@@ -315,6 +327,16 @@ def run_migrations(
|
|
|
315
327
|
"""
|
|
316
328
|
Run edwh-migrate based on the typedal config.
|
|
317
329
|
"""
|
|
330
|
+
try:
|
|
331
|
+
import edwh_migrate
|
|
332
|
+
except ImportError:
|
|
333
|
+
rich.print(
|
|
334
|
+
r"[red]missing dependency![/red] "
|
|
335
|
+
r"Please install [blue]`typedal\[migrations]`[/blue] "
|
|
336
|
+
r"to use [blue]`typedal migrations.*`[/blue]."
|
|
337
|
+
)
|
|
338
|
+
raise typer.Exit(code=127)
|
|
339
|
+
|
|
318
340
|
# 1. build migrate Config from TypeDAL config
|
|
319
341
|
# 2. import right file
|
|
320
342
|
# 3. `activate_migrations`
|
|
@@ -360,6 +382,16 @@ def fake_migrations(
|
|
|
360
382
|
|
|
361
383
|
glob is supported in 'names'
|
|
362
384
|
"""
|
|
385
|
+
try:
|
|
386
|
+
import edwh_migrate
|
|
387
|
+
except ImportError:
|
|
388
|
+
rich.print(
|
|
389
|
+
r"[red]missing dependency![/red] "
|
|
390
|
+
r"Please install [blue]`typedal\[migrations]`[/blue] "
|
|
391
|
+
r"to use [blue]`typedal migrations.*`[/blue]."
|
|
392
|
+
)
|
|
393
|
+
raise typer.Exit(code=127)
|
|
394
|
+
|
|
363
395
|
if not (names or all):
|
|
364
396
|
rich.print("Please provide one or more migration names, or pass --all to fake all.")
|
|
365
397
|
return 1
|
|
@@ -385,13 +417,16 @@ def fake_migrations(
|
|
|
385
417
|
db = edwh_migrate.setup_db(config=migrate_config)
|
|
386
418
|
except edwh_migrate.migrate.DatabaseNotYetInitialized:
|
|
387
419
|
db = edwh_migrate.setup_db(
|
|
388
|
-
config=migrate_config,
|
|
420
|
+
config=migrate_config,
|
|
421
|
+
migrate=True,
|
|
422
|
+
migrate_enabled=True,
|
|
423
|
+
remove_migrate_tablefile=True,
|
|
389
424
|
)
|
|
390
425
|
|
|
391
426
|
previously_migrated = (
|
|
392
427
|
db(
|
|
393
428
|
db.ewh_implemented_features.name.belongs(to_fake)
|
|
394
|
-
& (db.ewh_implemented_features.installed == True) # noqa E712
|
|
429
|
+
& (db.ewh_implemented_features.installed == True), # noqa E712
|
|
395
430
|
)
|
|
396
431
|
.select(db.ewh_implemented_features.name)
|
|
397
432
|
.column("name")
|
|
@@ -447,6 +482,71 @@ def migrations_stub(
|
|
|
447
482
|
return 0
|
|
448
483
|
|
|
449
484
|
|
|
485
|
+
@app.command(name="typescript.generate")
|
|
486
|
+
@with_exit_code(hide_tb=IS_DEBUG)
|
|
487
|
+
def generate_typescript(
|
|
488
|
+
connection: typing.Annotated[str, typer.Option("--connection", "-c")] = None,
|
|
489
|
+
filename: OptionalArgument[str] = None,
|
|
490
|
+
tables: Tables_Option = None,
|
|
491
|
+
magic: Optional[bool] = None,
|
|
492
|
+
function: Optional[str] = None,
|
|
493
|
+
output_file: Optional[str] = None,
|
|
494
|
+
) -> bool:
|
|
495
|
+
"""
|
|
496
|
+
Generate TypeScript interfaces from TypeDAL table definitions.
|
|
497
|
+
"""
|
|
498
|
+
from .serializers.typescript import is_supported
|
|
499
|
+
|
|
500
|
+
if not is_supported(): # pragma: no cover
|
|
501
|
+
rich.print(
|
|
502
|
+
r"[red]missing dependency![/red] "
|
|
503
|
+
r"Please install [blue]`typedal\[typescript]`[/blue] "
|
|
504
|
+
r"to use [blue]`typedal typescript.generate`[/blue]."
|
|
505
|
+
)
|
|
506
|
+
raise typer.Exit(code=127)
|
|
507
|
+
|
|
508
|
+
config = load_config(connection)
|
|
509
|
+
config.update(
|
|
510
|
+
input=filename,
|
|
511
|
+
tables=tables,
|
|
512
|
+
magic=magic,
|
|
513
|
+
function=function,
|
|
514
|
+
typescript_output=output_file,
|
|
515
|
+
_skip_none=True,
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
def typescript_renderer(context: RenderContext) -> str:
|
|
519
|
+
db: TypeDAL = context.db_new
|
|
520
|
+
return db.as_typescript(*context.tables)
|
|
521
|
+
|
|
522
|
+
mut_functions: list[str] = []
|
|
523
|
+
source_input = config.input
|
|
524
|
+
source_code = find_file_contents(
|
|
525
|
+
source_input,
|
|
526
|
+
config.function,
|
|
527
|
+
found_functions=mut_functions,
|
|
528
|
+
default_version="current" if source_input else "stdin",
|
|
529
|
+
)
|
|
530
|
+
|
|
531
|
+
success = render_schema_from_code(
|
|
532
|
+
source_code,
|
|
533
|
+
# output_file=raw_output,
|
|
534
|
+
output_file=config.typescript_output or None,
|
|
535
|
+
renderer=typescript_renderer,
|
|
536
|
+
db_type=config.dialect,
|
|
537
|
+
tables=config.tables,
|
|
538
|
+
magic=config.magic,
|
|
539
|
+
function_name=tuple(mut_functions),
|
|
540
|
+
use_typedal=True,
|
|
541
|
+
write_mode="w",
|
|
542
|
+
)
|
|
543
|
+
|
|
544
|
+
if not success: # pragma: no cover
|
|
545
|
+
rich.print("[red] something went wrong [/red]")
|
|
546
|
+
|
|
547
|
+
return success
|
|
548
|
+
|
|
549
|
+
|
|
450
550
|
type AnyNestedDict = dict[str, AnyDict]
|
|
451
551
|
|
|
452
552
|
|
|
@@ -16,7 +16,7 @@ from .types import AnyDict
|
|
|
16
16
|
|
|
17
17
|
if t.TYPE_CHECKING:
|
|
18
18
|
from edwh_migrate import Config as MigrateConfig
|
|
19
|
-
from
|
|
19
|
+
from pydal2sql_core.state import Config as P2SConfig
|
|
20
20
|
|
|
21
21
|
LazyPolicy = t.Literal["forbid", "warn", "ignore", "tolerate", "allow"]
|
|
22
22
|
|
|
@@ -39,6 +39,7 @@ class TypeDALConfig(TypedConfig):
|
|
|
39
39
|
# pydal2sql:
|
|
40
40
|
input: str = ""
|
|
41
41
|
output: str = ""
|
|
42
|
+
typescript_output: str = ""
|
|
42
43
|
noop: bool = False
|
|
43
44
|
magic: bool = True
|
|
44
45
|
tables: t.Optional[list[str]] = None
|
|
@@ -70,7 +71,7 @@ class TypeDALConfig(TypedConfig):
|
|
|
70
71
|
"""
|
|
71
72
|
Convert the config to the format required by pydal2sql.
|
|
72
73
|
"""
|
|
73
|
-
from
|
|
74
|
+
from pydal2sql_core.state import Config, get_pydal2sql_config
|
|
74
75
|
|
|
75
76
|
if self.pyproject: # pragma: no cover
|
|
76
77
|
project = Path(self.pyproject).read_text()
|
|
@@ -22,6 +22,7 @@ from .helpers import (
|
|
|
22
22
|
sql_expression,
|
|
23
23
|
to_snake,
|
|
24
24
|
)
|
|
25
|
+
from .serializers.typescript import TypedDictRegistry
|
|
25
26
|
|
|
26
27
|
# noinspection PyUnusedImports
|
|
27
28
|
from .types import CacheStatus, Field, Template
|
|
@@ -143,7 +144,13 @@ def resolve_annotation(ftype: str, namespace: dict[str, type] | None = None) ->
|
|
|
143
144
|
return resolve_annotation_314(ftype, namespace=namespace)
|
|
144
145
|
|
|
145
146
|
|
|
146
|
-
class
|
|
147
|
+
class DALProtocol(t.Protocol): ...
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
BaseDAL = DALProtocol if t.TYPE_CHECKING else pydal.DAL
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class TypeDAL(BaseDAL):
|
|
147
154
|
"""
|
|
148
155
|
Drop-in replacement for pyDAL with layer to convert class-based table definitions to classical pydal define_tables.
|
|
149
156
|
"""
|
|
@@ -499,6 +506,32 @@ class TypeDAL(pydal.DAL):
|
|
|
499
506
|
"""
|
|
500
507
|
return memoize(self, func, *args, key=key, ttl=ttl, **kwargs)
|
|
501
508
|
|
|
509
|
+
def as_typescript(self, *tables: str | type[TypedTable]) -> str:
|
|
510
|
+
"""
|
|
511
|
+
Generate a TypeScript schema string for all currently defined typedal models.
|
|
512
|
+
"""
|
|
513
|
+
TypedDictRegistry.clear() # clean registry in order to apply 'tables' filtering
|
|
514
|
+
registry = TypedDictRegistry()
|
|
515
|
+
|
|
516
|
+
do_filter: t.Callable[[str], bool]
|
|
517
|
+
if tables:
|
|
518
|
+
names = {
|
|
519
|
+
model.__name__
|
|
520
|
+
for table in tables
|
|
521
|
+
if (model := (self.find_model(table) if isinstance(table, str) else table))
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
do_filter = lambda name: name in names # noqa: E731
|
|
525
|
+
else:
|
|
526
|
+
do_filter = lambda name: not name.startswith("_") # noqa: E731
|
|
527
|
+
|
|
528
|
+
# Ensure all currently defined models are registered into the TypedDict registry/world.
|
|
529
|
+
for model in self._class_map.values():
|
|
530
|
+
if do_filter(model.__name__):
|
|
531
|
+
model.as_typeddict()
|
|
532
|
+
|
|
533
|
+
return registry.get_typescript("as_typescript")
|
|
534
|
+
|
|
502
535
|
|
|
503
536
|
TypeDAL.representers.setdefault("rows_render", default_representer)
|
|
504
537
|
|
|
@@ -118,8 +118,9 @@ class TableDefinitionBuilder:
|
|
|
118
118
|
table=table,
|
|
119
119
|
relationships=t.cast(dict[str, Relationship[t.Any]], relationships),
|
|
120
120
|
)
|
|
121
|
-
self.class_map[str(table)] = cls
|
|
122
|
-
self.class_map[
|
|
121
|
+
self.class_map[str(table)] = cls # tablename - pydal name
|
|
122
|
+
self.class_map[cls.__name__] = cls # TableName - typedal name
|
|
123
|
+
self.class_map[table._rname] = cls # table_model - sql name
|
|
123
124
|
cls.__on_define__(self.db)
|
|
124
125
|
else:
|
|
125
126
|
warnings.warn("db.define used without inheriting TypedTable. This could lead to strange problems!")
|
|
@@ -11,6 +11,12 @@ class InvalidEnumValue:
|
|
|
11
11
|
raw: t.Any
|
|
12
12
|
value: None = None
|
|
13
13
|
|
|
14
|
+
def __bool__(self) -> bool:
|
|
15
|
+
"""
|
|
16
|
+
Make this fake type falsey.
|
|
17
|
+
"""
|
|
18
|
+
return False
|
|
19
|
+
|
|
14
20
|
|
|
15
21
|
def enum_value_type(enum_type: type[enum.Enum]) -> type[t.Any]:
|
|
16
22
|
values = [member.value for member in enum_type]
|
|
@@ -373,9 +373,7 @@ def UploadField(**kw: t.Unpack[FieldSettings]) -> TypedField[str]:
|
|
|
373
373
|
Upload = UploadField
|
|
374
374
|
|
|
375
375
|
|
|
376
|
-
def ReferenceField[
|
|
377
|
-
T_subclass: (TypedTable, Table)
|
|
378
|
-
](
|
|
376
|
+
def ReferenceField[T_subclass: (TypedTable, Table)](
|
|
379
377
|
other_table: str | t.Type[TypedTable] | TypedTable | Table | T_subclass,
|
|
380
378
|
**kw: t.Unpack[FieldSettings],
|
|
381
379
|
) -> TypedField[int]:
|
|
@@ -559,7 +557,7 @@ def PointField(**kw: t.Unpack[FieldSettings]) -> TypedField[tuple[float, float]]
|
|
|
559
557
|
NativeUUIDField = SQLCustomType(
|
|
560
558
|
type="string",
|
|
561
559
|
native="uuid",
|
|
562
|
-
encoder=str,
|
|
560
|
+
encoder=lambda val: "" if val is None else str(val),
|
|
563
561
|
decoder=lambda value: uuid.UUID(value) if value else None,
|
|
564
562
|
)
|
|
565
563
|
|