Skip to content

Commit

Permalink
Add codegen docs (#380)
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
colinhacks and fantix authored Oct 20, 2022
1 parent 361221d commit 23dd42e
Show file tree
Hide file tree
Showing 16 changed files with 145 additions and 15 deletions.
96 changes: 96 additions & 0 deletions docs/api/codegen.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
.. _edgedb-python-codegen:

===============
Code Generation
===============

.. py:currentmodule:: edgedb
The ``edgedb-python`` package exposes a command-line tool to generate
typesafe functions from ``*.edgeql`` files, using :py:mod:`dataclasses` for
objects primarily.

.. code-block:: bash
$ edgedb-py
Or alternatively:

.. code-block:: bash
$ python -m edgedb.codegen
Consider a simple query that lives in a file called ``get_number.edgeql``:

.. code-block:: edgeql
select <int64>$arg;
Running the code generator will generate a new file called
``get_number_async_edgeql.py`` containing the following code (roughly):

.. code-block:: python
from __future__ import annotations
import edgedb
async def get_number(
client: edgedb.AsyncIOClient,
*,
arg: int,
) -> int:
return await client.query_single(
"""\
select <int64>$arg\
""",
arg=arg,
)
Target
~~~~~~

By default, the generated code uses an ``async`` API. The generator supports
additional targets via the ``--target`` flag.

.. code-block:: bash
$ edgedb-py --target async # generate async function (default)
$ edgedb-py --target blocking # generate blocking code
The names of the generated files will differ accordingly:
``{query_filename}_{target}_edgeql.py``.

Single-file mode
~~~~~~~~~~~~~~~~

It may be preferable to generate a single file containing all the generated
functions. This can be done by passing the ``--file`` flag.

.. code-block:: bash
$ edgedb-py --file
This generates a single file called ``generated_{target}_edgeql.py`` in the
root of your project.

Connection
~~~~~~~~~~

The ``edgedb-py`` command supports the same set of :ref:`connection options
<ref_cli_edgedb_connopts>` as the ``edgedb`` CLI.

.. code-block::
-I, --instance <instance>
--dsn <dsn>
--credentials-file <path/to/credentials.json>
-H, --host <host>
-P, --port <port>
-d, --database <database>
-u, --user <user>
--password
--password-from-stdin
--tls-ca-file <path/to/certificate>
--tls-security <insecure | no_host_verification | strict | default>
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,5 @@ and :ref:`asyncio <edgedb-python-asyncio-api-reference>` implementations.
api/asyncio_client
api/blocking_client
api/types
api/codegen
api/advanced
36 changes: 33 additions & 3 deletions edgedb/codegen/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@


import argparse
import sys

from . import generator

Expand All @@ -39,15 +40,44 @@
"--tls-security",
choices=["default", "strict", "no_host_verification", "insecure"],
)
parser.add_argument("--file", action="store_true")
parser.add_argument(
"--file",
action="store_true",
help="Generate a single file instead of one per .edgeql file.",
)
parser.add_argument(
"--target",
choices=["blocking", "async", "pydantic"],
choices=["blocking", "async"],
nargs="*",
default=["async", "pydantic"],
default=["async"],
help="Choose one or more targets to generate code (default is async)."
)
if sys.version_info[:2] >= (3, 9):
parser.add_argument(
"--skip-pydantic-validation",
action=argparse.BooleanOptionalAction,
default=argparse.SUPPRESS, # override the builtin help for default
help="Add a mixin to generated dataclasses "
"to skip Pydantic validation (default is to add the mixin).",
)
else:
parser.add_argument(
"--skip-pydantic-validation",
action="store_true",
default=True,
)
parser.add_argument(
"--no-skip-pydantic-validation",
dest="skip_pydantic_validation",
action="store_false",
default=False,
help="Add a mixin to generated dataclasses "
"to skip Pydantic validation (default is to add the mixin).",
)


def main():
args = parser.parse_args()
if not hasattr(args, "skip_pydantic_validation"):
args.skip_pydantic_validation = True
generator.Generator(args).run()
3 changes: 2 additions & 1 deletion edgedb/codegen/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ class Generator:
def __init__(self, args: argparse.Namespace):
self._default_module = "default"
self._targets = args.target
self._skip_pydantic_validation = args.skip_pydantic_validation
self._async = False
try:
self._project_dir = pathlib.Path(
Expand Down Expand Up @@ -407,7 +408,7 @@ def _generate_code(
buf = io.StringIO()
self._imports.add("dataclasses")
print("@dataclasses.dataclass", file=buf)
if "pydantic" in self._targets:
if self._skip_pydantic_validation:
print(f"class {rv}(NoPydanticValidation):", file=buf)
self._use_pydantic = True
else:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# AUTOGENERATED FROM 'select_scalar.edgeql' WITH:
# $ edgedb-py --target async --file
# $ edgedb-py --target async --file --no-skip-pydantic-validation


from __future__ import annotations
Expand Down
2 changes: 1 addition & 1 deletion tests/codegen/test-project1/select_scalar_edgeql.py.assert
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# AUTOGENERATED FROM 'select_scalar.edgeql' WITH:
# $ edgedb-py --target blocking
# $ edgedb-py --target blocking --no-skip-pydantic-validation


from __future__ import annotations
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# AUTOGENERATED FROM 'argnames/query_one.edgeql' WITH:
# $ edgedb-py --target blocking
# $ edgedb-py --target blocking --no-skip-pydantic-validation


from __future__ import annotations
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# 'scalar/select_scalar.edgeql'
# 'scalar/select_scalars.edgeql'
# WITH:
# $ edgedb-py --target async --file
# $ edgedb-py --target async --file --no-skip-pydantic-validation


from __future__ import annotations
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# AUTOGENERATED FROM 'object/link_prop.edgeql' WITH:
# $ edgedb-py --target blocking
# $ edgedb-py --target blocking --no-skip-pydantic-validation


from __future__ import annotations
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# AUTOGENERATED FROM 'object/select_object.edgeql' WITH:
# $ edgedb-py --target blocking
# $ edgedb-py --target blocking --no-skip-pydantic-validation


from __future__ import annotations
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# AUTOGENERATED FROM 'object/select_objects.edgeql' WITH:
# $ edgedb-py --target blocking
# $ edgedb-py --target blocking --no-skip-pydantic-validation


from __future__ import annotations
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# AUTOGENERATED FROM 'parpkg/select_args.edgeql' WITH:
# $ edgedb-py --target blocking
# $ edgedb-py --target blocking --no-skip-pydantic-validation


from __future__ import annotations
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# AUTOGENERATED FROM 'parpkg/subpkg/my_query.edgeql' WITH:
# $ edgedb-py --target blocking
# $ edgedb-py --target blocking --no-skip-pydantic-validation


from __future__ import annotations
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# AUTOGENERATED FROM 'scalar/select_scalar.edgeql' WITH:
# $ edgedb-py --target blocking
# $ edgedb-py --target blocking --no-skip-pydantic-validation


from __future__ import annotations
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# AUTOGENERATED FROM 'scalar/select_scalars.edgeql' WITH:
# $ edgedb-py --target blocking
# $ edgedb-py --target blocking --no-skip-pydantic-validation


from __future__ import annotations
Expand Down
2 changes: 2 additions & 0 deletions tests/test_codegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,15 @@ async def run(*args, extra_env=None):
"edgedb-py",
"--target",
"blocking",
"--no-skip-pydantic-validation",
extra_env={"EDGEDB_PYTHON_CODEGEN_PY_VER": "3.9.2"},
)
await run(
"edgedb-py",
"--target",
"async",
"--file",
"--no-skip-pydantic-validation",
extra_env={"EDGEDB_PYTHON_CODEGEN_PY_VER": "3.10.3"},
)

Expand Down

0 comments on commit 23dd42e

Please sign in to comment.