Skip to content

Commit

Permalink
Add ruff for linting
Browse files Browse the repository at this point in the history
Apply ruff fixes
  • Loading branch information
yugokato committed Oct 1, 2024
1 parent 434df7e commit 588349f
Show file tree
Hide file tree
Showing 20 changed files with 128 additions and 84 deletions.
6 changes: 6 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ ci:
autofix_prs: false
autoupdate_schedule: quarterly
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: "v0.6.8"
hooks:
- id: ruff
args: ["--fix"]
types_or: [python]
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
Expand Down
24 changes: 23 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,22 @@ dependencies = [
"inflect==7.0.0",
"phonenumbers==8.13.45",
"pydantic-extra-types==2.9.0",
"pre-commit==3.6.2",
"pydantic[email]==2.9.2",
"PyYAML==6.0.1",
]
readme = "README.md"

[project.optional-dependencies]
dev = ["pre-commit==3.6.2", "ruff==0.6.8"]
app = [
"common-libs[dev]",
"Quart==0.19.4",
"quart-auth==0.9.0",
"quart-schema[pydantic]==0.19.1",

]
test = [
"common-libs[dev]",
"openapi-test-client[app]",
"pytest==8.3.2",
"pytest-lazy-fixtures==1.1.1",
Expand All @@ -49,3 +51,23 @@ profile = "black"

[tool.black]
line_length = 120

[tool.ruff]
line-length = 120
indent-width = 4

[tool.ruff.lint]
select = [
# pycodestyle
"E",
# Pyflakes
"F",
# pyupgrade
"UP",
]
ignore = ["E731", "E741", "F403"]

[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401"]
# We currently use `Optional` in a special way
"**/{clients,}/**/{api,models}/*" = ["UP007"]
2 changes: 1 addition & 1 deletion src/demo_app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def _register_blueprints(app, version: int):
from demo_app.handlers.error_handlers import bp_error_handler
from demo_app.handlers.request_handlers import bp_request_handler

bp_api = Blueprint(f"demo_app", __name__, url_prefix=f"/v{version}")
bp_api = Blueprint("demo_app", __name__, url_prefix=f"/v{version}")
bp_api.register_blueprint(bp_auth, name=bp_auth.name)
bp_api.register_blueprint(bp_user, name=bp_user.name)

Expand Down
29 changes: 14 additions & 15 deletions src/demo_app/api/user/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from enum import Enum
from typing import Optional

from pydantic import AnyUrl, BaseModel, EmailStr, Field
from quart_schema.pydantic import File
Expand All @@ -18,35 +17,35 @@ class UserTheme(Enum):


class UserQuery(BaseModel):
id: Optional[int] = None
email: Optional[EmailStr] = None
role: Optional[UserRole] = None
id: int | None = None
email: EmailStr | None = None
role: UserRole | None = None


class SocialLinks(BaseModel):
facebook: Optional[AnyUrl] = None
instagram: Optional[AnyUrl] = None
linkedin: Optional[AnyUrl] = None
github: Optional[AnyUrl] = None
facebook: AnyUrl | None = None
instagram: AnyUrl | None = None
linkedin: AnyUrl | None = None
github: AnyUrl | None = None


class Preferences(BaseModel):
theme: Optional[UserTheme] = UserTheme.LIGHT_MODE.value
language: Optional[str] = None
font_size: Optional[int] = Field(None, ge=8, le=40, multiple_of=2)
theme: UserTheme | None = UserTheme.LIGHT_MODE.value
language: str | None = None
font_size: int | None = Field(None, ge=8, le=40, multiple_of=2)


class Metadata(BaseModel):
preferences: Optional[Preferences] = None
social_links: Optional[SocialLinks] = None
preferences: Preferences | None = None
social_links: SocialLinks | None = None


class UserRequest(BaseModel):
first_name: str = Field(..., min_length=1, max_length=255)
last_name: str = Field(..., min_length=1, max_length=255)
email: EmailStr
role: UserRole
metadata: Optional[Metadata] = Field(default_factory=dict)
metadata: Metadata | None = Field(default_factory=dict)


class User(UserRequest):
Expand All @@ -55,4 +54,4 @@ class User(UserRequest):

class UserImage(BaseModel):
file: File
description: Optional[str] = None
description: str | None = None
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from __future__ import annotations

from collections.abc import Callable
from functools import wraps
from typing import TYPE_CHECKING, Callable, ParamSpec
from typing import TYPE_CHECKING, ParamSpec

from common_libs.clients.rest_client import RestResponse

Expand All @@ -15,9 +16,12 @@
def do_something_before_and_after_request(f: Callable[P, RestResponse]) -> Callable[P, RestResponse]:
"""This is a template of the request wrapper that will decorate an API request"
To enable this hook, add this function to the parent class's `request_wrapper` inside the base API class's pre_request_hook():
To enable this hook, add this function to the parent class's `request_wrapper` inside the base API class's
pre_request_hook():
>>> from typing import Callable
>>> from openapi_test_client.clients.demo_app.api.request_hooks.request_wrapper import do_something_before_and_after_request
>>> from openapi_test_client.clients.demo_app.api.request_hooks.request_wrapper import (
>>> do_something_before_and_after_request
>>> )
>>>
>>> def request_wrapper(self) -> list[Callable]:
>>> request_wrappers = super().request_wrapper() # noqa
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def init_api_classes(base_api_class: type[APIClassType]) -> list[type[APIClassTy
Endpoint(tag='Auth', api_class=<class 'test_client.clients.demo_app.api.auth.AuthAPI'>, method='get', path='/v1/auth/logout', func_name='logout', model=<class 'types.LogoutEndpointModel'>)
]
"""
""" # noqa: E501
from openapi_test_client.libraries.api import Endpoint

previous_frame = inspect.currentframe().f_back
Expand Down
11 changes: 6 additions & 5 deletions src/openapi_test_client/libraries/api/api_classes/base.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from __future__ import annotations

from abc import ABCMeta, abstractmethod
from typing import TYPE_CHECKING, Callable, Optional
from collections.abc import Callable
from typing import TYPE_CHECKING

from common_libs.clients.rest_client import RestResponse
from common_libs.logging import get_logger
Expand All @@ -18,10 +19,10 @@
class APIBase(metaclass=ABCMeta):
"""Base API class"""

app_name: Optional[str] = None
app_name: str | None = None
is_documented: bool = True
is_deprecated: bool = False
endpoints: Optional[list[Endpoint]] = None
endpoints: list[Endpoint] | None = None

def __init__(self, api_client: APIClientType):
if self.app_name != api_client.app_name:
Expand Down Expand Up @@ -51,8 +52,8 @@ def pre_request_hook(self, endpoint: Endpoint, *path_params, **params):
def post_request_hook(
self,
endpoint: Endpoint,
response: Optional[RestResponse],
request_exception: Optional[RequestException],
response: RestResponse | None,
request_exception: RequestException | None,
*path_params,
**params,
):
Expand Down
16 changes: 10 additions & 6 deletions src/openapi_test_client/libraries/api/api_client_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,8 @@ def update_endpoint_functions(
) -> bool | tuple[str, Exception]:
'''Update endpoint functions (signature and docstring) and API TAGs based on the definition of the latest API spec
When no exception is thrown during the process, a boolean flag to indicate whether update is required or not is returned.
When no exception is thrown during the process, a boolean flag to indicate whether update is required or not is
returned.
If an exception is thrown, API class name and the exception will be returned.
:param api_class: API class
Expand All @@ -258,7 +259,9 @@ def update_endpoint_functions(
>>> TAGs = ("Some Tag",)
>>>
>>> @endpoint.get("/v1/something/{uuid}")
>>> def do_something(self, uuid: str, /, *, param1: str = None, param2: int = None, **kwargs) -> RestResponse:
>>> def do_something(
>>> self, uuid: str, /, *, param1: str = None, param2: int = None, **kwargs
>>> ) -> RestResponse:
>>> """Do something"""
>>> ...
>>>
Expand Down Expand Up @@ -369,7 +372,8 @@ def update_existing_endpoints(target_api_class: type[APIClassType] = api_class):

# Collect all param models for this endpoint
param_models.extend(param_model_util.get_param_models(endpoint_model))
# Fill missing imports (typing and custom param model classes). Duplicates will be removed by black at the end
# Fill missing imports (typing and custom param model classes). Duplicates will be removed by black at
# the end
if missing_imports_code := param_model_util.generate_imports_code_from_model(api_class, endpoint_model):
new_code = missing_imports_code + new_code

Expand Down Expand Up @@ -424,9 +428,9 @@ def update_existing_endpoints(target_api_class: type[APIClassType] = api_class):
if tags_in_class:
defined_tags = re.findall(regex_tag, tags_in_class.group(0))
if defined_tags or (not defined_tags and tags_in_class):
# Update TAGs only when none of defined tags match with documented tags. Note that when multiple tags are
# documented, the updated tags may not what you exactly want. If that is the case you'll need to remove
# tags that is not needed for this API class
# Update TAGs only when none of defined tags match with documented tags. Note that when multiple tags
# are documented, the updated tags may not what you exactly want. If that is the case you'll need to
# remove tags that is not needed for this API class
if not set(defined_tags).intersection(api_spec_tags):
new_code = re.sub(regex_tags, f"TAGs = {tuple(api_spec_tags)}", new_code)
else:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from collections.abc import Callable
from functools import wraps
from typing import Callable, ParamSpec
from typing import ParamSpec

from common_libs.clients.rest_client import RestResponse
from common_libs.logging import get_logger
Expand Down
17 changes: 9 additions & 8 deletions src/openapi_test_client/libraries/api/api_functions/endpoints.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from __future__ import annotations

from collections.abc import Callable, Sequence
from copy import deepcopy
from dataclasses import dataclass
from functools import partial, update_wrapper, wraps
from threading import RLock
from typing import TYPE_CHECKING, Any, Callable, Optional, ParamSpec, Sequence, TypeVar, cast
from typing import TYPE_CHECKING, Any, ParamSpec, TypeVar, cast

from common_libs.ansi_colors import ColorCodes, color
from common_libs.clients.rest_client import RestResponse
Expand Down Expand Up @@ -49,8 +50,8 @@ class Endpoint:
path: str
func_name: str
model: type[EndpointModel]
url: Optional[str] = None # Available only for an endpoint object accessed via an API client instance
content_type: Optional[str] = None
url: str | None = None # Available only for an endpoint object accessed via an API client instance
content_type: str | None = None
is_public: bool = False
is_documented: bool = True
is_deprecated: bool = False
Expand Down Expand Up @@ -125,7 +126,7 @@ class endpoint:
>>> client.AUTH.login.endpoint.url
'http://127.0.0.1:5000/v1/auth/login'
"""
""" # noqa: E501

@staticmethod
def get(path: str, **requests_lib_options) -> Callable[P, OriginalFunc | EndpointFunc]:
Expand Down Expand Up @@ -409,7 +410,7 @@ def __init__(
self.is_deprecated = False
self.__decorators = []

def __get__(self, instance: Optional[APIClassType], owner: type[APIClassType]) -> EndpointFunc:
def __get__(self, instance: APIClassType | None, owner: type[APIClassType]) -> EndpointFunc:
"""Return an EndpointFunc object"""
key = (self.original_func.__name__, instance, owner)
with EndpointHandler._lock:
Expand Down Expand Up @@ -442,14 +443,14 @@ class EndpointFunc:
All parameters passed to the original API class function call will be passed through to the __call__()
"""

def __init__(self, endpoint_handler: EndpointHandler, instance: Optional[APIClassType], owner: type[APIClassType]):
def __init__(self, endpoint_handler: EndpointHandler, instance: APIClassType | None, owner: type[APIClassType]):
"""Initialize endpoint function"""
if not issubclass(owner, APIBase):
raise NotImplementedError(f"Unsupported API class: {owner}")

self.method = endpoint_handler.method
self.path = endpoint_handler.path
self.rest_client: Optional[RestClient]
self.rest_client: RestClient | None
if instance:
self.api_client = instance.api_client
self.rest_client = self.api_client.rest_client
Expand Down Expand Up @@ -643,7 +644,7 @@ def with_retry(
f = retry_on(condition, num_retry=num_retry, retry_after=retry_after, safe_methods_only=False)(self)
return f(*args, **kwargs)

def get_usage(self) -> Optional[str]:
def get_usage(self) -> str | None:
"""Get OpenAPI spec definition for the endpoint"""
if self.api_client and self.endpoint.is_documented:
return self.api_client.api_spec.get_endpoint_usage(self.endpoint)
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import json
import re
from collections import OrderedDict
from typing import TYPE_CHECKING, Annotated, Any, Optional, get_args, get_origin
from typing import TYPE_CHECKING, Annotated, Any, get_args, get_origin

from common_libs.clients.rest_client.utils import get_supported_request_parameters
from common_libs.logging import get_logger
Expand Down Expand Up @@ -39,7 +39,8 @@ def check_params(endpoint: Endpoint, params: dict[str, Any]):
if unexpected_params:
msg = (
f"The request contains one or more parameters "
f"{endpoint.api_class.__name__}.{endpoint.func_name}() does not expect:\n{list_items(unexpected_params)}"
f"{endpoint.api_class.__name__}.{endpoint.func_name}() does not expect:\n"
f"{list_items(unexpected_params)}"
)
logger.warning(msg)

Expand Down Expand Up @@ -224,7 +225,7 @@ def generate_rest_func_params(
# We will set the Content-type value using from the OpenAPI specs for this case, unless the header is explicitly
# set by a user. Otherwise, requests lib will automatically handle this part
if (data := rest_func_params.get("data")) and (
isinstance(data, (str, bytes)) and not specified_content_type_header and endpoint.content_type
isinstance(data, str | bytes) and not specified_content_type_header and endpoint.content_type
):
rest_func_params.setdefault("headers", {}).update({"Content-Type": endpoint.content_type})

Expand All @@ -233,7 +234,7 @@ def generate_rest_func_params(

def _get_specified_content_type_header(
requests_lib_options: dict[str, Any], session_headers: dict[str, str]
) -> Optional[str]:
) -> str | None:
"""Get Content-Type header value set for the request or for the current session"""
request_headers = requests_lib_options.get("headers", {})
content_type_header = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import re
from copy import deepcopy
from dataclasses import MISSING, Field, field, make_dataclass
from typing import TYPE_CHECKING, Any, Optional, cast
from typing import TYPE_CHECKING, Any, cast

from common_libs.logging import get_logger

Expand Down Expand Up @@ -131,7 +131,7 @@ def _parse_parameter_objects(
method: str,
parameter_objects: list[dict[str, Any]],
path_param_fields: list[tuple[str, Any]],
body_or_query_param_fields: list[tuple[str, Any, Optional[Field]]],
body_or_query_param_fields: list[tuple[str, Any, Field | None]],
):
"""Parse parameter objects
Expand Down Expand Up @@ -205,8 +205,8 @@ def _parse_parameter_objects(


def _parse_request_body_object(
request_body_obj: dict[str, Any], body_or_query_param_fields: list[tuple[str, Any, Optional[Field]]]
) -> Optional[str]:
request_body_obj: dict[str, Any], body_or_query_param_fields: list[tuple[str, Any, Field | None]]
) -> str | None:
"""Parse request body object
https://swagger.io/specification/#request-body-object
Expand Down Expand Up @@ -249,7 +249,7 @@ def parse_schema_obj(obj: dict[str, Any]):
if _is_file_param(content_type, param_def):
param_type = File
if not param_def.is_required:
param_type = Optional[param_type]
param_type = param_type | None
body_or_query_param_fields.append((param_name, param_type, field(default=None)))
else:
existing_param_names = [x[0] for x in body_or_query_param_fields]
Expand Down
Loading

0 comments on commit 588349f

Please sign in to comment.