Skip to content

Commit cc24c5d

Browse files
authored
Feature/support pydantic2 (#23)
* Support pydantic v2 * Add templates
1 parent 3b2c9fe commit cc24c5d

File tree

9 files changed

+193
-105
lines changed

9 files changed

+193
-105
lines changed

graphql_query/templates.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# =======================================================================
2+
#
3+
# Copyright (c) 2020-2023 Northern Capital Gateway, LLC. All rights reserved.
4+
#
5+
# This software is the confidential and proprietary information of
6+
# Northern Capital Gateway, LLC.
7+
# You shall not disclose such Confidential Information and shall use it only in
8+
# accordance with the terms of the license agreement you entered into with
9+
# Northern Capital Gateway, LLC
10+
#
11+
# =======================================================================
12+
13+
import os
14+
from pathlib import Path
15+
16+
from jinja2 import Environment, FileSystemLoader, Template
17+
18+
__all__ = [
19+
"_template_key_value",
20+
"_template_key_values",
21+
"_template_key_argument",
22+
"_template_key_variable",
23+
"_template_key_arguments",
24+
"_template_key_objects",
25+
"_template_directive",
26+
"_template_variable",
27+
"_template_operation",
28+
"_template_query",
29+
"_template_fragment",
30+
"_template_inline_fragment",
31+
"_template_field",
32+
]
33+
34+
35+
# templates setting for render of classes
36+
TEMPLATES_FOLDER = Path(os.path.join(os.path.dirname(__file__), "templates/"))
37+
38+
template_env = Environment(loader=FileSystemLoader(searchpath=TEMPLATES_FOLDER))
39+
40+
41+
_template_key_value: Template = template_env.get_template("argument_key_value.jinja2")
42+
_template_key_values: Template = template_env.get_template("argument_key_values.jinja2")
43+
_template_key_argument: Template = template_env.get_template("argument_key_argument.jinja2")
44+
_template_key_variable: Template = template_env.get_template("argument_key_variable.jinja2")
45+
_template_key_arguments: Template = template_env.get_template("argument_key_arguments.jinja2")
46+
_template_key_objects: Template = template_env.get_template("argument_key_objects.jinja2")
47+
_template_directive: Template = template_env.get_template("directive.jinja2")
48+
_template_variable: Template = template_env.get_template("variable.jinja2")
49+
_template_operation: Template = template_env.get_template("operation.jinja2")
50+
_template_query: Template = template_env.get_template("query.jinja2")
51+
_template_fragment: Template = template_env.get_template("fragment.jinja2")
52+
_template_inline_fragment: Template = template_env.get_template("inline_fragment.jinja2")
53+
_template_field: Template = template_env.get_template("field.jinja2")

graphql_query/types.py

Lines changed: 69 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,25 @@
1-
import os
21
import sys
3-
from pathlib import Path
42
from typing import Any, List, Optional, Union
53

6-
from jinja2 import Environment, FileSystemLoader, Template
7-
from pydantic import BaseModel
4+
from pydantic import BaseModel as PydanticBaseModel
85
from pydantic import Field as PydanticField
96

7+
from .templates import (
8+
_template_key_value,
9+
_template_key_values,
10+
_template_key_argument,
11+
_template_key_variable,
12+
_template_key_arguments,
13+
_template_key_objects,
14+
_template_directive,
15+
_template_variable,
16+
_template_operation,
17+
_template_query,
18+
_template_fragment,
19+
_template_inline_fragment,
20+
_template_field,
21+
)
22+
1023
if sys.version_info >= (3, 10):
1124
from typing import TypeGuard
1225
else:
@@ -25,21 +38,12 @@
2538
]
2639

2740

28-
# templates setting for render of classes
29-
TEMPLATES_FOLDER = Path(os.path.join(os.path.dirname(__file__), "templates/"))
30-
31-
template_env = Environment(loader=FileSystemLoader(searchpath=TEMPLATES_FOLDER))
32-
33-
34-
class _GraphQL2PythonQuery(BaseModel):
41+
class _GraphQL2PythonQuery(PydanticBaseModel):
3542
"""An abstract class for GraphQL query type."""
3643

3744
class Config:
38-
# pylint: disable=too-few-public-methods
39-
smart_union = True
4045
extra = "forbid"
4146
arbitrary_types_allowed = True
42-
allow_reuse = True
4347

4448
@staticmethod
4549
def _line_shift(text: str) -> str:
@@ -93,14 +97,12 @@ class Variable(_GraphQL2PythonQuery):
9397
type: str
9498
default: Optional[str] = PydanticField(default=None)
9599

96-
_template: Template = template_env.get_template("variable.jinja2")
97-
98100
def render(self) -> str:
99-
return self._template.render(name=self.name, type=self.type, default=self.default)
101+
return _template_variable.render(name=self.name, type=self.type, default=self.default)
100102

101103

102104
class Argument(_GraphQL2PythonQuery):
103-
"""GraphQL argument type.
105+
"""GraphQL Argument type.
104106
105107
See https://graphql.org/learn/queries/#arguments for more details.
106108
@@ -164,25 +166,28 @@ class Argument(_GraphQL2PythonQuery):
164166
value: Union[
165167
str,
166168
int,
169+
bool,
167170
'Argument',
168171
Variable,
169172
List[str],
170173
List[int],
174+
List[bool],
171175
List['Argument'],
172176
List[List['Argument']],
173177
]
174178

175-
_template_key_value: Template = template_env.get_template("argument_key_value.jinja2")
176-
_template_key_values: Template = template_env.get_template("argument_key_values.jinja2")
177-
_template_key_argument: Template = template_env.get_template("argument_key_argument.jinja2")
178-
_template_key_variable: Template = template_env.get_template("argument_key_variable.jinja2")
179-
_template_key_arguments: Template = template_env.get_template("argument_key_arguments.jinja2")
180-
_template_key_objects: Template = template_env.get_template("argument_key_objects.jinja2")
181-
182179
@staticmethod
183180
def _check_is_list_of_str(values: List[Any]) -> TypeGuard[List[str]]:
184181
return all(isinstance(value, str) for value in values)
185182

183+
@staticmethod
184+
def _check_is_list_of_int(values: List[Any]) -> TypeGuard[List[int]]:
185+
return all(isinstance(value, int) for value in values)
186+
187+
@staticmethod
188+
def _check_is_list_of_bool(values: List[Any]) -> TypeGuard[List[bool]]:
189+
return all(isinstance(value, bool) for value in values)
190+
186191
@staticmethod
187192
def _check_is_list_of_arguments(values: List[Any]) -> TypeGuard[List['Argument']]:
188193
return all(isinstance(value, Argument) for value in values)
@@ -191,38 +196,49 @@ def _check_is_list_of_arguments(values: List[Any]) -> TypeGuard[List['Argument']
191196
def _check_is_list_of_list(values: List[Any]) -> TypeGuard[List[List[Any]]]:
192197
return all(isinstance(value, list) for value in values)
193198

194-
def _render_for_str(self, name: str, value: str) -> str:
195-
return self._template_key_value.render(name=name, value=value)
199+
@staticmethod
200+
def _render_for_str(name: str, value: str) -> str:
201+
return _template_key_value.render(name=name, value=value)
196202

197-
def _render_for_int(self, name: str, value: int) -> str:
198-
return self._template_key_value.render(name=name, value=str(value))
203+
@staticmethod
204+
def _render_for_int(name: str, value: int) -> str:
205+
return _template_key_value.render(name=name, value=str(value))
199206

200-
def _render_for_argument(self, name: str, value: 'Argument') -> str:
201-
return self._template_key_argument.render(name=name, argument=self._line_shift(value.render()))
207+
@staticmethod
208+
def _render_for_list_str(name: str, value: List[str]) -> str:
209+
return _template_key_values.render(name=name, values=value)
210+
211+
@staticmethod
212+
def _render_for_list_bool(name: str, value: List[bool]) -> str:
213+
return _template_key_values.render(name=name, values=[str(v).lower() for v in value])
202214

203-
def _render_for_list_str(self, name: str, value: List[str]) -> str:
204-
return self._template_key_values.render(name=name, values=value)
215+
@staticmethod
216+
def _render_for_variable(name: str, value: Variable) -> str:
217+
return _template_key_variable.render(name=name, value=value.name)
218+
219+
def _render_for_argument(self, name: str, value: 'Argument') -> str:
220+
return _template_key_argument.render(name=name, argument=self._line_shift(value.render()))
205221

206222
def _render_for_list_argument(self, name: str, value: List['Argument']) -> str:
207-
return self._template_key_arguments.render(
223+
return _template_key_arguments.render(
208224
name=name, arguments=[self._line_shift(argument.render()) for argument in value]
209225
)
210226

211227
def _render_for_list_list_argument(self, name: str, value: List[List['Argument']]) -> str:
212-
return self._template_key_objects.render(
228+
return _template_key_objects.render(
213229
name=name,
214230
list_arguments=[
215231
[self._line_shift(self._line_shift(argument.render())) for argument in arguments] for arguments in value
216232
],
217233
)
218234

219-
def _render_for_variable(self, name: str, value: Variable) -> str:
220-
return self._template_key_variable.render(name=name, value=value.name)
221-
222235
def render(self) -> str:
223236
if isinstance(self.value, str):
224237
return self._render_for_str(self.name, self.value)
225238

239+
if isinstance(self.value, bool):
240+
return self._render_for_str(self.name, str(self.value).lower())
241+
226242
if isinstance(self.value, int):
227243
return self._render_for_int(self.name, self.value)
228244

@@ -236,6 +252,12 @@ def render(self) -> str:
236252
if self._check_is_list_of_str(self.value):
237253
return self._render_for_list_str(self.name, self.value)
238254

255+
if self._check_is_list_of_bool(self.value):
256+
return self._render_for_list_bool(self.name, self.value)
257+
258+
if self._check_is_list_of_int(self.value):
259+
return self._render_for_list_str(self.name, [str(v) for v in self.value])
260+
239261
if self._check_is_list_of_arguments(self.value):
240262
return self._render_for_list_argument(self.name, self.value)
241263

@@ -285,11 +307,10 @@ class Directive(_GraphQL2PythonQuery):
285307
name: str
286308
arguments: List[Argument] = PydanticField(default_factory=list)
287309

288-
_template_directive: Template = template_env.get_template("directive.jinja2")
289-
290310
def render(self) -> str:
291-
return self._template_directive.render(
292-
name=self.name, arguments=[self._line_shift(argument.render()) for argument in self.arguments]
311+
return _template_directive.render(
312+
name=self.name,
313+
arguments=[self._line_shift(argument.render()) for argument in self.arguments],
293314
)
294315

295316

@@ -351,10 +372,8 @@ class Field(_GraphQL2PythonQuery):
351372
directives: List[Directive] = PydanticField(default_factory=list)
352373
typename: bool = PydanticField(default=False, description="Add meta field `__typename` to sub-fields.")
353374

354-
_template: Template = template_env.get_template("field.jinja2")
355-
356375
def render(self) -> str:
357-
return self._template.render(
376+
return _template_field.render(
358377
name=self.name,
359378
alias=self.alias,
360379
arguments=[self._line_shift(argument.render()) for argument in self.arguments],
@@ -405,10 +424,8 @@ class InlineFragment(_GraphQL2PythonQuery):
405424
fields: List[Union[str, 'Field', 'InlineFragment', 'Fragment']] = PydanticField(default_factory=list)
406425
typename: bool = PydanticField(default=False, description="Add meta field `__typename` to sub-fields.")
407426

408-
_template: Template = template_env.get_template("inline_fragment.jinja2")
409-
410427
def render(self) -> str:
411-
return self._template.render(
428+
return _template_inline_fragment.render(
412429
type=self.type,
413430
arguments=[self._line_shift(argument.render()) for argument in self.arguments],
414431
fields=[self._render_field(field) for field in self.fields],
@@ -465,10 +482,8 @@ class Fragment(_GraphQL2PythonQuery):
465482
fields: List[Union[str, 'Field', 'InlineFragment', 'Fragment']] = PydanticField(default_factory=list)
466483
typename: bool = PydanticField(default=False, description="Add meta field `__typename` to sub-fields")
467484

468-
_template: Template = template_env.get_template("fragment.jinja2")
469-
470485
def render(self) -> str:
471-
return self._template.render(
486+
return _template_fragment.render(
472487
name=self.name,
473488
type=self.type,
474489
fields=[self._render_field(field) for field in self.fields],
@@ -520,10 +535,8 @@ class Query(_GraphQL2PythonQuery):
520535
typename: bool = PydanticField(default=False, description="Add meta field `__typename` to the query.")
521536
fields: List[Union[str, 'Field', 'InlineFragment', 'Fragment']] = PydanticField(default_factory=list)
522537

523-
_template: Template = template_env.get_template("query.jinja2")
524-
525538
def render(self) -> str:
526-
return self._template.render(
539+
return _template_query.render(
527540
name=self.name,
528541
alias=self.alias,
529542
arguments=[self._line_shift(argument.render()) for argument in self.arguments],
@@ -533,7 +546,7 @@ def render(self) -> str:
533546

534547

535548
class Operation(_GraphQL2PythonQuery):
536-
"""GraphQL operation type.
549+
"""GraphQL Operation type.
537550
538551
See https://graphql.org/learn/queries/ for more details.
539552
@@ -590,23 +603,11 @@ class Operation(_GraphQL2PythonQuery):
590603
default_factory=list, description="https://graphql.org/learn/queries/#fragments"
591604
)
592605

593-
_template: Template = template_env.get_template("operation.jinja2")
594-
_supported_types = ["query", "mutation", "subscription"]
595-
596606
def render(self) -> str:
597-
return self._template.render(
607+
return _template_operation.render(
598608
type=self.type,
599609
name=self.name,
600610
variables=[self._line_shift(variable.render()) for variable in self.variables],
601611
queries=[self._line_shift(query.render()) for query in self.queries],
602612
fragments=[fragment.render() for fragment in self.fragments],
603613
)
604-
605-
606-
Variable.update_forward_refs()
607-
Argument.update_forward_refs()
608-
Field.update_forward_refs()
609-
InlineFragment.update_forward_refs()
610-
Fragment.update_forward_refs()
611-
Query.update_forward_refs()
612-
Operation.update_forward_refs()

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ classifiers = [
3838
]
3939
requires-python = ">=3.7"
4040
dependencies = [
41-
"pydantic>=1.10, <1.11",
41+
"pydantic>=2",
4242
"jinja2>=3.1, <3.2",
4343
]
4444
dynamic = ["version"]

0 commit comments

Comments
 (0)