Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement #29 #30

Merged
merged 15 commits into from
Jul 29, 2024
12 changes: 11 additions & 1 deletion dataclass_rest/boundmethod.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import copy

from abc import ABC, abstractmethod
from inspect import getcallargs
from logging import getLogger
Expand Down Expand Up @@ -30,7 +32,15 @@ def _apply_args(self, *args, **kwargs) -> Dict:
)

def _get_url(self, args) -> str:
return self.method_spec.url_template.format(**args)
args = copy.copy(args)

if not self.method_spec.url_template_func_pop_args:
return self.method_spec.url_template_func(**args)

for arg in self.method_spec.url_template_func_pop_args:
args.pop(arg)

return self.method_spec.url_template_func(**args)

def _get_body(self, args) -> Any:
python_body = args.get(self.method_spec.body_param_name)
Expand Down
8 changes: 6 additions & 2 deletions dataclass_rest/methodspec.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from typing import Any, Dict, Type, Callable, List
from typing import Any, Dict, Type, Callable, List, Optional


class MethodSpec:
def __init__(
self,
func: Callable,
url_template: str,
url_template: Optional[str],
url_template_func: Optional[Callable[..., str]],
url_template_func_pop_args: Optional[List[str]],
lubaskinc0de marked this conversation as resolved.
Show resolved Hide resolved
http_method: str,
response_type: Type,
body_param_name: str,
Expand All @@ -17,6 +19,8 @@ def __init__(
):
self.func = func
self.url_template = url_template
self.url_template_func = url_template_func
self.url_template_func_pop_args = url_template_func_pop_args
self.http_method = http_method
self.response_type = response_type
self.body_param_name = body_param_name
Expand Down
42 changes: 35 additions & 7 deletions dataclass_rest/parse_func.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
import string
from inspect import getfullargspec, FullArgSpec, isclass
from typing import Callable, List, Sequence, Any, Type, TypedDict, Dict
from typing import Callable, List, Sequence, Any, Type, TypedDict, Dict, Union, Optional

from .http_request import File
from .methodspec import MethodSpec

DEFAULT_BODY_PARAM = "body"


def get_url_params(url_template: str) -> List[str]:
parsed_format = string.Formatter().parse(url_template)
return [x[1] for x in parsed_format]
def get_url_params(url_template: Union[str, Callable[..., str]], arg_spec: Optional[FullArgSpec] = None) -> List[str]:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather split into 2 functions. It's strange to get arg_spec for string.
Additionally we can remove try/except below and just call 2 different functions for both cases

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

is_string = isinstance(url_template, str)

if is_string:
parsed_format = string.Formatter().parse(url_template)
return [x[1] for x in parsed_format]
else:
return arg_spec.args


def create_query_params_type(
Expand Down Expand Up @@ -54,19 +59,42 @@ def get_file_params(spec):
def parse_func(
func: Callable,
method: str,
url_template: str,
url_template: Union[str, Callable[..., str]],
additional_params: Dict[str, Any],
is_json_request: bool,
body_param_name: str,
) -> MethodSpec:
spec = getfullargspec(func)
url_params = get_url_params(url_template)
file_params = get_file_params(spec)

is_string_url_template = isinstance(url_template, str)
url_template_func = url_template.format if is_string_url_template else url_template

try:
url_template_func_arg_spec = getfullargspec(url_template_func)

url_template_func_args = set(url_template_func_arg_spec.args)
diff_kwargs = set(spec.kwonlyargs).difference(url_template_func_args)
diff_args = set(spec.args).difference(url_template_func_args)

url_template_func_pop_args = diff_args.union(diff_kwargs)
except TypeError as _exc:
url_template_func_arg_spec = None
url_template_func_pop_args = None

if is_string_url_template:
url_params = get_url_params(url_template)
else:
url_params = get_url_params(url_template_func, url_template_func_arg_spec)

skipped_params = url_params + file_params + [body_param_name]

return MethodSpec(
func=func,
http_method=method,
url_template=url_template,
url_template=url_template if is_string_url_template else None,
url_template_func=url_template_func,
url_template_func_pop_args=url_template_func_pop_args,
query_params_type=create_query_params_type(spec, func, skipped_params),
body_type=create_body_type(spec, body_param_name),
response_type=create_response_type(spec),
Expand Down
4 changes: 2 additions & 2 deletions dataclass_rest/rest.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
from functools import partial
from typing import Any, Dict, Optional, Callable
from typing import Any, Dict, Optional, Callable, Union

from .boundmethod import BoundMethod
from .method import Method
from .parse_func import parse_func, DEFAULT_BODY_PARAM


def rest(
url_template: str,
url_template: Union[str, Callable[..., str]],
*,
method: str,
body_name: str = DEFAULT_BODY_PARAM,
Expand Down