Skip to content

Commit 23dd42e

Browse files
colinhacksfantix
andauthored
Add codegen docs (#380)
Also drop pydantic target: It is not really a code generation target. Instead, new options are added: --skip-pydantic-validation and --no-skip-pydantic-validation. Also added help text in codegen CLI. Co-authored-by: Fantix King <fantix.king@gmail.com>
1 parent 361221d commit 23dd42e

16 files changed

+145
-15
lines changed

docs/api/codegen.rst

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
.. _edgedb-python-codegen:
2+
3+
===============
4+
Code Generation
5+
===============
6+
7+
.. py:currentmodule:: edgedb
8+
9+
The ``edgedb-python`` package exposes a command-line tool to generate
10+
typesafe functions from ``*.edgeql`` files, using :py:mod:`dataclasses` for
11+
objects primarily.
12+
13+
.. code-block:: bash
14+
15+
$ edgedb-py
16+
17+
Or alternatively:
18+
19+
.. code-block:: bash
20+
21+
$ python -m edgedb.codegen
22+
23+
Consider a simple query that lives in a file called ``get_number.edgeql``:
24+
25+
.. code-block:: edgeql
26+
27+
select <int64>$arg;
28+
29+
Running the code generator will generate a new file called
30+
``get_number_async_edgeql.py`` containing the following code (roughly):
31+
32+
.. code-block:: python
33+
34+
from __future__ import annotations
35+
import edgedb
36+
37+
38+
async def get_number(
39+
client: edgedb.AsyncIOClient,
40+
*,
41+
arg: int,
42+
) -> int:
43+
return await client.query_single(
44+
"""\
45+
select <int64>$arg\
46+
""",
47+
arg=arg,
48+
)
49+
50+
Target
51+
~~~~~~
52+
53+
By default, the generated code uses an ``async`` API. The generator supports
54+
additional targets via the ``--target`` flag.
55+
56+
.. code-block:: bash
57+
58+
$ edgedb-py --target async # generate async function (default)
59+
$ edgedb-py --target blocking # generate blocking code
60+
61+
The names of the generated files will differ accordingly:
62+
``{query_filename}_{target}_edgeql.py``.
63+
64+
Single-file mode
65+
~~~~~~~~~~~~~~~~
66+
67+
It may be preferable to generate a single file containing all the generated
68+
functions. This can be done by passing the ``--file`` flag.
69+
70+
.. code-block:: bash
71+
72+
$ edgedb-py --file
73+
74+
This generates a single file called ``generated_{target}_edgeql.py`` in the
75+
root of your project.
76+
77+
Connection
78+
~~~~~~~~~~
79+
80+
The ``edgedb-py`` command supports the same set of :ref:`connection options
81+
<ref_cli_edgedb_connopts>` as the ``edgedb`` CLI.
82+
83+
.. code-block::
84+
85+
-I, --instance <instance>
86+
--dsn <dsn>
87+
--credentials-file <path/to/credentials.json>
88+
-H, --host <host>
89+
-P, --port <port>
90+
-d, --database <database>
91+
-u, --user <user>
92+
--password
93+
--password-from-stdin
94+
--tls-ca-file <path/to/certificate>
95+
--tls-security <insecure | no_host_verification | strict | default>
96+

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,5 @@ and :ref:`asyncio <edgedb-python-asyncio-api-reference>` implementations.
4646
api/asyncio_client
4747
api/blocking_client
4848
api/types
49+
api/codegen
4950
api/advanced

edgedb/codegen/cli.py

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919

2020
import argparse
21+
import sys
2122

2223
from . import generator
2324

@@ -39,15 +40,44 @@
3940
"--tls-security",
4041
choices=["default", "strict", "no_host_verification", "insecure"],
4142
)
42-
parser.add_argument("--file", action="store_true")
43+
parser.add_argument(
44+
"--file",
45+
action="store_true",
46+
help="Generate a single file instead of one per .edgeql file.",
47+
)
4348
parser.add_argument(
4449
"--target",
45-
choices=["blocking", "async", "pydantic"],
50+
choices=["blocking", "async"],
4651
nargs="*",
47-
default=["async", "pydantic"],
52+
default=["async"],
53+
help="Choose one or more targets to generate code (default is async)."
4854
)
55+
if sys.version_info[:2] >= (3, 9):
56+
parser.add_argument(
57+
"--skip-pydantic-validation",
58+
action=argparse.BooleanOptionalAction,
59+
default=argparse.SUPPRESS, # override the builtin help for default
60+
help="Add a mixin to generated dataclasses "
61+
"to skip Pydantic validation (default is to add the mixin).",
62+
)
63+
else:
64+
parser.add_argument(
65+
"--skip-pydantic-validation",
66+
action="store_true",
67+
default=True,
68+
)
69+
parser.add_argument(
70+
"--no-skip-pydantic-validation",
71+
dest="skip_pydantic_validation",
72+
action="store_false",
73+
default=False,
74+
help="Add a mixin to generated dataclasses "
75+
"to skip Pydantic validation (default is to add the mixin).",
76+
)
4977

5078

5179
def main():
5280
args = parser.parse_args()
81+
if not hasattr(args, "skip_pydantic_validation"):
82+
args.skip_pydantic_validation = True
5383
generator.Generator(args).run()

edgedb/codegen/generator.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ class Generator:
122122
def __init__(self, args: argparse.Namespace):
123123
self._default_module = "default"
124124
self._targets = args.target
125+
self._skip_pydantic_validation = args.skip_pydantic_validation
125126
self._async = False
126127
try:
127128
self._project_dir = pathlib.Path(
@@ -407,7 +408,7 @@ def _generate_code(
407408
buf = io.StringIO()
408409
self._imports.add("dataclasses")
409410
print("@dataclasses.dataclass", file=buf)
410-
if "pydantic" in self._targets:
411+
if self._skip_pydantic_validation:
411412
print(f"class {rv}(NoPydanticValidation):", file=buf)
412413
self._use_pydantic = True
413414
else:

tests/codegen/test-project1/generated_async_edgeql.py.assert

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# AUTOGENERATED FROM 'select_scalar.edgeql' WITH:
2-
# $ edgedb-py --target async --file
2+
# $ edgedb-py --target async --file --no-skip-pydantic-validation
33

44

55
from __future__ import annotations

tests/codegen/test-project1/select_scalar_edgeql.py.assert

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# AUTOGENERATED FROM 'select_scalar.edgeql' WITH:
2-
# $ edgedb-py --target blocking
2+
# $ edgedb-py --target blocking --no-skip-pydantic-validation
33

44

55
from __future__ import annotations

tests/codegen/test-project2/argnames/query_one_edgeql.py.assert

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# AUTOGENERATED FROM 'argnames/query_one.edgeql' WITH:
2-
# $ edgedb-py --target blocking
2+
# $ edgedb-py --target blocking --no-skip-pydantic-validation
33

44

55
from __future__ import annotations

tests/codegen/test-project2/generated_async_edgeql.py.assert

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
# 'scalar/select_scalar.edgeql'
99
# 'scalar/select_scalars.edgeql'
1010
# WITH:
11-
# $ edgedb-py --target async --file
11+
# $ edgedb-py --target async --file --no-skip-pydantic-validation
1212

1313

1414
from __future__ import annotations

tests/codegen/test-project2/object/link_prop_edgeql.py.assert

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# AUTOGENERATED FROM 'object/link_prop.edgeql' WITH:
2-
# $ edgedb-py --target blocking
2+
# $ edgedb-py --target blocking --no-skip-pydantic-validation
33

44

55
from __future__ import annotations

tests/codegen/test-project2/object/select_object_edgeql.py.assert

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# AUTOGENERATED FROM 'object/select_object.edgeql' WITH:
2-
# $ edgedb-py --target blocking
2+
# $ edgedb-py --target blocking --no-skip-pydantic-validation
33

44

55
from __future__ import annotations

0 commit comments

Comments
 (0)