diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 000000000..b837b5d14 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,30 @@ +name: Run Pre-commits + +env: + # enable colored output + # https://github.com/pytest-dev/pytest/issues/7443 + PY_COLORS: 1 + +on: + push: + branches: ["main"] + pull_request: + workflow_dispatch: + +permissions: + contents: read + +jobs: + static_analysis: + timeout-minutes: 1 + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Run pre-commit + uses: pre-commit/action@v3.0.1 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index e51173c31..66ab30968 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -5,28 +5,22 @@ on: workflow_dispatch: jobs: - publish-pypi-release: + pypi-publish: + name: Upload to PyPI runs-on: ubuntu-latest - environment: release permissions: - contents: write - id-token: write + id-token: write # For PyPI's trusted publishing steps: - name: Checkout uses: actions/checkout@v4 - - name: Set up Python - uses: actions/setup-python@v5 with: - python-version: "3.11" - cache: pip - cache-dependency-path: "**/pyproject.toml" - - name: Install dependencies - run: | - pip install setuptools wheel build + fetch-depth: 0 + + - name: "Install uv" + uses: astral-sh/setup-uv@v3 + - name: Build - run: | - python -m build - - name: Publish - uses: pypa/gh-action-pypi-publish@release/v1 - with: - verbose: true + run: uv build + + - name: Publish to PyPi + run: uv publish -v dist/* diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 86609284e..848c63272 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -28,7 +28,7 @@ jobs: run: uv python install 3.11 - name: Install FastMCP - run: uv sync --all-extras --dev + run: uv sync --extra dev - name: Run tests run: uv run pytest -vv diff --git a/.gitignore b/.gitignore index 4021cf770..7cab6898b 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,8 @@ wheels/ .DS_Store -src/fastmcp/_version.py \ No newline at end of file +src/fastmcp/_version.py + +# editors +.cursorrules +.vscode/ \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..9d6e1982b --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,20 @@ +fail_fast: true + +repos: + - repo: https://github.com/abravalheri/validate-pyproject + rev: v0.23 + hooks: + - id: validate-pyproject + + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v3.1.0 + hooks: + - id: prettier + types_or: [yaml, json5] + + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.8.0 + hooks: + - id: ruff-format + - id: ruff + args: [--fix, --exit-non-zero-on-fix] diff --git a/examples/echo.py b/examples/echo.py index 2ac9edc92..48c0883a5 100644 --- a/examples/echo.py +++ b/examples/echo.py @@ -4,7 +4,6 @@ FastMCP Echo Server from fastmcp import FastMCP - # Create server mcp = FastMCP("Echo Server") diff --git a/pyproject.toml b/pyproject.toml index e49a67d1b..19f04b769 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,15 +24,18 @@ build-backend = "setuptools.build_meta" [tool.setuptools_scm] write_to = "src/fastmcp/_version.py" -[dependency-groups] +[project.optional-dependencies] dev = [ "copychat>=0.5.2", "ipython>=8.12.3", "pdbpp>=0.10.3", + "pre-commit", "pytest-xdist>=3.6.1", "pytest>=8.3.3", "pytest-asyncio>=0.23.5", + "ruff", ] [tool.pytest.ini_options] asyncio_mode = "auto" +asyncio_default_fixture_loop_scope = "session" diff --git a/src/fastmcp/resources/base.py b/src/fastmcp/resources/base.py index eee7f1d24..bde9b3820 100644 --- a/src/fastmcp/resources/base.py +++ b/src/fastmcp/resources/base.py @@ -1,17 +1,43 @@ """Base classes and interfaces for FastMCP resources.""" import abc -from typing import Union +from typing import Annotated, Union -from pydantic import BaseModel, Field, field_validator -from pydantic.networks import _BaseUrl +from pydantic import ( + AnyUrl, + BaseModel, + BeforeValidator, + ConfigDict, + Field, + FileUrl, + ValidationInfo, + field_validator, +) +from pydantic.networks import _BaseUrl # TODO: remove this once pydantic is updated + + +def maybe_cast_str_to_any_url(x) -> AnyUrl: + if isinstance(x, FileUrl): + return x + elif isinstance(x, AnyUrl): + return x + elif isinstance(x, str): + if x.startswith("file://"): + return FileUrl(x) + return AnyUrl(x) + raise ValueError(f"Expected str or AnyUrl, got {type(x)}") + + +LaxAnyUrl = Annotated[_BaseUrl | str, BeforeValidator(maybe_cast_str_to_any_url)] class Resource(BaseModel, abc.ABC): """Base class for all resources.""" - uri: _BaseUrl = Field(description="URI of the resource") - name: str = Field(description="Name of the resource", default=None) + model_config = ConfigDict(validate_default=True) + + uri: LaxAnyUrl = Field(default=..., description="URI of the resource") + name: str | None = Field(description="Name of the resource", default=None) description: str | None = Field( description="Description of the resource", default=None ) @@ -23,7 +49,7 @@ class Resource(BaseModel, abc.ABC): @field_validator("name", mode="before") @classmethod - def set_default_name(cls, name: str | None, info) -> str: + def set_default_name(cls, name: str | None, info: ValidationInfo) -> str: """Set default name from URI if not provided.""" if name: return name @@ -35,7 +61,3 @@ class Resource(BaseModel, abc.ABC): async def read(self) -> Union[str, bytes]: """Read the resource content.""" pass - - model_config = { - "validate_default": True, - } diff --git a/src/fastmcp/resources/resource_manager.py b/src/fastmcp/resources/resource_manager.py index 3e690d83e..5b8c3ad92 100644 --- a/src/fastmcp/resources/resource_manager.py +++ b/src/fastmcp/resources/resource_manager.py @@ -2,7 +2,7 @@ from typing import Callable, Dict, Optional, Union -from pydantic.networks import _BaseUrl +from pydantic import AnyUrl from fastmcp.resources.base import Resource from fastmcp.resources.templates import ResourceTemplate @@ -64,7 +64,7 @@ class ResourceManager: self._templates[template.uri_template] = template return template - async def get_resource(self, uri: Union[_BaseUrl, str]) -> Optional[Resource]: + async def get_resource(self, uri: Union[AnyUrl, str]) -> Optional[Resource]: """Get resource by URI, checking concrete resources first, then templates.""" uri_str = str(uri) logger.debug("Getting resource", extra={"uri": uri_str}) diff --git a/src/fastmcp/resources/types.py b/src/fastmcp/resources/types.py index 3dd8db115..c5363a6d3 100644 --- a/src/fastmcp/resources/types.py +++ b/src/fastmcp/resources/types.py @@ -1,6 +1,5 @@ """Concrete resource implementations.""" -import pydantic_core import asyncio import json from pathlib import Path @@ -8,6 +7,7 @@ from typing import Any, Callable, Union import httpx import pydantic.json +import pydantic_core from pydantic import Field from fastmcp.resources.base import Resource diff --git a/src/fastmcp/server.py b/src/fastmcp/server.py index 2a36a5c75..c3ef8884c 100644 --- a/src/fastmcp/server.py +++ b/src/fastmcp/server.py @@ -1,43 +1,47 @@ """FastMCP - A more ergonomic interface for MCP servers.""" -import pydantic_core -from typing import Any, Literal, Optional, Union, Dict - -from mcp.server import RequestContext -from pydantic import BaseModel -from pydantic.networks import AnyUrl - -from fastmcp.utilities.logging import get_logger import asyncio import functools -import json -from typing import Callable, Sequence import inspect +import json import re +from itertools import chain +from typing import Any, Callable, Dict, Literal, Sequence +import pydantic_core +import uvicorn from mcp.server import Server as MCPServer -from mcp.server.stdio import stdio_server from mcp.server.sse import SseServerTransport +from mcp.server.stdio import stdio_server +from mcp.shared.context import RequestContext +from mcp.types import ( + EmbeddedResource, + GetPromptResult, + ImageContent, + TextContent, +) +from mcp.types import ( + Prompt as MCPPrompt, +) from mcp.types import ( Resource as MCPResource, - Tool as MCPTool, - ResourceTemplate as MCPResourceTemplate, - TextContent, - ImageContent, - EmbeddedResource, - Prompt as MCPPrompt, - GetPromptResult, ) -from pydantic_settings import BaseSettings -from pydantic.networks import _BaseUrl +from mcp.types import ( + ResourceTemplate as MCPResourceTemplate, +) +from mcp.types import ( + Tool as MCPTool, +) +from pydantic import BaseModel +from pydantic.networks import AnyUrl +from pydantic_settings import BaseSettings, SettingsConfigDict from fastmcp.exceptions import ResourceError -from fastmcp.resources import Resource, ResourceManager, FunctionResource -from fastmcp.tools import ToolManager -from fastmcp.utilities.logging import configure_logging -from fastmcp.utilities.types import Image from fastmcp.prompts import Prompt, PromptManager -from itertools import chain +from fastmcp.resources import FunctionResource, Resource, ResourceManager +from fastmcp.tools import ToolManager +from fastmcp.utilities.logging import configure_logging, get_logger +from fastmcp.utilities.types import Image logger = get_logger(__name__) @@ -49,7 +53,7 @@ class Settings(BaseSettings): For example, FASTMCP_DEBUG=true will set debug=True. """ - model_config: dict = dict(env_prefix="FASTMCP_") + model_config: SettingsConfigDict = SettingsConfigDict(env_prefix="FASTMCP_") # Server settings debug: bool = False @@ -70,7 +74,7 @@ class Settings(BaseSettings): class FastMCP: - def __init__(self, name=None, **settings: Optional[Settings]): + def __init__(self, name: str | None = None, **settings: Any): self.settings = Settings(**settings) self._mcp_server = MCPServer(name=name or "FastMCP") self._tool_manager = ToolManager( @@ -144,7 +148,7 @@ class FastMCP: async def call_tool( self, name: str, arguments: dict - ) -> Sequence[Union[TextContent, ImageContent]]: + ) -> Sequence[TextContent | ImageContent]: """Call a tool by name with arguments.""" context = self.get_context() result = await self._tool_manager.call_tool(name, arguments, context=context) @@ -158,7 +162,7 @@ class FastMCP: return [ MCPResource( uri=resource.uri, - name=resource.name, + name=resource.name or "", description=resource.description, mimeType=resource.mime_type, ) @@ -176,7 +180,7 @@ class FastMCP: for template in templates ] - async def read_resource(self, uri: _BaseUrl) -> Union[str, bytes]: + async def read_resource(self, uri: AnyUrl | str) -> str | bytes: """Read a resource by URI.""" resource = await self._resource_manager.get_resource(uri) if not resource: @@ -191,8 +195,8 @@ class FastMCP: def add_tool( self, fn: Callable, - name: Optional[str] = None, - description: Optional[str] = None, + name: str | None = None, + description: str | None = None, ) -> None: """Add a tool to the server. @@ -206,9 +210,7 @@ class FastMCP: """ self._tool_manager.add_tool(fn, name=name, description=description) - def tool( - self, name: Optional[str] = None, description: Optional[str] = None - ) -> Callable: + def tool(self, name: str | None = None, description: str | None = None) -> Callable: """Decorator to register a tool. Tools can optionally request a Context object by adding a parameter with the Context type annotation. @@ -258,9 +260,9 @@ class FastMCP: self, uri: str, *, - name: Optional[str] = None, - description: Optional[str] = None, - mime_type: Optional[str] = None, + name: str | None = None, + description: str | None = None, + mime_type: str | None = None, ) -> Callable: """Decorator to register a function as a resource. @@ -326,7 +328,7 @@ class FastMCP: else: # Register as regular resource resource = FunctionResource( - uri=uri, + uri=AnyUrl(uri), name=name, description=description, mime_type=mime_type or "text/plain", @@ -346,7 +348,7 @@ class FastMCP: self._prompt_manager.add_prompt(prompt) def prompt( - self, name: Optional[str] = None, description: Optional[str] = None + self, name: str | None = None, description: str | None = None ) -> Callable: """Decorator to register a prompt. @@ -398,7 +400,7 @@ class FastMCP: async def run_stdio_async(self) -> None: """Run the server using stdio transport.""" async with stdio_server() as (read_stream, write_stream): - print(f'Starting "{self.name}"...') + logger.info(f'Starting "{self.name}"...') await self._mcp_server.run( read_stream, write_stream, @@ -409,7 +411,6 @@ class FastMCP: """Run the server using SSE transport.""" from starlette.applications import Starlette from starlette.routing import Route - import uvicorn sse = SseServerTransport("/messages") @@ -434,12 +435,14 @@ class FastMCP: ], ) - uvicorn.run( + config = uvicorn.Config( starlette_app, host=self.settings.host, port=self.settings.port, - log_level=self.settings.log_level, + log_level=self.settings.log_level.lower(), ) + server = uvicorn.Server(config) + await server.serve() async def list_prompts(self) -> list[MCPPrompt]: """List all available prompts.""" @@ -461,7 +464,7 @@ class FastMCP: ] async def get_prompt( - self, name: str, arguments: Optional[Dict[str, Any]] = None + self, name: str, arguments: Dict[str, Any] | None = None ) -> GetPromptResult: """Get a prompt by name with arguments.""" try: @@ -475,7 +478,7 @@ class FastMCP: def _convert_to_content( result: Any, -) -> Sequence[Union[TextContent, ImageContent, EmbeddedResource]]: +) -> Sequence[TextContent | ImageContent | EmbeddedResource]: """Convert a result to a sequence of content objects.""" if result is None: return [] @@ -532,14 +535,14 @@ class Context(BaseModel): The context is optional - tools that don't need it can omit the parameter. """ - _request_context: RequestContext - _fastmcp: FastMCP + _request_context: RequestContext | None + _fastmcp: FastMCP | None def __init__( self, *, - request_context: RequestContext = None, - fastmcp: FastMCP = None, + request_context: RequestContext | None = None, + fastmcp: FastMCP | None = None, **kwargs: Any, ): super().__init__(**kwargs) @@ -561,7 +564,7 @@ class Context(BaseModel): return self._request_context async def report_progress( - self, progress: float, total: Optional[float] = None + self, progress: float, total: float | None = None ) -> None: """Report progress for the current operation. @@ -583,7 +586,7 @@ class Context(BaseModel): progress_token=progress_token, progress=progress, total=total ) - async def read_resource(self, uri: Union[str, AnyUrl]) -> Union[str, bytes]: + async def read_resource(self, uri: str | AnyUrl) -> str | bytes: """Read a resource by URI. Args: @@ -592,6 +595,9 @@ class Context(BaseModel): Returns: The resource content as either text or bytes """ + assert ( + self._fastmcp is not None + ), "Context is not available outside of a request" return await self._fastmcp.read_resource(uri) def log( @@ -599,7 +605,7 @@ class Context(BaseModel): level: Literal["debug", "info", "warning", "error"], message: str, *, - logger_name: Optional[str] = None, + logger_name: str | None = None, ) -> None: """Send a log message to the client. @@ -614,14 +620,18 @@ class Context(BaseModel): ) @property - def client_id(self) -> Optional[str]: + def client_id(self) -> str | None: """Get the client ID if available.""" - return self.request_context.meta.clientId if self.request_context.meta else None + return ( + getattr(self.request_context.meta, "client_id", None) + if self.request_context.meta + else None + ) @property def request_id(self) -> str: """Get the unique ID for this request.""" - return self.request_context.request_id + return str(self.request_context.request_id) @property def session(self): diff --git a/tests/resources/test_resources.py b/tests/resources/test_resources.py index 8bab8804f..0dc5a1b49 100644 --- a/tests/resources/test_resources.py +++ b/tests/resources/test_resources.py @@ -1,5 +1,6 @@ import pytest -from fastmcp.resources import Resource, FunctionResource + +from fastmcp.resources import FunctionResource, Resource class TestResourceValidation: @@ -95,4 +96,4 @@ class TestResourceValidation: pass with pytest.raises(TypeError, match="abstract method"): - ConcreteResource(uri="test://test", name="test") + ConcreteResource(uri="test://test", name="test") # type: ignore diff --git a/tests/test_server.py b/tests/test_server.py index f2ee46d22..950cf2246 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -1,16 +1,18 @@ +import base64 +from pathlib import Path +from typing import TYPE_CHECKING, Union + +import pytest +from mcp.shared.exceptions import McpError from mcp.shared.memory import ( create_connected_server_and_client_session as client_session, ) -from mcp.shared.exceptions import McpError -from fastmcp import FastMCP, Context +from mcp.types import ImageContent, TextContent + +from fastmcp import Context, FastMCP +from fastmcp.prompts.base import EmbeddedResource, Message, UserMessage from fastmcp.resources import FileResource, FunctionResource from fastmcp.utilities.types import Image -from mcp.types import TextContent, ImageContent -from fastmcp.prompts.base import Message, UserMessage, TextContent, EmbeddedResource -import pytest -from pathlib import Path -import base64 -from typing import Union, TYPE_CHECKING if TYPE_CHECKING: from fastmcp import Context @@ -35,7 +37,7 @@ class TestServer: with pytest.raises(TypeError, match="The @tool decorator was used incorrectly"): - @mcp.tool # Missing parentheses + @mcp.tool # Missing parentheses #type: ignore def add(x: int, y: int) -> int: return x + y @@ -55,7 +57,7 @@ class TestServer: TypeError, match="The @resource decorator was used incorrectly" ): - @mcp.resource # Missing parentheses + @mcp.resource # Missing parentheses #type: ignore def get_data(x: str) -> str: return f"Data: {x}" diff --git a/tests/test_tool_manager.py b/tests/test_tool_manager.py index 831c96167..31924542e 100644 --- a/tests/test_tool_manager.py +++ b/tests/test_tool_manager.py @@ -1,7 +1,8 @@ import logging +from typing import Optional + import pytest from pydantic import BaseModel -from typing import Optional from fastmcp.exceptions import ToolError from fastmcp.tools import ToolManager @@ -69,7 +70,7 @@ class TestAddTools: def test_add_invalid_tool(self): manager = ToolManager() with pytest.raises(AttributeError): - manager.add_tool(1) + manager.add_tool(1) # type: ignore def test_add_lambda(self): manager = ToolManager() @@ -185,7 +186,7 @@ class TestContextHandling: return str(x) manager = ToolManager() - tool = manager.add_tool(tool_with_context) + manager.add_tool(tool_with_context) mcp = FastMCP() ctx = mcp.get_context() @@ -201,7 +202,7 @@ class TestContextHandling: return str(x) manager = ToolManager() - tool = manager.add_tool(async_tool) + manager.add_tool(async_tool) mcp = FastMCP() ctx = mcp.get_context() @@ -216,7 +217,7 @@ class TestContextHandling: return str(x) manager = ToolManager() - tool = manager.add_tool(tool_with_context) + manager.add_tool(tool_with_context) # Should not raise an error when context is not provided result = await manager.call_tool("tool_with_context", {"x": 42}) assert result == "42" @@ -229,7 +230,7 @@ class TestContextHandling: raise ValueError("Test error") manager = ToolManager() - tool = manager.add_tool(tool_with_context) + manager.add_tool(tool_with_context) mcp = FastMCP() ctx = mcp.get_context() diff --git a/uv.lock b/uv.lock index 7ce77c70d..b341455df 100644 --- a/uv.lock +++ b/uv.lock @@ -25,25 +25,13 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e4/f5/f2b75d2fc6f1a260f340f0e7c6a060f4dd2961cc16884ed851b0d18da06a/anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d", size = 90377 }, ] -[[package]] -name = "appnope" -version = "0.1.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/35/5d/752690df9ef5b76e169e68d6a129fa6d08a7100ca7f754c89495db3c6019/appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee", size = 4170 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321 }, -] - [[package]] name = "asttokens" -version = "2.4.1" +version = "3.0.0" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/45/1d/f03bcb60c4a3212e15f99a56085d93093a497718adf828d050b9d675da81/asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0", size = 62284 } +sdist = { url = "https://files.pythonhosted.org/packages/4a/e7/82da0a03e7ba5141f05cce0d302e6eed121ae055e0456ca228bf693984bc/asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7", size = 61978 } wheels = [ - { url = "https://files.pythonhosted.org/packages/45/86/4736ac618d82a20d87d2f92ae19441ebc7ac9e7a581d7e58bbe79233b24a/asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24", size = 27764 }, + { url = "https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2", size = 26918 }, ] [[package]] @@ -55,15 +43,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6a/21/5b6702a7f963e95456c0de2d495f67bf5fd62840ac655dc451586d23d39a/attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2", size = 63001 }, ] -[[package]] -name = "backcall" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/40/764a663805d84deee23043e1426a9175567db89c8b3287b5c2ad9f71aa93/backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e", size = 18041 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4c/1c/ff6546b6c12603d8dd1070aa3c3d273ad4c07f5771689a7b69a550e8c951/backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255", size = 11157 }, -] - [[package]] name = "certifi" version = "2024.8.30" @@ -73,6 +52,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", size = 167321 }, ] +[[package]] +name = "cfgv" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249 }, +] + [[package]] name = "charset-normalizer" version = "3.4.0" @@ -189,6 +177,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d5/50/83c593b07763e1161326b3b8c6686f0f4b0f24d5526546bee538c89837d6/decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186", size = 9073 }, ] +[[package]] +name = "distlib" +version = "0.3.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0d/dd/1bec4c5ddb504ca60fc29472f3d27e8d4da1257a854e1d96742f15c1d02d/distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403", size = 613923 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/a1/cf2472db20f7ce4a6be1253a81cfdf85ad9c7885ffbed7047fb72c24cf87/distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", size = 468973 }, +] + [[package]] name = "exceptiongroup" version = "1.2.2" @@ -231,7 +228,7 @@ wheels = [ [[package]] name = "fastmcp" -version = "0.2.1.dev44+gbfae276.d20241130" +version = "0.2.1.dev56+g0fb37ec.d20241201" source = { editable = "." } dependencies = [ { name = "httpx" }, @@ -241,33 +238,42 @@ dependencies = [ { name = "typer" }, ] -[package.dev-dependencies] +[package.optional-dependencies] dev = [ { name = "copychat" }, { name = "ipython" }, { name = "pdbpp" }, + { name = "pre-commit" }, { name = "pytest" }, { name = "pytest-asyncio" }, { name = "pytest-xdist" }, + { name = "ruff" }, ] [package.metadata] requires-dist = [ + { name = "copychat", marker = "extra == 'dev'", specifier = ">=0.5.2" }, { name = "httpx", specifier = ">=0.26.0" }, + { name = "ipython", marker = "extra == 'dev'", specifier = ">=8.12.3" }, { name = "mcp", specifier = ">=1.0.0,<2.0.0" }, + { name = "pdbpp", marker = "extra == 'dev'", specifier = ">=0.10.3" }, + { name = "pre-commit", marker = "extra == 'dev'" }, { name = "pydantic", specifier = ">=2.5.3,<3.0.0" }, { name = "pydantic-settings", specifier = ">=2.6.1" }, + { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.3.3" }, + { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.23.5" }, + { name = "pytest-xdist", marker = "extra == 'dev'", specifier = ">=3.6.1" }, + { name = "ruff", marker = "extra == 'dev'" }, { name = "typer", specifier = ">=0.9.0" }, ] -[package.metadata.requires-dev] -dev = [ - { name = "copychat", specifier = ">=0.5.2" }, - { name = "ipython", specifier = ">=8.12.3" }, - { name = "pdbpp", specifier = ">=0.10.3" }, - { name = "pytest", specifier = ">=8.3.3" }, - { name = "pytest-asyncio", specifier = ">=0.23.5" }, - { name = "pytest-xdist", specifier = ">=3.6.1" }, +[[package]] +name = "filelock" +version = "3.16.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/db/3ef5bb276dae18d6ec2124224403d1d67bccdbefc17af4cc8f553e341ab1/filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435", size = 18037 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/f8/feced7779d755758a52d1f6635d990b8d98dc0a29fa568bbe0625f18fdf3/filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0", size = 16163 }, ] [[package]] @@ -340,6 +346,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819 }, ] +[[package]] +name = "identify" +version = "2.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1a/5f/05f0d167be94585d502b4adf8c7af31f1dc0b1c7e14f9938a88fdbbcf4a7/identify-2.6.3.tar.gz", hash = "sha256:62f5dae9b5fef52c84cc188514e9ea4f3f636b1d8799ab5ebc475471f9e47a02", size = 99179 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c9/f5/09644a3ad803fae9eca8efa17e1f2aef380c7f0b02f7ec4e8d446e51d64a/identify-2.6.3-py2.py3-none-any.whl", hash = "sha256:9edba65473324c2ea9684b1f944fe3191db3345e50b6d04571d10ed164f8d7bd", size = 99049 }, +] + [[package]] name = "idna" version = "3.10" @@ -360,25 +375,24 @@ wheels = [ [[package]] name = "ipython" -version = "8.12.3" +version = "8.30.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "appnope", marker = "sys_platform == 'darwin'" }, - { name = "backcall" }, { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "decorator" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "jedi" }, { name = "matplotlib-inline" }, - { name = "pexpect", marker = "sys_platform != 'win32'" }, - { name = "pickleshare" }, + { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, { name = "prompt-toolkit" }, { name = "pygments" }, { name = "stack-data" }, { name = "traitlets" }, + { name = "typing-extensions", marker = "python_full_version < '3.12'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9e/6a/44ef299b1762f5a73841e87fae8a73a8cc8aee538d6dc8c77a5afe1fd2ce/ipython-8.12.3.tar.gz", hash = "sha256:3910c4b54543c2ad73d06579aa771041b7d5707b033bd488669b4cf544e3b363", size = 5470171 } +sdist = { url = "https://files.pythonhosted.org/packages/d8/8b/710af065ab8ed05649afa5bd1e07401637c9ec9fb7cfda9eac7e91e9fbd4/ipython-8.30.0.tar.gz", hash = "sha256:cb0a405a306d2995a5cbb9901894d240784a9f341394c6ba3f4fe8c6eb89ff6e", size = 5592205 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/97/8fe103906cd81bc42d3b0175b5534a9f67dccae47d6451131cf8d0d70bb2/ipython-8.12.3-py3-none-any.whl", hash = "sha256:b0340d46a933d27c657b211a329d0be23793c36595acf9e6ef4164bc01a1804c", size = 798307 }, + { url = "https://files.pythonhosted.org/packages/1d/f3/1332ba2f682b07b304ad34cad2f003adcfeb349486103f4b632335074a7c/ipython-8.30.0-py3-none-any.whl", hash = "sha256:85ec56a7e20f6c38fce7727dcca699ae4ffc85985aa7b23635a8008f918ae321", size = 820765 }, ] [[package]] @@ -443,6 +457,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, ] +[[package]] +name = "nodeenv" +version = "1.9.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 }, +] + [[package]] name = "packaging" version = "24.2" @@ -497,12 +520,12 @@ wheels = [ ] [[package]] -name = "pickleshare" -version = "0.7.5" +name = "platformdirs" +version = "4.3.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/b6/df3c1c9b616e9c0edbc4fbab6ddd09df9535849c64ba51fcb6531c32d4d8/pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca", size = 6161 } +sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 } wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/41/220f49aaea88bc6fa6cba8d05ecf24676326156c23b991e80b3f2fc24c77/pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56", size = 6877 }, + { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 }, ] [[package]] @@ -514,6 +537,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, ] +[[package]] +name = "pre-commit" +version = "4.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cfgv" }, + { name = "identify" }, + { name = "nodeenv" }, + { name = "pyyaml" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2e/c8/e22c292035f1bac8b9f5237a2622305bc0304e776080b246f3df57c4ff9f/pre_commit-4.0.1.tar.gz", hash = "sha256:80905ac375958c0444c65e9cebebd948b3cdb518f335a091a670a89d652139d2", size = 191678 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/8f/496e10d51edd6671ebe0432e33ff800aa86775d2d147ce7d43389324a525/pre_commit-4.0.1-py2.py3-none-any.whl", hash = "sha256:efde913840816312445dc98787724647c65473daefe420785f885e8ed9a06878", size = 218713 }, +] + [[package]] name = "prompt-toolkit" version = "3.0.48" @@ -724,6 +763,50 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863 }, ] +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199 }, + { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758 }, + { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463 }, + { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280 }, + { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239 }, + { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802 }, + { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527 }, + { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052 }, + { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774 }, + { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 }, + { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 }, + { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 }, + { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167 }, + { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952 }, + { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301 }, + { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 }, + { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 }, + { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 }, + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, +] + [[package]] name = "regex" version = "2024.11.6" @@ -822,6 +905,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 }, ] +[[package]] +name = "ruff" +version = "0.8.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/d0/8ff5b189d125f4260f2255d143bf2fa413b69c2610c405ace7a0a8ec81ec/ruff-0.8.1.tar.gz", hash = "sha256:3583db9a6450364ed5ca3f3b4225958b24f78178908d5c4bc0f46251ccca898f", size = 3313222 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/d6/1a6314e568db88acdbb5121ed53e2c52cebf3720d3437a76f82f923bf171/ruff-0.8.1-py3-none-linux_armv6l.whl", hash = "sha256:fae0805bd514066f20309f6742f6ee7904a773eb9e6c17c45d6b1600ca65c9b5", size = 10532605 }, + { url = "https://files.pythonhosted.org/packages/89/a8/a957a8812e31facffb6a26a30be0b5b4af000a6e30c7d43a22a5232a3398/ruff-0.8.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b8a4f7385c2285c30f34b200ca5511fcc865f17578383db154e098150ce0a087", size = 10278243 }, + { url = "https://files.pythonhosted.org/packages/a8/23/9db40fa19c453fabf94f7a35c61c58f20e8200b4734a20839515a19da790/ruff-0.8.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:cd054486da0c53e41e0086e1730eb77d1f698154f910e0cd9e0d64274979a209", size = 9917739 }, + { url = "https://files.pythonhosted.org/packages/e2/a0/6ee2d949835d5701d832fc5acd05c0bfdad5e89cfdd074a171411f5ccad5/ruff-0.8.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2029b8c22da147c50ae577e621a5bfbc5d1fed75d86af53643d7a7aee1d23871", size = 10779153 }, + { url = "https://files.pythonhosted.org/packages/7a/25/9c11dca9404ef1eb24833f780146236131a3c7941de394bc356912ef1041/ruff-0.8.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2666520828dee7dfc7e47ee4ea0d928f40de72056d929a7c5292d95071d881d1", size = 10304387 }, + { url = "https://files.pythonhosted.org/packages/c8/b9/84c323780db1b06feae603a707d82dbbd85955c8c917738571c65d7d5aff/ruff-0.8.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:333c57013ef8c97a53892aa56042831c372e0bb1785ab7026187b7abd0135ad5", size = 11360351 }, + { url = "https://files.pythonhosted.org/packages/6b/e1/9d4bbb2ace7aad14ded20e4674a48cda5b902aed7a1b14e6b028067060c4/ruff-0.8.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:288326162804f34088ac007139488dcb43de590a5ccfec3166396530b58fb89d", size = 12022879 }, + { url = "https://files.pythonhosted.org/packages/75/28/752ff6120c0e7f9981bc4bc275d540c7f36db1379ba9db9142f69c88db21/ruff-0.8.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b12c39b9448632284561cbf4191aa1b005882acbc81900ffa9f9f471c8ff7e26", size = 11610354 }, + { url = "https://files.pythonhosted.org/packages/ba/8c/967b61c2cc8ebd1df877607fbe462bc1e1220b4a30ae3352648aec8c24bd/ruff-0.8.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:364e6674450cbac8e998f7b30639040c99d81dfb5bbc6dfad69bc7a8f916b3d1", size = 12813976 }, + { url = "https://files.pythonhosted.org/packages/7f/29/e059f945d6bd2d90213387b8c360187f2fefc989ddcee6bbf3c241329b92/ruff-0.8.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b22346f845fec132aa39cd29acb94451d030c10874408dbf776af3aaeb53284c", size = 11154564 }, + { url = "https://files.pythonhosted.org/packages/55/47/cbd05e5a62f3fb4c072bc65c1e8fd709924cad1c7ec60a1000d1e4ee8307/ruff-0.8.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b2f2f7a7e7648a2bfe6ead4e0a16745db956da0e3a231ad443d2a66a105c04fa", size = 10760604 }, + { url = "https://files.pythonhosted.org/packages/bb/ee/4c3981c47147c72647a198a94202633130cfda0fc95cd863a553b6f65c6a/ruff-0.8.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:adf314fc458374c25c5c4a4a9270c3e8a6a807b1bec018cfa2813d6546215540", size = 10391071 }, + { url = "https://files.pythonhosted.org/packages/6b/e6/083eb61300214590b188616a8ac6ae1ef5730a0974240fb4bec9c17de78b/ruff-0.8.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a885d68342a231b5ba4d30b8c6e1b1ee3a65cf37e3d29b3c74069cdf1ee1e3c9", size = 10896657 }, + { url = "https://files.pythonhosted.org/packages/77/bd/aacdb8285d10f1b943dbeb818968efca35459afc29f66ae3bd4596fbf954/ruff-0.8.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:d2c16e3508c8cc73e96aa5127d0df8913d2290098f776416a4b157657bee44c5", size = 11228362 }, + { url = "https://files.pythonhosted.org/packages/39/72/fcb7ad41947f38b4eaa702aca0a361af0e9c2bf671d7fd964480670c297e/ruff-0.8.1-py3-none-win32.whl", hash = "sha256:93335cd7c0eaedb44882d75a7acb7df4b77cd7cd0d2255c93b28791716e81790", size = 8803476 }, + { url = "https://files.pythonhosted.org/packages/e4/ea/cae9aeb0f4822c44651c8407baacdb2e5b4dcd7b31a84e1c5df33aa2cc20/ruff-0.8.1-py3-none-win_amd64.whl", hash = "sha256:2954cdbe8dfd8ab359d4a30cd971b589d335a44d444b6ca2cb3d1da21b75e4b6", size = 9614463 }, + { url = "https://files.pythonhosted.org/packages/eb/76/fbb4bd23dfb48fa7758d35b744413b650a9fd2ddd93bca77e30376864414/ruff-0.8.1-py3-none-win_arm64.whl", hash = "sha256:55873cc1a473e5ac129d15eccb3c008c096b94809d693fc7053f588b67822737", size = 8959621 }, +] + [[package]] name = "shellingham" version = "1.5.4" @@ -831,15 +939,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 }, ] -[[package]] -name = "six" -version = "1.16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", size = 34041 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", size = 11053 }, -] - [[package]] name = "smmap" version = "5.0.1" @@ -1029,6 +1128,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/50/c1/2d27b0a15826c2b71dcf6e2f5402181ef85acf439617bb2f1453125ce1f3/uvicorn-0.32.1-py3-none-any.whl", hash = "sha256:82ad92fd58da0d12af7482ecdb5f2470a04c9c9a53ced65b9bbb4a205377602e", size = 63828 }, ] +[[package]] +name = "virtualenv" +version = "20.28.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "platformdirs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bf/75/53316a5a8050069228a2f6d11f32046cfa94fbb6cc3f08703f59b873de2e/virtualenv-20.28.0.tar.gz", hash = "sha256:2c9c3262bb8e7b87ea801d715fae4495e6032450c71d2309be9550e7364049aa", size = 7650368 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/f9/0919cf6f1432a8c4baa62511f8f8da8225432d22e83e3476f5be1a1edc6e/virtualenv-20.28.0-py3-none-any.whl", hash = "sha256:23eae1b4516ecd610481eda647f3a7c09aea295055337331bb4e6892ecce47b0", size = 4276702 }, +] + [[package]] name = "wcwidth" version = "0.2.13"