943acbc573
Durcit la convention de nommage des projets (dérive constatée : 'Sliding Automation', 'code_versioning'... au lieu des formes canoniques). - trilium_api.py : projets_canoniques() lit le référentiel = valeurs du label projet sur les notes de type=projet (source unique, pas de constante en dur). Note-projet CodeVersioning créée (manquait). - mcp_server.py : _valider_projet() branché dans les 6 tools de création (add_decision/history/backlog, new_conversation, create_entite, add_skill). Refuse un projet non canonique (suggestion si faute) ou inconnu (renvoi au processus de création de projet). Ne verrouille pas si référentiel illisible. - lint_audit.py : VAL-nommage aligné sur le référentiel (attrape casse, espace ET snake_case ; l'ancien 'contient un espace' ratait code_versioning). - Données : 79 notes ré-étiquetées vers les 3 formes canoniques. Quality by design : l'erreur de nommage devient impossible à l'écriture, le Lint n'est plus que le filet de sécurité.
45 lines
1.5 KiB
Python
45 lines
1.5 KiB
Python
from __future__ import annotations
|
|
|
|
import sys
|
|
from collections.abc import Awaitable, Iterator
|
|
from typing import Any, Callable, Protocol
|
|
|
|
if sys.version_info >= (3, 10): # pragma: no cover
|
|
from typing import ParamSpec
|
|
else: # pragma: no cover
|
|
from typing_extensions import ParamSpec
|
|
|
|
|
|
P = ParamSpec("P")
|
|
|
|
|
|
_Scope = Any
|
|
_Receive = Callable[[], Awaitable[Any]]
|
|
_Send = Callable[[Any], Awaitable[None]]
|
|
# Since `starlette.types.ASGIApp` type differs from `ASGIApplication` from `asgiref`
|
|
# we need to define a more permissive version of ASGIApp that doesn't cause type errors.
|
|
_ASGIApp = Callable[[_Scope, _Receive, _Send], Awaitable[None]]
|
|
|
|
|
|
class _MiddlewareFactory(Protocol[P]):
|
|
def __call__(self, app: _ASGIApp, /, *args: P.args, **kwargs: P.kwargs) -> _ASGIApp: ... # pragma: no cover
|
|
|
|
|
|
class Middleware:
|
|
def __init__(self, cls: _MiddlewareFactory[P], *args: P.args, **kwargs: P.kwargs) -> None:
|
|
self.cls = cls
|
|
self.args = args
|
|
self.kwargs = kwargs
|
|
|
|
def __iter__(self) -> Iterator[Any]:
|
|
as_tuple = (self.cls, self.args, self.kwargs)
|
|
return iter(as_tuple)
|
|
|
|
def __repr__(self) -> str:
|
|
class_name = self.__class__.__name__
|
|
args_strings = [f"{value!r}" for value in self.args]
|
|
option_strings = [f"{key}={value!r}" for key, value in self.kwargs.items()]
|
|
name = getattr(self.cls, "__name__", "")
|
|
args_repr = ", ".join([name] + args_strings + option_strings)
|
|
return f"{class_name}({args_repr})"
|