Skip to content

Commit

Permalink
move examples enable ruff D (#17)
Browse files Browse the repository at this point in the history
  • Loading branch information
samuelcolvin authored Oct 21, 2024
1 parent c91603f commit 351b249
Show file tree
Hide file tree
Showing 13 changed files with 48 additions and 54 deletions.
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion pydantic_ai/_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ async def validate(


class ToolRetryError(Exception):
"""Internal exception used to indicate a signal a `ToolRetry` message should be returned to the LLM"""
"""Internal exception used to indicate a signal a `ToolRetry` message should be returned to the LLM."""

def __init__(self, tool_retry: messages.ToolRetry):
self.tool_retry = tool_retry
Expand Down
4 changes: 1 addition & 3 deletions pydantic_ai/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,7 @@ class ArgsObject:

@dataclass
class ToolCall:
"""
Either a retriever/tool call or structured response from the agent.
"""
"""Either a retriever/tool call or structured response from the agent."""

tool_name: str
args: ArgsJson | ArgsObject
Expand Down
7 changes: 4 additions & 3 deletions pydantic_ai/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,10 @@ class AbstractToolDefinition(Protocol):

@cache
def cached_async_http_client() -> AsyncHTTPClient:
"""
There are good reasons why in production you should use a `AsyncHTTPClient` as an async context manager as
"""Cached HTTPX async client so multiple agents and calls can share the same client.
There are good reasons why in production you should use a `httpx.AsyncClient` as an async context manager as
described in [encode/httpx#2026](https://github.com/encode/httpx/pull/2026), but when experimenting or showing
examples, it's very useful, this allows multiple Agents to use a single client.
examples, it's very useful not to, this allows multiple Agents to use a single client.
"""
return AsyncHTTPClient()
31 changes: 13 additions & 18 deletions pydantic_ai/models/gemini.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
"""
"""Custom interface to the `generativelanguage.googleapis.com` API using HTTPX and Pydantic.
The Google SDK for interacting with the `generativelanguage.googleapis.com` API
[`google-generativeai`](https://ai.google.dev/gemini-api/docs/quickstart?lang=python) reads like it was written by a
Java developer who thought they knew everything about OOP, spent 30 minutes trying to learn Python,
gave up and decided to build the library to prove how horrible Python is. It also doesn't use httpx for HTTP requests,
and it tries to implement tool calling itself, but doesn't use Pydantic or equivalent for validation.
and tries to implement tool calling itself, but doesn't use Pydantic or equivalent for validation.
We could also use the Google Vertex SDK,
[`google-cloud-aiplatform`](https://cloud.google.com/vertex-ai/docs/python-sdk/use-vertex-ai-python-sdk)
which uses the `*-aiplatform.googleapis.com` API, but that requires a service account for authentication
which is a faff to set up and manage. The big advantages of `*-aiplatform.googleapis.com` is that it claims API
compatibility with OpenAI's API, but I suspect Gemini's limited support for JSON Schema means you'd need to
hack around its limitations anyway for tool calls.
This code is a custom interface to the `generativelanguage.googleapis.com` API using httpx, Pydantic
and just a little bit of Python knowledge.
"""

from __future__ import annotations as _annotations
Expand Down Expand Up @@ -161,9 +159,7 @@ def process_response(response: _GeminiResponse) -> LLMMessage:

@staticmethod
def message_to_gemini(m: Message) -> _utils.Either[_GeminiTextPart, _GeminiContent]:
"""
Convert a message to a _GeminiTextPart for "system_instructions" or _GeminiContent for "contents".
"""
"""Convert a message to a _GeminiTextPart for "system_instructions" or _GeminiContent for "contents"."""
if m.role == 'system':
# SystemPrompt ->
return _utils.Either(left=_GeminiTextPart(text=m.content))
Expand Down Expand Up @@ -256,7 +252,7 @@ def from_call(cls, tool: ToolCall) -> _GeminiFunctionCallPart:

@dataclass
class _GeminiFunctionCall:
"""See <https://ai.google.dev/api/caching#FunctionCall>"""
"""See <https://ai.google.dev/api/caching#FunctionCall>."""

name: str
args: dict[str, Any]
Expand All @@ -273,7 +269,7 @@ def from_response(cls, name: str, response: dict[str, Any]) -> _GeminiFunctionRe

@dataclass
class _GeminiFunctionResponse:
"""See <https://ai.google.dev/api/caching#FunctionResponse>"""
"""See <https://ai.google.dev/api/caching#FunctionResponse>."""

name: str
response: dict[str, Any]
Expand Down Expand Up @@ -335,8 +331,7 @@ class _GeminiFunctionCallingConfig:

@dataclass
class _GeminiResponse:
"""
Schema for the response from the Gemini API.
"""Schema for the response from the Gemini API.
See <https://ai.google.dev/api/generate-content#v1beta.GenerateContentResponse>
"""
Expand Down Expand Up @@ -379,7 +374,7 @@ def as_cost(self) -> shared.Cost:

@dataclass
class _GeminiSafetyRating:
"""See https://ai.google.dev/gemini-api/docs/safety-settings#safety-filters"""
"""See <https://ai.google.dev/gemini-api/docs/safety-settings#safety-filters>."""

category: Literal[
'HARM_CATEGORY_HARASSMENT',
Expand All @@ -393,7 +388,7 @@ class _GeminiSafetyRating:

@dataclass
class _GeminiPromptFeedback:
"""See <https://ai.google.dev/api/generate-content#v1beta.GenerateContentResponse>"""
"""See <https://ai.google.dev/api/generate-content#v1beta.GenerateContentResponse>."""

block_reason: Annotated[str, Field(alias='blockReason')]
safety_ratings: Annotated[list[_GeminiSafetyRating], Field(alias='safetyRatings')]
Expand All @@ -404,10 +399,10 @@ class _GeminiPromptFeedback:


class _GeminiJsonSchema:
"""
Transforms the JSON Schema from Pydantic to be suitable for Gemini which
[support](https://ai.google.dev/gemini-api/docs/function-calling#function_declarations) a subset
of OpenAPI v3.0.3.
"""Transforms the JSON Schema from Pydantic to be suitable for Gemini.
Gemini which [supports](https://ai.google.dev/gemini-api/docs/function-calling#function_declarations)
a subset of OpenAPI v3.0.3.
Specifically:
* gemini doesn't allow the `title` keyword to be set
Expand Down
13 changes: 6 additions & 7 deletions pydantic_ai/models/test.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Utilities for testing apps build with pydantic_ai, specifically by using a model based which calls
local functions.
"""Utilities for testing apps build with pydantic_ai.
Specifically by using a model based which calls all retrievers in the agent by inferring the arguments
from the JSON schema. Also infers suitable data for the return type.
"""

from __future__ import annotations as _annotations
Expand All @@ -20,8 +21,7 @@

@dataclass
class TestModel(Model):
"""
A model specifically for testing purposes.
"""A model specifically for testing purposes.
This will (by default) call all retrievers in the agent model, then return a tool response if possible,
otherwise a plain response.
Expand Down Expand Up @@ -132,8 +132,7 @@ def gen_retriever_args(self, tool_def: AbstractToolDefinition) -> Any:


class _JsonSchemaTestData:
"""
Generate data that matches a JSON schema.
"""Generate data that matches a JSON schema.
This tries to generate the minimal viable data for the schema.
"""
Expand Down
6 changes: 2 additions & 4 deletions pydantic_ai/shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,7 @@ def __init__(self, history: list[messages.Message], model: Model):
super().__init__(f'Error while running model {self.model_name}')

def cause(self) -> ValidationError | UnexpectedModelBehaviour:
"""
This is really just typing super and improved find-ability for `Exception.__cause__`.
"""
"""This is really just typing super and improved find-ability for `Exception.__cause__`."""
cause = self.__cause__
if isinstance(cause, (ValidationError, UnexpectedModelBehaviour)):
return cause
Expand Down Expand Up @@ -137,7 +135,7 @@ def __init__(self, message: str):


class UnexpectedModelBehaviour(RuntimeError):
"""Error caused by unexpected Model behavior, e.g. an unexpected response code"""
"""Error caused by unexpected Model behavior, e.g. an unexpected response code."""

message: str
body: str | None
Expand Down
20 changes: 14 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ include = ["/README.md", "/Makefile", "/pydantic_ai", "/tests"]
[tool.ruff]
line-length = 120
target-version = "py39"
include = [
"pydantic_ai/**/*.py",
"tests/**/*.py",
"examples/**/*.py",
]

[tool.ruff.lint]
extend-select = [
Expand All @@ -78,18 +83,18 @@ extend-select = [
"C90",
"UP",
"I",
# TODo require docstrings
# "D",
"D",
]
flake8-quotes = { inline-quotes = "single", multiline-quotes = "double" }
isort = { combine-as-imports = true, known-first-party = ["pydantic_ai"] }
mccabe = { max-complexity = 14 }
mccabe = { max-complexity = 15 }
ignore = [
"D101", # ignore missing docstring in public class TODO remove
"D102", # ignore missing docstring in public method TODO remove
"D107", # ignore missing docstring in __init__ methods
"D100", # ignore missing docstring in module
"D104", # ignore missing docstring in public package
"D105", # ignore missing docstring in magic methods
"C901", # ignore too complex functions, doesn't seem to be worth it
]

[tool.ruff.lint.pydocstyle]
Expand All @@ -99,13 +104,16 @@ convention = "google"
docstring-code-format = true
quote-style = "single"

[tool.ruff.lint.per-file-ignores]
"tests/**.py" = ["D"]

[tool.pyright]
typeCheckingMode = "strict"
reportUnnecessaryTypeIgnoreComment = true
reportMissingTypeStubs = false
include = ["pydantic_ai", "tests", "demos"]
include = ["pydantic_ai", "tests", "examples"]
venvPath = ".venv"
# see https://github.com/microsoft/pyright/issues/7771, we don't want to error on decorated functions in tests
# see https://github.com/microsoft/pyright/issues/7771 - we don't want to error on decorated functions in tests
# which are not otherwise used
executionEnvironments = [
{ root = "tests", reportUnusedFunction = false },
Expand Down
4 changes: 1 addition & 3 deletions tests/models/test_model_test.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
"""
This module contains tests for the testing module.
"""
"""This module contains tests for the testing module."""

from __future__ import annotations as _annotations

Expand Down
11 changes: 5 additions & 6 deletions tests/test_retrievers.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,7 @@ def invalid_retriever(x: int, ctx: CallContext[None]) -> str: # pragma: no cove


async def google_style_docstring(foo: int, bar: str) -> str: # pragma: no cover
"""
Do foobar stuff, a lot.
"""Do foobar stuff, a lot.
Args:
foo: The foo thing.
Expand Down Expand Up @@ -97,8 +96,7 @@ def test_docstring_google():


def sphinx_style_docstring(foo: int, /) -> str: # pragma: no cover
"""
Sphinx style docstring.
"""Sphinx style docstring.
:param foo: The foo thing.
:return: The result.
Expand Down Expand Up @@ -126,8 +124,7 @@ def test_docstring_sphinx():


def numpy_style_docstring(*, foo: int, bar: str) -> str: # pragma: no cover
"""
Numpy style docstring.
"""Numpy style docstring.
Parameters
----------
Expand Down Expand Up @@ -180,6 +177,7 @@ def test_docstring_unknown():
)


# fmt: off
async def google_style_docstring_no_body(
foo: int, bar: Annotated[str, Field(description='from fields')]
) -> str: # pragma: no cover
Expand All @@ -188,6 +186,7 @@ async def google_style_docstring_no_body(
foo: The foo thing.
bar: The bar thing.
"""
# fmt: on
return f'{foo} {bar}'


Expand Down
4 changes: 1 addition & 3 deletions tests/typed_agent.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
"""
This file is used to test static typing, it's analyzed with pyright and mypy.
"""
"""This file is used to test static typing, it's analyzed with pyright and mypy."""

from collections.abc import Iterator
from contextlib import contextmanager
Expand Down

0 comments on commit 351b249

Please sign in to comment.