diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bbf0986b56..766279dcb1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -81,6 +81,31 @@ jobs: - name: Run pyright run: pdm run pyright + slotscheck: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.8" + allow-prereleases: false + + - uses: pdm-project/setup-pdm@v4 + name: Set up PDM + with: + python-version: "3.8" + allow-python-prereleases: false + cache: true + cache-dependency-path: | + ./pdm.lock + + - name: Install dependencies + run: pdm install -G:all + + - name: Run slotscheck + run: pdm run slotscheck litestar + test: name: "test (${{ matrix.python-version }})" strategy: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index abdb4aac6d..ecf00f31c9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -41,11 +41,6 @@ repos: - id: ensure-dunder-all exclude: "test*|examples*|tools" args: ["--use-tuple"] - - repo: https://github.com/ariebovenberg/slotscheck - rev: v0.19.0 - hooks: - - id: slotscheck - exclude: "test_*|docs|.github" - repo: https://github.com/sphinx-contrib/sphinx-lint rev: "v0.9.1" hooks: diff --git a/Makefile b/Makefile index 4407d391da..b17df23549 100644 --- a/Makefile +++ b/Makefile @@ -102,8 +102,14 @@ pre-commit: ## Runs pre-commit hooks; includes ruff formatting and lin @$(PDM) run pre-commit run --all-files @echo "=> Pre-commit complete" +.PHONY: slots-check +slots-check: ## Check for slots usage in classes + @echo "=> Checking for slots usage in classes" + @$(PDM) run slotscheck litestar + @echo "=> Slots check complete" + .PHONY: lint -lint: pre-commit type-check ## Run all linting +lint: pre-commit type-check slots-check ## Run all linting .PHONY: coverage coverage: ## Run the tests and generate coverage report diff --git a/litestar/app.py b/litestar/app.py index e1bd989d75..928617e322 100644 --- a/litestar/app.py +++ b/litestar/app.py @@ -150,21 +150,17 @@ class Litestar(Router): "csrf_config", "event_emitter", "get_logger", - "include_in_schema", "logger", "logging_config", "multipart_form_part_limit", "on_shutdown", "on_startup", "openapi_config", - "request_class", "response_cache_config", "route_map", - "signature_namespace", "state", "stores", "template_engine", - "websocket_class", "pdb_on_exception", "experimental_features", ) diff --git a/litestar/contrib/opentelemetry/middleware.py b/litestar/contrib/opentelemetry/middleware.py index 762bae9125..59ea4dce06 100644 --- a/litestar/contrib/opentelemetry/middleware.py +++ b/litestar/contrib/opentelemetry/middleware.py @@ -24,8 +24,6 @@ class OpenTelemetryInstrumentationMiddleware(AbstractMiddleware): """OpenTelemetry Middleware.""" - __slots__ = ("open_telemetry_middleware",) - def __init__(self, app: ASGIApp, config: OpenTelemetryConfig) -> None: """Middleware that adds OpenTelemetry instrumentation to the application. diff --git a/litestar/dto/_types.py b/litestar/dto/_types.py index 24e99b793b..b0863b2593 100644 --- a/litestar/dto/_types.py +++ b/litestar/dto/_types.py @@ -96,9 +96,6 @@ class MappingType(CompositeType): @dataclass(frozen=True) class TransferDTOFieldDefinition(DTOFieldDefinition): __slots__ = ( - "default_factory", - "dto_field", - "model_name", "is_excluded", "is_partial", "serialization_name", diff --git a/litestar/handlers/websocket_handlers/listener.py b/litestar/handlers/websocket_handlers/listener.py index 86fefc913a..8e702ea1aa 100644 --- a/litestar/handlers/websocket_handlers/listener.py +++ b/litestar/handlers/websocket_handlers/listener.py @@ -62,11 +62,10 @@ class WebsocketListenerRouteHandler(WebsocketRouteHandler): "connection_accept_handler": "Callback to accept a WebSocket connection. By default, calls WebSocket.accept", "on_accept": "Callback invoked after a WebSocket connection has been accepted", "on_disconnect": "Callback invoked after a WebSocket connection has been closed", - "weboscket_class": "WebSocket class", "_connection_lifespan": None, - "_handle_receive": None, - "_handle_send": None, + "_receive_handler": None, "_receive_mode": None, + "_send_handler": None, "_send_mode": None, } diff --git a/litestar/handlers/websocket_handlers/route_handler.py b/litestar/handlers/websocket_handlers/route_handler.py index edb49c3030..4b8953ee26 100644 --- a/litestar/handlers/websocket_handlers/route_handler.py +++ b/litestar/handlers/websocket_handlers/route_handler.py @@ -18,6 +18,8 @@ class WebsocketRouteHandler(BaseRouteHandler): Use this decorator to decorate websocket handler functions. """ + __slots__ = ("websocket_class",) + def __init__( self, path: str | list[str] | None = None, diff --git a/litestar/middleware/compression/facade.py b/litestar/middleware/compression/facade.py index 0074b57419..a1a62728ce 100644 --- a/litestar/middleware/compression/facade.py +++ b/litestar/middleware/compression/facade.py @@ -12,6 +12,8 @@ class CompressionFacade(Protocol): """A unified facade offering a uniform interface for different compression libraries.""" + __slots__ = () + encoding: ClassVar[str] """The encoding of the compression.""" diff --git a/litestar/middleware/cors.py b/litestar/middleware/cors.py index 6c4de31f8f..010576aa6a 100644 --- a/litestar/middleware/cors.py +++ b/litestar/middleware/cors.py @@ -17,8 +17,6 @@ class CORSMiddleware(AbstractMiddleware): """CORS Middleware.""" - __slots__ = ("config",) - def __init__(self, app: ASGIApp, config: CORSConfig) -> None: """Middleware that adds CORS validation to the application. diff --git a/litestar/middleware/logging.py b/litestar/middleware/logging.py index 0094f10cfa..c986eb6433 100644 --- a/litestar/middleware/logging.py +++ b/litestar/middleware/logging.py @@ -48,8 +48,6 @@ class LoggingMiddleware(AbstractMiddleware): """Logging middleware.""" - __slots__ = ("config", "logger", "request_extractor", "response_extractor", "is_struct_logger") - logger: Logger def __init__(self, app: ASGIApp, config: LoggingMiddlewareConfig) -> None: diff --git a/litestar/middleware/rate_limit.py b/litestar/middleware/rate_limit.py index cd767ba4a2..0c3de7f6e5 100644 --- a/litestar/middleware/rate_limit.py +++ b/litestar/middleware/rate_limit.py @@ -41,8 +41,6 @@ class CacheObject: class RateLimitMiddleware(AbstractMiddleware): """Rate-limiting middleware.""" - __slots__ = ("app", "check_throttle_handler", "max_requests", "unit", "request_quota", "config") - def __init__(self, app: ASGIApp, config: RateLimitConfig) -> None: """Initialize ``RateLimitMiddleware``. diff --git a/litestar/plugins/base.py b/litestar/plugins/base.py index afc571efe7..65710c9fc0 100644 --- a/litestar/plugins/base.py +++ b/litestar/plugins/base.py @@ -212,6 +212,8 @@ def to_openapi_schema(self, field_definition: FieldDefinition, schema_creator: S class OpenAPISchemaPlugin(OpenAPISchemaPluginProtocol): """Plugin to extend the support of OpenAPI schema generation for non-library types.""" + __slots__ = () + @staticmethod def is_plugin_supported_type(value: Any) -> bool: """Given a value of indeterminate type, determine if this value is supported by the plugin. diff --git a/litestar/stores/base.py b/litestar/stores/base.py index 34aa514fca..69a63663e5 100644 --- a/litestar/stores/base.py +++ b/litestar/stores/base.py @@ -20,6 +20,8 @@ class Store(ABC): """Thread and process safe asynchronous key/value store.""" + __slots__ = () + @abstractmethod async def set(self, key: str, value: str | bytes, expires_in: int | timedelta | None = None) -> None: """Set a value. @@ -97,6 +99,8 @@ class NamespacedStore(Store): should be isolated. """ + __slots__ = ("namespace",) + @abstractmethod def with_namespace(self, namespace: str) -> Self: """Return a new instance of :class:`NamespacedStore`, which exists in a child namespace of the current namespace. diff --git a/litestar/stores/redis.py b/litestar/stores/redis.py index 6697962fab..4b46097199 100644 --- a/litestar/stores/redis.py +++ b/litestar/stores/redis.py @@ -21,7 +21,12 @@ class RedisStore(NamespacedStore): """Redis based, thread and process safe asynchronous key/value store.""" - __slots__ = ("_redis",) + __slots__ = ( + "_delete_all_script", + "_get_and_renew_script", + "_redis", + "handle_client_shutdown", + ) def __init__( self, redis: Redis, namespace: str | None | EmptyType = Empty, handle_client_shutdown: bool = False diff --git a/pyproject.toml b/pyproject.toml index 1dfc87b68b..3eb1d2489d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -285,6 +285,12 @@ reportUnnecessaryTypeIgnoreComments = true [tool.slotscheck] strict-imports = false +exclude-classes = """ +( + # github.com/python/cpython/pull/106771 + (^litestar.events.emitter:BaseEventEmitterBackend) +) +""" [tool.ruff] lint.select = [