Skip to content

Commit

Permalink
Merge pull request #6 from lonelyteapot/refactor
Browse files Browse the repository at this point in the history
Various fixes and refactors
  • Loading branch information
lonelyteapot authored Jul 15, 2022
2 parents ca09b04 + 1002b1d commit f2b6bfb
Show file tree
Hide file tree
Showing 7 changed files with 35 additions and 35 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ incorrectly. The API may change at any time.
## Example

### Server schema

``` gql
type User {
id: int!
Expand Down
4 changes: 2 additions & 2 deletions pygraphic/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from . import types
from . import defaults, types
from ._gql_parameters import GQLParameters
from ._gql_query import GQLQuery
from ._gql_type import GQLType


__all__ = ["GQLParameters", "GQLType", "GQLQuery", "types"]
__all__ = ["defaults", "GQLParameters", "GQLType", "GQLQuery", "types"]
17 changes: 10 additions & 7 deletions pygraphic/_gql_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,28 @@

import pydantic
import pydantic.main
from pydantic.fields import Field, FieldInfo
from pydantic.main import __dataclass_transform__

from .defaults import default_alias_generator


@__dataclass_transform__(kw_only_default=True, field_descriptors=(Field, FieldInfo))
class ModelMetaclass(pydantic.main.ModelMetaclass):
def __getattr__(cls, __name: str) -> Any:
try:
return cls.__fields__[__name]
mcs: type[GQLParameters] = cls # type: ignore
return mcs.__fields__[__name]
except KeyError:
raise AttributeError(
f"type object '{cls.__name__}' has no attribute '{__name}'"
)


class GQLParameters(
pydantic.BaseModel,
metaclass=ModelMetaclass,
alias_generator=default_alias_generator,
allow_population_by_field_name=True,
):
class GQLParameters(pydantic.BaseModel, metaclass=ModelMetaclass):
def json(self, **kwargs: Any) -> str:
return super().json(by_alias=True, **kwargs)

class Config:
alias_generator = default_alias_generator
allow_population_by_field_name = True
32 changes: 14 additions & 18 deletions pygraphic/_gql_query.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
from typing import Any, Iterator, Optional
from __future__ import annotations

from typing import Iterator, Optional

import pydantic

from ._gql_parameters import GQLParameters
from ._gql_type import GQLType
from .defaults import default_alias_generator
from .types import class_to_graphql_type


class GQLQuery(
GQLType,
alias_generator=default_alias_generator,
allow_population_by_field_name=True,
):
__parameters__ = None

class GQLQuery(GQLType):
@classmethod
def get_query_string(cls, named: bool = True) -> str:
if not named and cls.__parameters__ is not None:
parameters: Optional[
type[GQLParameters]
] = cls.__config__.parameters # type: ignore

if not named and parameters is not None:
# TODO Find a better exception type
raise Exception("Query with parameters must have a name")

def _gen():
if named:
params = "".join(_gen_parameter_string(cls.__parameters__))
params = "".join(_gen_parameter_string(parameters))
yield "query " + cls.__name__ + params + " {"
else:
yield "query {"
Expand All @@ -31,13 +32,8 @@ def _gen():

return "\n".join(_gen())

def __init_subclass__(
cls,
parameters: Optional[type[GQLParameters]] = None,
**pydantic_kwargs: Any,
) -> None:
cls.__parameters__ = parameters
return super().__init_subclass__(**pydantic_kwargs)
class Config(pydantic.BaseConfig):
parameters: Optional[type[GQLParameters]] = None


def _gen_parameter_string(parameters: Optional[type[GQLParameters]]) -> Iterator[str]:
Expand Down
10 changes: 5 additions & 5 deletions pygraphic/_gql_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,7 @@
from .defaults import default_alias_generator


class GQLType(
pydantic.BaseModel,
alias_generator=default_alias_generator,
allow_population_by_field_name=True,
):
class GQLType(pydantic.BaseModel):
@classmethod
def generate_query_lines(cls, nest_level: int = 0) -> Iterator[str]:
fields = typing.get_type_hints(cls)
Expand All @@ -36,6 +32,10 @@ def generate_query_lines(cls, nest_level: int = 0) -> Iterator[str]:
yield " " * nest_level + field.alias + params
continue

class Config:
alias_generator = default_alias_generator
allow_population_by_field_name = True


def _gen_parameter_string(parameters: dict[str, Any]) -> Iterator[str]:
if not parameters:
Expand Down
2 changes: 1 addition & 1 deletion pygraphic/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ def class_to_graphql_type(python_class: type, allow_none: bool) -> str:
return type_ + "!"
except KeyError:
raise KeyError(
f"Type '{python_class.__name__}' could not be converted to a GraphQL type. "
f"Type '{python_class.__name__}' could not be converted to a GraphQL type."
"See pygraphic.types.register_graphql_type"
)
4 changes: 2 additions & 2 deletions tests/test_parametrized_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ def test_query_string_generation():

def test_local_query_execution():
query = GetUsersBornAfter.get_query_string()
variables = Parameters(bornAfter=date.fromtimestamp(0.0))
variables = Parameters(born_after=date.fromtimestamp(0.0))
result = server_schema.execute_sync(query, json.loads(variables.json()))
assert result.errors is None
assert result.data is not None


def test_pydantic_object_parsing():
query = GetUsersBornAfter.get_query_string()
variables = Parameters(bornAfter=date.fromtimestamp(0.0))
variables = Parameters(born_after=date.fromtimestamp(0.0))
result = server_schema.execute_sync(query, json.loads(variables.json()))
assert type(result.data) is dict
result = GetUsersBornAfter.parse_obj(result.data)
Expand Down

0 comments on commit f2b6bfb

Please sign in to comment.