Skip to content

Commit 361221d

Browse files
fantixfmoor
andauthored
Codegen with the describe_query() API (#363)
* Change the lower bound of type annotations tag to 0x80, refs geldata/gel#1958 * Add `client._describe()` API * Add missing copyright header * Implement `_describe()` API on codecs * Implement codegen using `_describe()` API * Support `edgedb.Range` type (Pydantic support for edgedb.Range type is not included out of the box) * Support Python 3.9 and 3.10 typing simplification * Add header comments to generated files * Add e2e test for codegen (test case copied from edgedb-go) * Support typed link properties Co-authored-by: fmoor <fmoor@gmx.com>
1 parent 2c5dcc7 commit 361221d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+2683
-26
lines changed

edgedb/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from edgedb.datatypes.range import Range
2929

3030
from .abstract import (
31-
Executor, AsyncIOExecutor, ReadOnlyExecutor, AsyncIOReadOnlyExecutor
31+
Executor, AsyncIOExecutor, ReadOnlyExecutor, AsyncIOReadOnlyExecutor,
3232
)
3333

3434
from .asyncio_client import (
@@ -37,6 +37,7 @@
3737
)
3838

3939
from .blocking_client import create_client, Client
40+
from .enums import Cardinality, ElementKind
4041
from .options import RetryCondition, IsolationLevel, default_backoff
4142
from .options import RetryOptions, TransactionOptions
4243
from .options import State

edgedb/_testbase.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,3 +584,8 @@ def gen_lock_key():
584584
if os.environ.get('USE_UVLOOP'):
585585
import uvloop
586586
uvloop.install()
587+
elif sys.platform == 'win32' and sys.version_info[:2] == (3, 7):
588+
# The default policy on win32 of Python 3.7 is SelectorEventLoop, which
589+
# does not implement some subprocess functions required by the tests,
590+
# so we have to manually set it to the proactor one (default in 3.8).
591+
asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())

edgedb/abstract.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,29 @@
1+
#
2+
# This source file is part of the EdgeDB open source project.
3+
#
4+
# Copyright 2020-present MagicStack Inc. and the EdgeDB authors.
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
#
18+
19+
20+
from __future__ import annotations
121
import abc
22+
import dataclasses
223
import typing
324

25+
from . import describe
26+
from . import enums
427
from . import options
528
from .protocol import protocol
629

@@ -13,6 +36,8 @@
1336
"AsyncIOExecutor",
1437
"ReadOnlyExecutor",
1538
"AsyncIOReadOnlyExecutor",
39+
"DescribeContext",
40+
"DescribeResult",
1641
)
1742

1843

@@ -47,6 +72,21 @@ class ExecuteContext(typing.NamedTuple):
4772
state: typing.Optional[options.State]
4873

4974

75+
@dataclasses.dataclass
76+
class DescribeContext:
77+
query: str
78+
state: typing.Optional[options.State]
79+
inject_type_names: bool
80+
81+
82+
@dataclasses.dataclass
83+
class DescribeResult:
84+
input_type: typing.Optional[describe.AnyType]
85+
output_type: typing.Optional[describe.AnyType]
86+
output_cardinality: enums.Cardinality
87+
capabilities: enums.Capability
88+
89+
5090
_query_opts = QueryOptions(
5191
output_format=protocol.OutputFormat.BINARY,
5292
expect_one=False,

edgedb/asyncio_client.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,15 @@ async def __aenter__(self):
347347
async def __aexit__(self, exc_type, exc_val, exc_tb):
348348
await self.aclose()
349349

350+
async def _describe_query(
351+
self, query: str, *, inject_type_names: bool = False
352+
) -> abstract.DescribeResult:
353+
return await self._describe(abstract.DescribeContext(
354+
query=query,
355+
state=self._get_state(),
356+
inject_type_names=inject_type_names,
357+
))
358+
350359

351360
def create_async_client(
352361
dsn=None,

edgedb/base_client.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,25 @@ async def _execute(self, execute_context: abstract.ExecuteContext) -> None:
287287
),
288288
)
289289

290+
async def describe(
291+
self, describe_context: abstract.DescribeContext
292+
) -> abstract.DescribeResult:
293+
cardinality, in_dc, out_dc, capabilities = await self._protocol._parse(
294+
describe_context.query,
295+
reg=protocol.CodecsRegistry(),
296+
inline_typenames=describe_context.inject_type_names,
297+
state=(
298+
describe_context.state.as_dict()
299+
if describe_context.state else None
300+
),
301+
)
302+
return abstract.DescribeResult(
303+
input_type=in_dc.make_type(describe_context),
304+
output_type=out_dc.make_type(describe_context),
305+
output_cardinality=enums.Cardinality(cardinality[0]),
306+
capabilities=capabilities,
307+
)
308+
290309
def terminate(self):
291310
if not self.is_closed():
292311
try:
@@ -739,6 +758,15 @@ async def _execute(self, execute_context: abstract.ExecuteContext) -> None:
739758
finally:
740759
await self._impl.release(con)
741760

761+
async def _describe(
762+
self, describe_context: abstract.DescribeContext
763+
) -> abstract.DescribeResult:
764+
con = await self._impl.acquire()
765+
try:
766+
return await con.describe(describe_context)
767+
finally:
768+
await self._impl.release(con)
769+
742770
def terminate(self):
743771
"""Terminate all connections in the pool."""
744772
self._impl.terminate()

edgedb/blocking_client.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,15 @@ def __enter__(self):
327327
def __exit__(self, exc_type, exc_val, exc_tb):
328328
self.close()
329329

330+
def _describe_query(
331+
self, query: str, *, inject_type_names: bool = False
332+
) -> abstract.DescribeResult:
333+
return self._iter_coroutine(self._describe(abstract.DescribeContext(
334+
query=query,
335+
state=self._get_state(),
336+
inject_type_names=inject_type_names,
337+
)))
338+
330339

331340
def create_client(
332341
dsn=None,

edgedb/codegen/__init__.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#
2+
# This source file is part of the EdgeDB open source project.
3+
#
4+
# Copyright 2022-present MagicStack Inc. and the EdgeDB authors.
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
#

edgedb/codegen/__main__.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#
2+
# This source file is part of the EdgeDB open source project.
3+
#
4+
# Copyright 2022-present MagicStack Inc. and the EdgeDB authors.
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
#
18+
19+
20+
from .cli import main
21+
22+
if __name__ == "__main__":
23+
main()

edgedb/codegen/cli.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#
2+
# This source file is part of the EdgeDB open source project.
3+
#
4+
# Copyright 2022-present MagicStack Inc. and the EdgeDB authors.
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
#
18+
19+
20+
import argparse
21+
22+
from . import generator
23+
24+
25+
parser = argparse.ArgumentParser(
26+
description="Generate Python code for .edgeql files."
27+
)
28+
parser.add_argument("--dsn")
29+
parser.add_argument("--credentials_file", metavar="PATH")
30+
parser.add_argument("-I", "--instance", metavar="NAME")
31+
parser.add_argument("-H", "--host")
32+
parser.add_argument("-P", "--port")
33+
parser.add_argument("-d", "--database", metavar="NAME")
34+
parser.add_argument("-u", "--user")
35+
parser.add_argument("--password")
36+
parser.add_argument("--password-from-stdin", action="store_true")
37+
parser.add_argument("--tls-ca-file", metavar="PATH")
38+
parser.add_argument(
39+
"--tls-security",
40+
choices=["default", "strict", "no_host_verification", "insecure"],
41+
)
42+
parser.add_argument("--file", action="store_true")
43+
parser.add_argument(
44+
"--target",
45+
choices=["blocking", "async", "pydantic"],
46+
nargs="*",
47+
default=["async", "pydantic"],
48+
)
49+
50+
51+
def main():
52+
args = parser.parse_args()
53+
generator.Generator(args).run()

0 commit comments

Comments
 (0)