From 75a96522d44602c75ed08b91570b7a2f47a80493 Mon Sep 17 00:00:00 2001 From: nil Date: Fri, 27 Feb 2026 12:32:48 +0100 Subject: [PATCH 01/31] Add low-level MCP contrib package with tools and tests --- AGENTS.md | 41 ++++++ CHANGELOG.rst | 3 + contrib-requirements.txt | 1 + docs/source/contrib/index.rst | 1 + docs/source/contrib/mcp.md | 42 ++++++ guillotina/contrib/mcp/__init__.py | 24 ++++ guillotina/contrib/mcp/backend.py | 127 +++++++++++++++++ guillotina/contrib/mcp/install.py | 16 +++ guillotina/contrib/mcp/interfaces.py | 25 ++++ guillotina/contrib/mcp/permissions.py | 9 ++ guillotina/contrib/mcp/server.py | 64 +++++++++ guillotina/contrib/mcp/services.py | 96 +++++++++++++ guillotina/contrib/mcp/subscribers.py | 18 +++ guillotina/contrib/mcp/tools.py | 192 ++++++++++++++++++++++++++ guillotina/tests/mcp/test_mcp.py | 80 +++++++++++ setup.py | 1 + 16 files changed, 740 insertions(+) create mode 100644 AGENTS.md create mode 100644 docs/source/contrib/mcp.md create mode 100644 guillotina/contrib/mcp/__init__.py create mode 100644 guillotina/contrib/mcp/backend.py create mode 100644 guillotina/contrib/mcp/install.py create mode 100644 guillotina/contrib/mcp/interfaces.py create mode 100644 guillotina/contrib/mcp/permissions.py create mode 100644 guillotina/contrib/mcp/server.py create mode 100644 guillotina/contrib/mcp/services.py create mode 100644 guillotina/contrib/mcp/subscribers.py create mode 100644 guillotina/contrib/mcp/tools.py create mode 100644 guillotina/tests/mcp/test_mcp.py diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..2bb39ab88 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,41 @@ +# AGENTS.md + +## Project Overview +- Purpose: Guillotina core framework and official contrib addons. +- Main stack: Python async API server (ASGI), PostgreSQL, optional Redis. +- Key paths: + - `guillotina/` core framework and contrib packages + - `guillotina/tests/` test suite + - `docs/source/` documentation + +## Development Commands +- Setup (local venv expected at repo root): + - `python3 -m venv .venv` + - `source .venv/bin/activate` + - `pip install -r requirements.txt` + - `pip install -r contrib-requirements.txt` + - `pip install -e '.[test]'` +- Run local server: + - `g` (uses `config.yaml` by default) +- Run tests: + - `.venv/bin/pytest guillotina/tests` + - Targeted: `.venv/bin/pytest guillotina/tests/` + +## Validation +- For contrib changes, run focused tests under the touched contrib test folder. +- For API/service changes, verify status codes and response payload contracts. +- Keep docs updated under `docs/source/contrib/` when adding contrib features. + +## Deployment Notes +- This repo is a framework/library; no direct client deployment from this repo by default. +- Build/release lifecycle should follow package versioning (`VERSION`, `CHANGELOG.rst`). + +## Constraints / Gotchas +- Keep compatibility with repository formatting (`black` line length 110). +- Avoid wrapper layers when task explicitly requires low-level protocol primitives. +- Never commit credentials or local environment files. + +## Task Closeout Notes +- Update `CHANGELOG.rst` for notable changes. +- Record branch name, commit hash, validation output, and task evidence in Ops Tracker. + diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c6d9d1ced..3a62a4ef3 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,9 @@ CHANGELOG 7.0.7 (unreleased) ------------------ +- Add `guillotina.contrib.mcp` with low-level MCP server integration + (`mcp.server.lowlevel`), tool registry utility, MCP services, + cache invalidation subscribers, and tests/docs coverage. - Docs: Update documentation and configuration settings - Chore: Update sphinx-guillotina-theme version to 1.0.9 [rboixaderg] diff --git a/contrib-requirements.txt b/contrib-requirements.txt index b7260ebc9..babdae365 100644 --- a/contrib-requirements.txt +++ b/contrib-requirements.txt @@ -13,6 +13,7 @@ MarkupSafe<2.1.0 pytz==2020.1 emcache==0.6.0; python_version < '3.10' pymemcache==3.4.0; python_version < '3.10' +mcp>=1.0.0 # Conditional Pillow versions pillow==10.4.0; python_version < '3.11' diff --git a/docs/source/contrib/index.rst b/docs/source/contrib/index.rst index 2af6a4ea0..89224182b 100644 --- a/docs/source/contrib/index.rst +++ b/docs/source/contrib/index.rst @@ -17,3 +17,4 @@ Contents: swagger mailer dbusers + mcp diff --git a/docs/source/contrib/mcp.md b/docs/source/contrib/mcp.md new file mode 100644 index 000000000..be729f5b3 --- /dev/null +++ b/docs/source/contrib/mcp.md @@ -0,0 +1,42 @@ +# MCP + +`guillotina.contrib.mcp` provides a low-level MCP integration layer built on +the official `mcp.server.lowlevel` primitives (no FastMCP wrapper). + +## Installation + +```bash +pip install "guillotina[mcp]" +``` + +## Configuration + +```yaml +applications: + - guillotina + - guillotina.contrib.mcp + +mcp: + enabled: true + server_name: guillotina-mcp + default_child_limit: 50 +``` + +## Runtime endpoints + +- `GET /@mcp`: registry metadata and registered tools. +- `GET /@mcp/tools`: tool list and schemas. +- `POST /@mcp/tools/invoke`: executes one tool with payload + `{ "tool": "", "arguments": { ... } }`. +- `GET /@mcp/server/status`: validates low-level SDK availability. + +## Built-in tools + +- `resolve_path`: resolve a path and return basic metadata. +- `list_children`: list child resources from a folder-like resource. +- `serialize_resource`: execute Guillotina serialization adapters. +- `notify_modified`: emit an `ObjectModifiedEvent`. + +The tool registry is implemented as a Guillotina utility and cache invalidation +is handled by subscribers on object add/modify/remove events. + diff --git a/guillotina/contrib/mcp/__init__.py b/guillotina/contrib/mcp/__init__.py new file mode 100644 index 000000000..26dfdfb30 --- /dev/null +++ b/guillotina/contrib/mcp/__init__.py @@ -0,0 +1,24 @@ +from guillotina import configure + + +app_settings = { + "mcp": { + "enabled": True, + "server_name": "guillotina-mcp", + "default_child_limit": 50, + }, + "load_utilities": { + "mcp_tool_registry": { + "provides": "guillotina.contrib.mcp.interfaces.IMCPToolRegistry", + "factory": "guillotina.contrib.mcp.backend.MCPToolRegistry", + "settings": {}, + } + }, +} + + +def includeme(root, settings): + configure.scan("guillotina.contrib.mcp.install") + configure.scan("guillotina.contrib.mcp.permissions") + configure.scan("guillotina.contrib.mcp.services") + configure.scan("guillotina.contrib.mcp.subscribers") diff --git a/guillotina/contrib/mcp/backend.py b/guillotina/contrib/mcp/backend.py new file mode 100644 index 000000000..4e19a91d7 --- /dev/null +++ b/guillotina/contrib/mcp/backend.py @@ -0,0 +1,127 @@ +from dataclasses import dataclass +from guillotina import app_settings +from guillotina.contrib.mcp import tools +from typing import Any +from typing import Awaitable +from typing import Callable +from typing import Dict +from typing import List +from typing import Optional + +import json + + +ToolHandler = Callable[[Any, Any, Dict[str, Any]], Awaitable[Dict[str, Any]]] + + +@dataclass +class MCPTool: + name: str + description: str + input_schema: Dict[str, Any] + handler: ToolHandler + cacheable: bool = False + + +class MCPToolRegistry: + def __init__(self, settings: Optional[Dict[str, Any]] = None): + config = app_settings.get("mcp", {}) + self._settings = settings or {} + self._enabled = bool(config.get("enabled", True)) + self._server_name = str(config.get("server_name", "guillotina-mcp")) + self._default_child_limit = int(config.get("default_child_limit", 50)) + self._tools: Dict[str, MCPTool] = {} + self._cache: Dict[str, Dict[str, Any]] = {} + self._cache_revision = 0 + self._register_default_tools() + + def _register_default_tools(self) -> None: + for tool_name, description, input_schema, handler, cacheable in tools.default_tools( + self._default_child_limit + ): + self.register_tool( + name=tool_name, + description=description, + input_schema=input_schema, + handler=handler, + cacheable=cacheable, + ) + + def is_enabled(self) -> bool: + return self._enabled + + def register_tool( + self, + *, + name: str, + description: str, + input_schema: Dict[str, Any], + handler: ToolHandler, + cacheable: bool = False, + ) -> None: + clean_name = str(name or "").strip() + if not clean_name: + raise ValueError("Tool name is required") + self._tools[clean_name] = MCPTool( + name=clean_name, + description=str(description or "").strip(), + input_schema=input_schema or {"type": "object"}, + handler=handler, + cacheable=cacheable, + ) + + def list_tools(self) -> List[Dict[str, Any]]: + return [ + { + "name": tool.name, + "description": tool.description, + "inputSchema": tool.input_schema, + "cacheable": tool.cacheable, + } + for tool in sorted(self._tools.values(), key=lambda registered: registered.name) + ] + + def _cache_key(self, tool_name: str, arguments: Dict[str, Any]) -> str: + payload = json.dumps(arguments, sort_keys=True, separators=(",", ":"), default=str) + return f"{tool_name}:{payload}" + + async def invoke( + self, tool_name: str, context: Any, request: Any, arguments: Optional[Dict[str, Any]] = None + ) -> Dict[str, Any]: + clean_name = str(tool_name or "").strip() + if clean_name not in self._tools: + raise ValueError(f"Unknown MCP tool: {tool_name}") + + clean_arguments = arguments or {} + if not isinstance(clean_arguments, dict): + raise ValueError("Tool arguments must be an object") + + tool = self._tools[clean_name] + cache_key = self._cache_key(clean_name, clean_arguments) + if tool.cacheable and cache_key in self._cache: + return self._cache[cache_key] + + result = await tool.handler(context, request, clean_arguments) + if tool.cacheable: + self._cache[cache_key] = result + return result + + def invalidate_cache(self, reason: str = "manual") -> None: + self._cache.clear() + self._cache_revision += 1 + + def metadata(self) -> Dict[str, Any]: + return { + "enabled": self.is_enabled(), + "server_name": self._server_name, + "tool_count": len(self._tools), + "cache_revision": self._cache_revision, + } + + def create_lowlevel_server(self, context: Any = None, request: Any = None) -> Any: + from guillotina.contrib.mcp.server import LowLevelMCPServer + + adapter = LowLevelMCPServer( + registry=self, context=context, request=request, server_name=self._server_name + ) + return adapter.build() diff --git a/guillotina/contrib/mcp/install.py b/guillotina/contrib/mcp/install.py new file mode 100644 index 000000000..f694bd2ec --- /dev/null +++ b/guillotina/contrib/mcp/install.py @@ -0,0 +1,16 @@ +from guillotina import configure +from guillotina.addons import Addon +from guillotina.contrib.mcp.interfaces import IMCPSettings +from guillotina.utils import get_registry + + +@configure.addon(name="mcp", title="Guillotina MCP integration") +class MCPAddon(Addon): + @classmethod + async def install(cls, container, request): + registry = await get_registry() + registry.register_interface(IMCPSettings) + + @classmethod + async def uninstall(cls, container, request): + pass diff --git a/guillotina/contrib/mcp/interfaces.py b/guillotina/contrib/mcp/interfaces.py new file mode 100644 index 000000000..ab7db107b --- /dev/null +++ b/guillotina/contrib/mcp/interfaces.py @@ -0,0 +1,25 @@ +from guillotina import schema +from zope.interface import Interface + + +class IMCPSettings(Interface): + enabled = schema.Bool(title="Enable MCP services", default=True, required=False) + server_name = schema.TextLine(title="Low-level MCP server name", default="guillotina-mcp", required=False) + default_child_limit = schema.Int(title="Default list_children limit", default=50, required=False) + + +class IMCPToolRegistry(Interface): + def list_tools(): + """Return registered MCP tools.""" + + async def invoke(tool_name, context, request, arguments=None): + """Execute one tool and return a JSON-serializable response.""" + + def metadata(): + """Return metadata for diagnostics.""" + + def invalidate_cache(reason="manual"): + """Invalidate cached tool responses.""" + + def create_lowlevel_server(context=None, request=None): + """Build a low-level MCP server object.""" diff --git a/guillotina/contrib/mcp/permissions.py b/guillotina/contrib/mcp/permissions.py new file mode 100644 index 000000000..9e0d23880 --- /dev/null +++ b/guillotina/contrib/mcp/permissions.py @@ -0,0 +1,9 @@ +from guillotina import configure + + +configure.permission("guillotina.MCPView", "View MCP integration services") +configure.permission("guillotina.MCPExecute", "Execute MCP tools") + +configure.grant(permission="guillotina.MCPView", role="guillotina.Manager") +configure.grant(permission="guillotina.MCPView", role="guillotina.Owner") +configure.grant(permission="guillotina.MCPExecute", role="guillotina.Manager") diff --git a/guillotina/contrib/mcp/server.py b/guillotina/contrib/mcp/server.py new file mode 100644 index 000000000..57725ddfd --- /dev/null +++ b/guillotina/contrib/mcp/server.py @@ -0,0 +1,64 @@ +from typing import Any + +import importlib +import json + + +class LowLevelMCPServer: + def __init__( + self, *, registry: Any, context: Any = None, request: Any = None, server_name: str = "guillotina-mcp" + ): + self.registry = registry + self.context = context + self.request = request + self.server_name = server_name + + def _load_lowlevel_modules(self): + try: + lowlevel = importlib.import_module("mcp.server.lowlevel") + types_module = importlib.import_module("mcp.types") + except ImportError as exc: + raise RuntimeError( + 'Low-level MCP SDK is missing. Install it with `pip install "guillotina[mcp]"`.' + ) from exc + return lowlevel, types_module + + def _build_tool_type(self, types_module: Any, tool_data: Any) -> Any: + try: + return types_module.Tool( + name=tool_data["name"], + description=tool_data["description"], + inputSchema=tool_data["inputSchema"], + ) + except TypeError: + return types_module.Tool( + name=tool_data["name"], + description=tool_data["description"], + input_schema=tool_data["inputSchema"], + ) + + def _build_text_content_type(self, types_module: Any, text: str) -> Any: + try: + return types_module.TextContent(type="text", text=text) + except TypeError: + return types_module.TextContent(text=text) + + def build(self) -> Any: + lowlevel, types_module = self._load_lowlevel_modules() + server = lowlevel.Server(self.server_name) + + @server.list_tools() + async def handle_list_tools(): + return [ + self._build_tool_type(types_module, tool_data) for tool_data in self.registry.list_tools() + ] + + @server.call_tool() + async def handle_call_tool(name: str, arguments: Any): + if self.context is None or self.request is None: + raise ValueError("Context and request are required to execute Guillotina MCP tools") + result = await self.registry.invoke(name, self.context, self.request, arguments or {}) + payload = json.dumps(result, ensure_ascii=True, sort_keys=True, default=str) + return [self._build_text_content_type(types_module, payload)] + + return server diff --git a/guillotina/contrib/mcp/services.py b/guillotina/contrib/mcp/services.py new file mode 100644 index 000000000..ce2ec3eb5 --- /dev/null +++ b/guillotina/contrib/mcp/services.py @@ -0,0 +1,96 @@ +from guillotina import configure +from guillotina.api.service import Service +from guillotina.component import query_utility +from guillotina.contrib.mcp.interfaces import IMCPToolRegistry +from guillotina.interfaces import IResource +from guillotina.response import HTTPBadRequest +from guillotina.response import HTTPServiceUnavailable + + +def _get_registry(): + registry = query_utility(IMCPToolRegistry) + if registry is None: + raise HTTPServiceUnavailable(content={"reason": "MCP registry utility is not available"}) + return registry + + +@configure.service( + method="GET", + context=IResource, + name="@mcp", + permission="guillotina.MCPView", + summary="Inspect MCP integration status and metadata", + allow_access=True, +) +class MCPInfoService(Service): + async def __call__(self): + registry = _get_registry() + return {"mcp": registry.metadata(), "tools": registry.list_tools()} + + +@configure.service( + method="GET", + context=IResource, + name="@mcp/tools", + permission="guillotina.MCPView", + summary="List available MCP tools", + allow_access=True, +) +class MCPToolsService(Service): + async def __call__(self): + registry = _get_registry() + return {"tools": registry.list_tools()} + + +@configure.service( + method="POST", + context=IResource, + name="@mcp/tools/invoke", + permission="guillotina.MCPExecute", + summary="Execute one registered MCP tool", + allow_access=True, +) +class MCPInvokeToolService(Service): + async def __call__(self): + registry = _get_registry() + try: + payload = await self.request.json() + except Exception as exc: + raise HTTPBadRequest(content={"reason": "Invalid JSON payload"}) from exc + + if not isinstance(payload, dict): + raise HTTPBadRequest(content={"reason": "Invalid payload: expected object"}) + + tool_name = payload.get("tool") + if not isinstance(tool_name, str) or not tool_name.strip(): + raise HTTPBadRequest(content={"reason": "Tool name is required"}) + + arguments = payload.get("arguments", {}) + if not isinstance(arguments, dict): + raise HTTPBadRequest(content={"reason": "Tool arguments must be an object"}) + + try: + result = await registry.invoke(tool_name, self.context, self.request, arguments) + except ValueError as exc: + raise HTTPBadRequest(content={"reason": str(exc)}) from exc + + return {"tool": tool_name, "result": result} + + +@configure.service( + method="GET", + context=IResource, + name="@mcp/server/status", + permission="guillotina.MCPView", + summary="Check whether low-level MCP server dependencies are available", + allow_access=True, +) +class MCPServerStatusService(Service): + async def __call__(self): + registry = _get_registry() + metadata = registry.metadata() + try: + registry.create_lowlevel_server(context=self.context, request=self.request) + except RuntimeError as exc: + return {"ready": False, "error": str(exc), "mcp": metadata} + return {"ready": True, "mcp": metadata} diff --git a/guillotina/contrib/mcp/subscribers.py b/guillotina/contrib/mcp/subscribers.py new file mode 100644 index 000000000..44feeac3e --- /dev/null +++ b/guillotina/contrib/mcp/subscribers.py @@ -0,0 +1,18 @@ +from guillotina import configure +from guillotina.component import query_utility +from guillotina.contrib.mcp.interfaces import IMCPToolRegistry +from guillotina.interfaces import IBeforeObjectRemovedEvent +from guillotina.interfaces import IObjectAddedEvent +from guillotina.interfaces import IObjectModifiedEvent +from guillotina.interfaces import IResource + + +@configure.subscriber(for_=(IResource, IObjectAddedEvent)) +@configure.subscriber(for_=(IResource, IObjectModifiedEvent)) +@configure.subscriber(for_=(IResource, IBeforeObjectRemovedEvent)) +def invalidate_mcp_cache_on_content_change(obj, event): + registry = query_utility(IMCPToolRegistry) + if registry is None: + return + object_id = getattr(obj, "id", None) or getattr(obj, "__name__", None) or "unknown" + registry.invalidate_cache(reason=f"{event.__class__.__name__}:{object_id}") diff --git a/guillotina/contrib/mcp/tools.py b/guillotina/contrib/mcp/tools.py new file mode 100644 index 000000000..ec1292665 --- /dev/null +++ b/guillotina/contrib/mcp/tools.py @@ -0,0 +1,192 @@ +from guillotina.component import query_multi_adapter +from guillotina.event import notify +from guillotina.events import ObjectModifiedEvent +from guillotina.interfaces import IResourceSerializeToJson +from guillotina.interfaces import IResourceSerializeToJsonSummary +from guillotina.utils import get_content_path +from guillotina.utils import get_current_container +from guillotina.utils import navigate_to +from typing import Any +from typing import Awaitable +from typing import Callable +from typing import Dict +from typing import List +from typing import Tuple + +import functools + + +ToolHandler = Callable[[Any, Any, Dict[str, Any]], Awaitable[Dict[str, Any]]] + + +RESOLVE_PATH_SCHEMA = { + "type": "object", + "properties": { + "path": {"type": "string", "description": "Absolute or relative Guillotina path", "default": "/"}, + "include_serialized": {"type": "boolean", "default": False}, + }, +} + +LIST_CHILDREN_SCHEMA = { + "type": "object", + "properties": { + "path": {"type": "string", "description": "Absolute or relative Guillotina path", "default": "/"}, + "limit": {"type": "integer", "minimum": 1, "maximum": 200, "default": 50}, + "include_serialized": {"type": "boolean", "default": False}, + }, +} + +SERIALIZE_RESOURCE_SCHEMA = { + "type": "object", + "properties": { + "path": {"type": "string", "description": "Absolute or relative Guillotina path", "default": "/"} + }, +} + +NOTIFY_MODIFIED_SCHEMA = { + "type": "object", + "properties": { + "path": {"type": "string", "description": "Absolute or relative Guillotina path"}, + "payload": {"type": "object", "description": "Payload sent to ObjectModifiedEvent", "default": {}}, + }, + "required": ["path"], +} + + +def _normalize_path(raw_path: Any) -> str: + clean = str(raw_path or "/").strip() or "/" + if clean.startswith("/"): + return clean + return clean + + +def _coerce_limit(value: Any, default: int) -> int: + try: + limit = int(value) + except (TypeError, ValueError): + return default + return max(1, min(limit, 200)) + + +def _resource_summary(resource: Any, path_hint: str = "") -> Dict[str, Any]: + path = path_hint or get_content_path(resource) + return { + "id": getattr(resource, "id", getattr(resource, "__name__", None)), + "@type": getattr(resource, "type_name", resource.__class__.__name__), + "title": getattr(resource, "title", None), + "path": path or "/", + } + + +async def _serialize_resource(resource: Any, request: Any) -> Dict[str, Any]: + serializer = query_multi_adapter((resource, request), IResourceSerializeToJsonSummary) + if serializer is None: + serializer = query_multi_adapter((resource, request), IResourceSerializeToJson) + if serializer is None: + return _resource_summary(resource) + return await serializer() + + +async def _resolve_target(context: Any, raw_path: Any) -> Tuple[Any, str]: + clean = _normalize_path(raw_path) + if clean in ("", "/"): + container = get_current_container() + if container is None: + raise ValueError("Container is not available in current task vars") + return container, "/" + + if clean.startswith("/"): + container = get_current_container() + if container is None: + raise ValueError("Container is not available in current task vars") + return await navigate_to(container, clean), clean + + return await navigate_to(context, clean), clean + + +async def resolve_path_tool(context: Any, request: Any, arguments: Dict[str, Any]) -> Dict[str, Any]: + target, resolved_path = await _resolve_target(context, arguments.get("path", "/")) + result = { + "path": resolved_path, + "resource": _resource_summary(target, get_content_path(target)), + } + if bool(arguments.get("include_serialized", False)): + result["serialized"] = await _serialize_resource(target, request) + return result + + +async def list_children_tool( + context: Any, request: Any, arguments: Dict[str, Any], default_limit: int = 50 +) -> Dict[str, Any]: + target, resolved_path = await _resolve_target(context, arguments.get("path", "/")) + if not hasattr(target, "async_items"): + raise ValueError("Target path does not point to a folder-like resource") + + limit = _coerce_limit(arguments.get("limit", default_limit), default_limit) + include_serialized = bool(arguments.get("include_serialized", False)) + + items: List[Dict[str, Any]] = [] + truncated = False + async for _, child in target.async_items(): + if len(items) >= limit: + truncated = True + break + item_summary = _resource_summary(child, get_content_path(child)) + if include_serialized: + item_summary["serialized"] = await _serialize_resource(child, request) + items.append(item_summary) + + return { + "path": resolved_path, + "limit": limit, + "items_total": len(items), + "truncated": truncated, + "items": items, + } + + +async def serialize_resource_tool(context: Any, request: Any, arguments: Dict[str, Any]) -> Dict[str, Any]: + target, resolved_path = await _resolve_target(context, arguments.get("path", "/")) + return {"path": resolved_path, "serialized": await _serialize_resource(target, request)} + + +async def notify_modified_tool(context: Any, request: Any, arguments: Dict[str, Any]) -> Dict[str, Any]: + target, resolved_path = await _resolve_target(context, arguments.get("path")) + payload = arguments.get("payload", {}) + if not isinstance(payload, dict): + raise ValueError("Tool argument 'payload' must be an object") + await notify(ObjectModifiedEvent(target, payload=payload)) + return {"path": resolved_path, "notified": True, "payload_keys": sorted(payload.keys())} + + +def default_tools(default_child_limit: int = 50) -> List[Tuple[str, str, Dict[str, Any], ToolHandler, bool]]: + return [ + ( + "resolve_path", + "Resolve a Guillotina path and return basic resource metadata.", + RESOLVE_PATH_SCHEMA, + resolve_path_tool, + True, + ), + ( + "list_children", + "List child resources from a folder-like Guillotina resource.", + LIST_CHILDREN_SCHEMA, + functools.partial(list_children_tool, default_limit=default_child_limit), + True, + ), + ( + "serialize_resource", + "Serialize a Guillotina resource using the registered serializers.", + SERIALIZE_RESOURCE_SCHEMA, + serialize_resource_tool, + False, + ), + ( + "notify_modified", + "Emit an ObjectModifiedEvent for a resource path, triggering subscribers.", + NOTIFY_MODIFIED_SCHEMA, + notify_modified_tool, + False, + ), + ] diff --git a/guillotina/tests/mcp/test_mcp.py b/guillotina/tests/mcp/test_mcp.py new file mode 100644 index 000000000..217df2e66 --- /dev/null +++ b/guillotina/tests/mcp/test_mcp.py @@ -0,0 +1,80 @@ +import json +import pytest + + +pytestmark = pytest.mark.asyncio + + +MCP_SETTINGS = { + "applications": ["guillotina", "guillotina.contrib.mcp"], +} + + +@pytest.mark.app_settings(MCP_SETTINGS) +async def test_mcp_tools_list(container_requester): + async with container_requester as requester: + response, status = await requester("GET", "/db/guillotina/@mcp/tools") + assert status == 200 + names = {tool["name"] for tool in response["tools"]} + assert "resolve_path" in names + assert "list_children" in names + assert "serialize_resource" in names + assert "notify_modified" in names + + +@pytest.mark.app_settings(MCP_SETTINGS) +async def test_invoke_resolve_path_tool(container_requester): + async with container_requester as requester: + payload = {"tool": "resolve_path", "arguments": {"path": "/"}} + response, status = await requester( + "POST", "/db/guillotina/@mcp/tools/invoke", data=json.dumps(payload) + ) + assert status == 200 + assert response["tool"] == "resolve_path" + assert response["result"]["resource"]["@type"] == "Container" + assert response["result"]["path"] == "/" + + +@pytest.mark.app_settings(MCP_SETTINGS) +async def test_list_children_tool_returns_newly_created_item(container_requester): + async with container_requester as requester: + _, status = await requester( + "POST", "/db/guillotina", data=json.dumps({"@type": "Item", "id": "item-mcp"}) + ) + assert status == 201 + + payload = {"tool": "list_children", "arguments": {"path": "/", "limit": 20}} + response, status = await requester( + "POST", "/db/guillotina/@mcp/tools/invoke", data=json.dumps(payload) + ) + assert status == 200 + ids = {item["id"] for item in response["result"]["items"]} + assert "item-mcp" in ids + + +@pytest.mark.app_settings(MCP_SETTINGS) +async def test_cache_revision_is_bumped_by_subscriber(container_requester): + async with container_requester as requester: + response, status = await requester("GET", "/db/guillotina/@mcp") + assert status == 200 + before = response["mcp"]["cache_revision"] + + _, status = await requester( + "POST", "/db/guillotina", data=json.dumps({"@type": "Item", "id": "item-cache"}) + ) + assert status == 201 + + response, status = await requester("GET", "/db/guillotina/@mcp") + assert status == 200 + assert response["mcp"]["cache_revision"] > before + + +@pytest.mark.app_settings(MCP_SETTINGS) +async def test_invoke_unknown_tool_returns_400(container_requester): + async with container_requester as requester: + payload = {"tool": "does-not-exist", "arguments": {}} + response, status = await requester( + "POST", "/db/guillotina/@mcp/tools/invoke", data=json.dumps(payload) + ) + assert status == 400 + assert "Unknown MCP tool" in response["reason"] diff --git a/setup.py b/setup.py index f2be4575d..2308fdb99 100644 --- a/setup.py +++ b/setup.py @@ -111,6 +111,7 @@ "memcached": ["emcache"], "validation": ["pytz==2020.1"], "recaptcha": ["aiohttp<4"], + "mcp": ["mcp>=1.0.0"], }, entry_points={ "console_scripts": [ From 54e355ed94b708def0f9f8b022710b3f14fefcca Mon Sep 17 00:00:00 2001 From: nil Date: Fri, 27 Feb 2026 13:06:03 +0100 Subject: [PATCH 02/31] Use catalog-first lookup in MCP list_children tool --- CHANGELOG.rst | 2 + docs/source/contrib/mcp.md | 2 + guillotina/contrib/mcp/tools.py | 93 ++++++++++++++++++++++++++++++++ guillotina/tests/mcp/test_mcp.py | 35 ++++++++++++ 4 files changed, 132 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3a62a4ef3..be820e8b6 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -7,6 +7,8 @@ CHANGELOG - Add `guillotina.contrib.mcp` with low-level MCP server integration (`mcp.server.lowlevel`), tool registry utility, MCP services, cache invalidation subscribers, and tests/docs coverage. +- Optimize MCP `list_children` tool to prefer catalog queries and + fallback to `async_items` when catalog is unavailable. - Docs: Update documentation and configuration settings - Chore: Update sphinx-guillotina-theme version to 1.0.9 [rboixaderg] diff --git a/docs/source/contrib/mcp.md b/docs/source/contrib/mcp.md index be729f5b3..58cf16a2f 100644 --- a/docs/source/contrib/mcp.md +++ b/docs/source/contrib/mcp.md @@ -40,3 +40,5 @@ mcp: The tool registry is implemented as a Guillotina utility and cache invalidation is handled by subscribers on object add/modify/remove events. +`list_children` prefers catalog-backed lookup when a catalog utility is +available and falls back to `async_items()` iteration when it is not. diff --git a/guillotina/contrib/mcp/tools.py b/guillotina/contrib/mcp/tools.py index ec1292665..87aa8dffa 100644 --- a/guillotina/contrib/mcp/tools.py +++ b/guillotina/contrib/mcp/tools.py @@ -1,8 +1,12 @@ +from guillotina.catalog.catalog import DefaultSearchUtility +from guillotina.component import query_utility from guillotina.component import query_multi_adapter from guillotina.event import notify from guillotina.events import ObjectModifiedEvent from guillotina.interfaces import IResourceSerializeToJson from guillotina.interfaces import IResourceSerializeToJsonSummary +from guillotina.interfaces.catalog import ICatalogUtility +from guillotina.utils import get_content_depth from guillotina.utils import get_content_path from guillotina.utils import get_current_container from guillotina.utils import navigate_to @@ -125,6 +129,95 @@ async def list_children_tool( limit = _coerce_limit(arguments.get("limit", default_limit), default_limit) include_serialized = bool(arguments.get("include_serialized", False)) + catalog_result = await _list_children_from_catalog( + target=target, + request=request, + resolved_path=resolved_path, + limit=limit, + include_serialized=include_serialized, + ) + if catalog_result is not None: + return catalog_result + + return await _list_children_from_async_items( + target=target, + request=request, + resolved_path=resolved_path, + limit=limit, + include_serialized=include_serialized, + ) + + +def _get_catalog_utility(): + catalog = query_utility(ICatalogUtility) + if catalog is None or catalog.__class__ == DefaultSearchUtility: + return None + return catalog + + +def _child_prefix(path: str) -> str: + if path in ("", "/"): + return "/" + return path.rstrip("/") + "/" + + +async def _serialize_from_catalog_path(path: str, request: Any) -> Dict[str, Any]: + container = get_current_container() + if container is None: + raise ValueError("Container is not available in current task vars") + resource = await navigate_to(container, path) + return await _serialize_resource(resource, request) + + +def _summary_from_catalog_hit(hit: Dict[str, Any], fallback_path: str = "") -> Dict[str, Any]: + return { + "id": hit.get("id", hit.get("@name")), + "@type": hit.get("type_name", hit.get("@type", "Resource")), + "title": hit.get("title"), + "path": hit.get("path", fallback_path or ""), + } + + +async def _list_children_from_catalog( + *, target: Any, request: Any, resolved_path: str, limit: int, include_serialized: bool +) -> Any: + catalog = _get_catalog_utility() + if catalog is None: + return None + + query = { + "path__starts": _child_prefix(resolved_path), + "depth": str(get_content_depth(target) + 1), + "_size": str(limit + 1), + "_sort_asc": "id", + "_metadata": "id,type_name,title,path", + } + result = await catalog.search(target, query) + hits = list(result.get("items", [])) + truncated = bool(result.get("items_total", len(hits)) > limit or len(hits) > limit) + hits = hits[:limit] + + items: List[Dict[str, Any]] = [] + for hit in hits: + item_summary = _summary_from_catalog_hit(hit) + if include_serialized: + path = item_summary.get("path", "") + if isinstance(path, str) and path: + item_summary["serialized"] = await _serialize_from_catalog_path(path, request) + items.append(item_summary) + + return { + "path": resolved_path, + "limit": limit, + "items_total": len(items), + "truncated": truncated, + "items": items, + } + + +async def _list_children_from_async_items( + *, target: Any, request: Any, resolved_path: str, limit: int, include_serialized: bool +) -> Dict[str, Any]: items: List[Dict[str, Any]] = [] truncated = False async for _, child in target.async_items(): diff --git a/guillotina/tests/mcp/test_mcp.py b/guillotina/tests/mcp/test_mcp.py index 217df2e66..d7ba6e700 100644 --- a/guillotina/tests/mcp/test_mcp.py +++ b/guillotina/tests/mcp/test_mcp.py @@ -1,6 +1,8 @@ import json import pytest +from guillotina.contrib.mcp import tools as mcp_tools + pytestmark = pytest.mark.asyncio @@ -78,3 +80,36 @@ async def test_invoke_unknown_tool_returns_400(container_requester): ) assert status == 400 assert "Unknown MCP tool" in response["reason"] + + +@pytest.mark.app_settings(MCP_SETTINGS) +async def test_list_children_uses_catalog_when_available(container_requester, monkeypatch): + class FakeCatalog: + async def search(self, context, query): + assert query["path__starts"] == "/" + return { + "items": [ + { + "id": "catalog-child", + "type_name": "Item", + "title": "From catalog", + "path": "/catalog-child", + } + ], + "items_total": 1, + } + + async def fail_async_items(**kwargs): + raise AssertionError("async_items fallback should not be used when catalog utility is available") + + monkeypatch.setattr(mcp_tools, "query_utility", lambda iface: FakeCatalog()) + monkeypatch.setattr(mcp_tools, "_list_children_from_async_items", fail_async_items) + + async with container_requester as requester: + payload = {"tool": "list_children", "arguments": {"path": "/", "limit": 20}} + response, status = await requester( + "POST", "/db/guillotina/@mcp/tools/invoke", data=json.dumps(payload) + ) + assert status == 200 + assert response["result"]["items_total"] == 1 + assert response["result"]["items"][0]["id"] == "catalog-child" From a3d2f5b8adbf66990035900a7c720bc173cd509d Mon Sep 17 00:00:00 2001 From: Xavier Date: Wed, 4 Mar 2026 12:24:57 +0100 Subject: [PATCH 03/31] Added pagination to mcp list children. --- guillotina/contrib/mcp/tools.py | 23 +++++++++++++++------- guillotina/tests/mcp/test_mcp.py | 33 ++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/guillotina/contrib/mcp/tools.py b/guillotina/contrib/mcp/tools.py index ec1292665..4539c1520 100644 --- a/guillotina/contrib/mcp/tools.py +++ b/guillotina/contrib/mcp/tools.py @@ -32,6 +32,7 @@ "properties": { "path": {"type": "string", "description": "Absolute or relative Guillotina path", "default": "/"}, "limit": {"type": "integer", "minimum": 1, "maximum": 200, "default": 50}, + "page": {"type": "integer", "minimum": 1, "default": 1}, "include_serialized": {"type": "boolean", "default": False}, }, } @@ -79,9 +80,9 @@ def _resource_summary(resource: Any, path_hint: str = "") -> Dict[str, Any]: async def _serialize_resource(resource: Any, request: Any) -> Dict[str, Any]: - serializer = query_multi_adapter((resource, request), IResourceSerializeToJsonSummary) + serializer = query_multi_adapter((resource, request), IResourceSerializeToJson) if serializer is None: - serializer = query_multi_adapter((resource, request), IResourceSerializeToJson) + serializer = query_multi_adapter((resource, request), IResourceSerializeToJsonSummary) if serializer is None: return _resource_summary(resource) return await serializer() @@ -123,22 +124,30 @@ async def list_children_tool( raise ValueError("Target path does not point to a folder-like resource") limit = _coerce_limit(arguments.get("limit", default_limit), default_limit) + page = max(1, int(arguments.get("page", 1))) include_serialized = bool(arguments.get("include_serialized", False)) items: List[Dict[str, Any]] = [] truncated = False + count = 0 + start_index = (page - 1) * limit + end_index = page * limit + async for _, child in target.async_items(): - if len(items) >= limit: + if count >= start_index and count < end_index: + item_summary = _resource_summary(child, get_content_path(child)) + if include_serialized: + item_summary["serialized"] = await _serialize_resource(child, request) + items.append(item_summary) + elif count >= end_index: truncated = True break - item_summary = _resource_summary(child, get_content_path(child)) - if include_serialized: - item_summary["serialized"] = await _serialize_resource(child, request) - items.append(item_summary) + count += 1 return { "path": resolved_path, "limit": limit, + "page": page, "items_total": len(items), "truncated": truncated, "items": items, diff --git a/guillotina/tests/mcp/test_mcp.py b/guillotina/tests/mcp/test_mcp.py index 217df2e66..54e691eb5 100644 --- a/guillotina/tests/mcp/test_mcp.py +++ b/guillotina/tests/mcp/test_mcp.py @@ -78,3 +78,36 @@ async def test_invoke_unknown_tool_returns_400(container_requester): ) assert status == 400 assert "Unknown MCP tool" in response["reason"] + + +@pytest.mark.app_settings(MCP_SETTINGS) +async def test_list_children_tool_pagination(container_requester): + async with container_requester as requester: + for i in range(1, 106): + await requester( + "POST", + "/db/guillotina", + data=json.dumps({"@type": "Item", "id": f"item-{i}"}) + ) + payload = {"tool": "list_children", "arguments": {"path": "/", "limit": 50, "page": 1}} + response, status = await requester( + "POST", + "/db/guillotina/@mcp/tools/invoke", + data=json.dumps(payload) + ) + assert status == 200 + assert response["result"]["limit"] == 50 + assert response["result"]["page"] == 1 + assert response["result"]["truncated"] is True + assert len(response["result"]["items"]) == 50 + + payload["arguments"]["page"] = 3 + response, status = await requester( + "POST", + "/db/guillotina/@mcp/tools/invoke", + data=json.dumps(payload) + ) + assert status == 200 + assert response["result"]["page"] == 3 + assert response["result"]["truncated"] is False + assert len(response["result"]["items"]) == 5 From 5693ff41750e82356eb4cba1835f565a14705d35 Mon Sep 17 00:00:00 2001 From: Xavier Date: Wed, 4 Mar 2026 14:07:56 +0100 Subject: [PATCH 04/31] - Seach inside catalog --- guillotina/contrib/mcp/tools.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/guillotina/contrib/mcp/tools.py b/guillotina/contrib/mcp/tools.py index 0fff801de..eacadb4e7 100644 --- a/guillotina/contrib/mcp/tools.py +++ b/guillotina/contrib/mcp/tools.py @@ -57,6 +57,14 @@ "required": ["path"], } +SEARCH_SCHEMA = { + "type": "object", + "properties": { + "query": {"type": "object", "description": "Guillotina catalog query object"}, + }, + "required": ["query"], +} + def _normalize_path(raw_path: Any) -> str: clean = str(raw_path or "/").strip() or "/" @@ -146,6 +154,7 @@ async def list_children_tool( request=request, resolved_path=resolved_path, limit=limit, + page=page, include_serialized=include_serialized, ) @@ -218,7 +227,7 @@ async def _list_children_from_catalog( async def _list_children_from_async_items( - *, target: Any, request: Any, resolved_path: str, limit: int, include_serialized: bool + *, target: Any, request: Any, resolved_path: str, limit: int, page: int, include_serialized: bool ) -> Dict[str, Any]: items: List[Dict[str, Any]] = [] truncated = False @@ -261,6 +270,15 @@ async def notify_modified_tool(context: Any, request: Any, arguments: Dict[str, return {"path": resolved_path, "notified": True, "payload_keys": sorted(payload.keys())} +async def search_tool(context: Any, request: Any, arguments: Dict[str, Any]) -> Dict[str, Any]: + catalog = _get_catalog_utility() + if catalog is None: + raise ValueError("Catalog utility is not available") + query = arguments.get("query", {}) + result = await catalog.search(context, query) + return {"query": query, "result": result} + + def default_tools(default_child_limit: int = 50) -> List[Tuple[str, str, Dict[str, Any], ToolHandler, bool]]: return [ ( @@ -291,4 +309,11 @@ def default_tools(default_child_limit: int = 50) -> List[Tuple[str, str, Dict[st notify_modified_tool, False, ), + ( + "search", + "Search for resources using the catalog.", + SEARCH_SCHEMA, + search_tool, + True, + ), ] From ec97b135f37e4b5f5a1be6ed27f2b4c922a25848 Mon Sep 17 00:00:00 2001 From: Xavier Date: Thu, 5 Mar 2026 12:53:50 +0100 Subject: [PATCH 05/31] Added mcp/resources (also solved isort, black and flake8) --- guillotina/contrib/mcp/backend.py | 68 +++++++++ guillotina/contrib/mcp/interfaces.py | 6 + guillotina/contrib/mcp/resources.py | 202 +++++++++++++++++++++++++++ guillotina/contrib/mcp/services.py | 91 ++++++++---- guillotina/contrib/mcp/tools.py | 3 +- guillotina/tests/mcp/test_mcp.py | 129 +++++++++++++++-- 6 files changed, 456 insertions(+), 43 deletions(-) create mode 100644 guillotina/contrib/mcp/resources.py diff --git a/guillotina/contrib/mcp/backend.py b/guillotina/contrib/mcp/backend.py index 4e19a91d7..b35f8fea7 100644 --- a/guillotina/contrib/mcp/backend.py +++ b/guillotina/contrib/mcp/backend.py @@ -1,5 +1,6 @@ from dataclasses import dataclass from guillotina import app_settings +from guillotina.contrib.mcp import resources as mcp_resources from guillotina.contrib.mcp import tools from typing import Any from typing import Awaitable @@ -12,6 +13,7 @@ ToolHandler = Callable[[Any, Any, Dict[str, Any]], Awaitable[Dict[str, Any]]] +ResourceHandler = Callable[[Any], Awaitable[Dict[str, Any]]] @dataclass @@ -23,6 +25,16 @@ class MCPTool: cacheable: bool = False +@dataclass +class MCPResource: + name: str + uri: str + description: str + endpoint: str + handler: ResourceHandler + mime_type: str = "application/json" + + class MCPToolRegistry: def __init__(self, settings: Optional[Dict[str, Any]] = None): config = app_settings.get("mcp", {}) @@ -31,9 +43,11 @@ def __init__(self, settings: Optional[Dict[str, Any]] = None): self._server_name = str(config.get("server_name", "guillotina-mcp")) self._default_child_limit = int(config.get("default_child_limit", 50)) self._tools: Dict[str, MCPTool] = {} + self._resources: Dict[str, MCPResource] = {} self._cache: Dict[str, Dict[str, Any]] = {} self._cache_revision = 0 self._register_default_tools() + self._register_default_resources() def _register_default_tools(self) -> None: for tool_name, description, input_schema, handler, cacheable in tools.default_tools( @@ -47,6 +61,16 @@ def _register_default_tools(self) -> None: cacheable=cacheable, ) + def _register_default_resources(self) -> None: + for name, uri, description, endpoint, handler in mcp_resources.default_resources(): + self.register_resource( + name=name, + uri=uri, + description=description, + endpoint=endpoint, + handler=handler, + ) + def is_enabled(self) -> bool: return self._enabled @@ -81,6 +105,49 @@ def list_tools(self) -> List[Dict[str, Any]]: for tool in sorted(self._tools.values(), key=lambda registered: registered.name) ] + # ── Resource management ────────────────────────────────────────── + + def register_resource( + self, + *, + name: str, + uri: str, + description: str, + endpoint: str, + handler: ResourceHandler, + mime_type: str = "application/json", + ) -> None: + clean_name = str(name or "").strip() + if not clean_name: + raise ValueError("Resource name is required") + self._resources[clean_name] = MCPResource( + name=clean_name, + uri=str(uri or "").strip(), + description=str(description or "").strip(), + endpoint=str(endpoint or "").strip(), + handler=handler, + mime_type=mime_type, + ) + + def list_resources(self) -> List[Dict[str, Any]]: + return [ + { + "uri": res.uri, + "name": res.name, + "description": res.description, + "endpoint": res.endpoint, + "mimeType": res.mime_type, + } + for res in sorted(self._resources.values(), key=lambda r: r.name) + ] + + async def read_resource(self, resource_name: str, context: Any, request: Any) -> Dict[str, Any]: + clean_name = str(resource_name or "").strip() + if clean_name not in self._resources: + raise ValueError(f"Unknown MCP resource: {resource_name}") + resource = self._resources[clean_name] + return await resource.handler(request) + def _cache_key(self, tool_name: str, arguments: Dict[str, Any]) -> str: payload = json.dumps(arguments, sort_keys=True, separators=(",", ":"), default=str) return f"{tool_name}:{payload}" @@ -115,6 +182,7 @@ def metadata(self) -> Dict[str, Any]: "enabled": self.is_enabled(), "server_name": self._server_name, "tool_count": len(self._tools), + "resource_count": len(self._resources), "cache_revision": self._cache_revision, } diff --git a/guillotina/contrib/mcp/interfaces.py b/guillotina/contrib/mcp/interfaces.py index ab7db107b..e38b86e54 100644 --- a/guillotina/contrib/mcp/interfaces.py +++ b/guillotina/contrib/mcp/interfaces.py @@ -12,9 +12,15 @@ class IMCPToolRegistry(Interface): def list_tools(): """Return registered MCP tools.""" + def list_resources(): + """Return registered MCP resources.""" + async def invoke(tool_name, context, request, arguments=None): """Execute one tool and return a JSON-serializable response.""" + async def read_resource(resource_name, context, request): + """Read one resource and return a JSON-serializable response.""" + def metadata(): """Return metadata for diagnostics.""" diff --git a/guillotina/contrib/mcp/resources.py b/guillotina/contrib/mcp/resources.py new file mode 100644 index 000000000..3df23948e --- /dev/null +++ b/guillotina/contrib/mcp/resources.py @@ -0,0 +1,202 @@ +from guillotina import __version__ +from guillotina import app_settings +from guillotina.component import query_utility +from guillotina.interfaces.catalog import ICatalogUtility +from guillotina.transactions import get_transaction +from guillotina.utils import get_content_path +from guillotina.utils import get_current_container +from guillotina.utils import navigate_to +from typing import Any +from typing import Dict + + +async def mcp_info_resource(request) -> Dict[str, Any]: + container = get_current_container() + return { + "version": __version__, + "container_id": getattr(container, "id", None) if container else None, + "container_path": get_content_path(container) if container else None, + "enabled_addons": getattr(container, "addons", []), + } + + +async def mcp_health_resource(request) -> Dict[str, Any]: + db_status = "unknown" + try: + txn = get_transaction() + if txn and txn.storage: + conn = await txn.get_connection() + if conn: + db_status = "ok" + except Exception: + db_status = "error" + + return { + "status": "ok" if db_status == "ok" else "degraded", + "db": db_status, + "cache": "ok", # stub - extend if cache utility is available + } + + +async def mcp_config_resource(request) -> Dict[str, Any]: + mcp_settings = app_settings.get("mcp", {}) + return { + "mcp": { + "enabled": mcp_settings.get("enabled", False), + "server_name": mcp_settings.get("server_name", "guillotina-mcp"), + "default_child_limit": mcp_settings.get("default_child_limit", 50), + }, + "applications": app_settings.get("applications", []), + } + + +async def mcp_users_resource(request) -> Dict[str, Any]: + """ + Returns a list of users if dbusers contrib is enabled. + """ + container = get_current_container() + if not container: + return {"error": "No container available"} + + try: + users_folder = await navigate_to(container, "users") + except (KeyError, AttributeError): + return { + "error": "Users folder not found. Ensure guillotina.contrib.dbusers is enabled.", + "users": [], + } + + catalog = query_utility(ICatalogUtility) + if catalog: + try: + query = { + "type_name": "User", + "_metadata": "id,user_name,user_email,user_roles,user_groups", + } + result = await catalog.search(container, query) + users = [] + for hit in result.get("items", []): + users.append( + { + "id": hit.get("id"), + "name": hit.get("user_name"), + "email": hit.get("user_email"), + "roles": hit.get("user_roles", []), + "groups": hit.get("user_groups", []), + } + ) + return {"users": users} + except Exception: + # Catalog search failed, fall back to async_items + pass + + users = [] + try: + async for user_id, user in users_folder.async_items(): + users.append( + { + "id": user_id, + "name": getattr(user, "name", None), + "email": getattr(user, "email", None), + "roles": list(getattr(user, "user_roles", [])), + "groups": list(getattr(user, "user_groups", [])), + } + ) + except Exception as e: + return {"error": f"Failed to list users: {str(e)}", "users": []} + + return {"users": users} + + +async def mcp_catalog_resource(request) -> Dict[str, Any]: + catalog = query_utility(ICatalogUtility) + if catalog is None: + return { + "available": False, + "note": "No catalog utility configured.", + } + + container = get_current_container() + return { + "available": True, + "catalog_type": catalog.__class__.__name__, + "container": getattr(container, "id", None) if container else None, + } + + +async def mcp_summary_resource(request) -> Dict[str, Any]: + path = request.query.get("path", "/") if hasattr(request, "query") else "/" + container = get_current_container() + if not container: + return {"error": "No container available"} + + try: + if path == "/": + resource = container + else: + resource = await navigate_to(container, path) + + summary = { + "path": get_content_path(resource), + "id": getattr(resource, "id", getattr(resource, "__name__", None)), + "@type": getattr(resource, "type_name", resource.__class__.__name__), + "title": getattr(resource, "title", None), + } + + # Add child count if it's a folder-like resource + if hasattr(resource, "async_len"): + try: + summary["children_count"] = await resource.async_len() + except Exception: + pass + + return summary + except (KeyError, AttributeError) as e: + return {"error": f"Resource not found at path: {path}", "details": str(e)} + + +def default_resources(): + return [ + ( + "info", + "guillotina://resources/info", + "Guillotina version, container id and enabled add-ons.", + "@mcp/resources/info", + mcp_info_resource, + ), + ( + "health", + "guillotina://resources/health", + "Database and cache health status.", + "@mcp/resources/health", + mcp_health_resource, + ), + ( + "config", + "guillotina://resources/config", + "MCP settings and loaded applications.", + "@mcp/resources/config", + mcp_config_resource, + ), + ( + "users", + "guillotina://resources/users", + "List users registered in the container (dbusers).", + "@mcp/resources/users", + mcp_users_resource, + ), + ( + "catalog", + "guillotina://resources/catalog", + "Catalog availability and type info.", + "@mcp/resources/catalog", + mcp_catalog_resource, + ), + ( + "summary", + "guillotina://resources/summary", + "Summary of a resource at a given path (accepts ?path=/).", + "@mcp/resources/summary", + mcp_summary_resource, + ), + ] diff --git a/guillotina/contrib/mcp/services.py b/guillotina/contrib/mcp/services.py index ce2ec3eb5..352e4b0d6 100644 --- a/guillotina/contrib/mcp/services.py +++ b/guillotina/contrib/mcp/services.py @@ -4,6 +4,7 @@ from guillotina.contrib.mcp.interfaces import IMCPToolRegistry from guillotina.interfaces import IResource from guillotina.response import HTTPBadRequest +from guillotina.response import HTTPNotFound from guillotina.response import HTTPServiceUnavailable @@ -25,33 +26,88 @@ def _get_registry(): class MCPInfoService(Service): async def __call__(self): registry = _get_registry() - return {"mcp": registry.metadata(), "tools": registry.list_tools()} + return { + "mcp": registry.metadata(), + "tools": registry.list_tools(), + "resources": registry.list_resources(), + } @configure.service( method="GET", context=IResource, - name="@mcp/tools", + name="@mcp/{action}", permission="guillotina.MCPView", - summary="List available MCP tools", + summary="MCP sub-actions (tools, resources, server)", allow_access=True, ) -class MCPToolsService(Service): +class MCPActionGetService(Service): async def __call__(self): + action = self.request.matchdict.get("action", "") + if action == "tools": + registry = _get_registry() + return {"tools": registry.list_tools()} + if action == "resources": + registry = _get_registry() + return {"resources": registry.list_resources()} + raise HTTPNotFound(content={"reason": f"Unknown MCP action: {action}"}) + + +@configure.service( + method="GET", + context=IResource, + name="@mcp/{action}/{sub}", + permission="guillotina.MCPView", + summary="MCP sub-resource endpoints", + allow_access=True, +) +class MCPSubActionGetService(Service): + async def __call__(self): + action = self.request.matchdict.get("action", "") + sub = self.request.matchdict.get("sub", "") + + if action == "server" and sub == "status": + return await self._server_status() + + if action == "resources": + # Dispatch to the registered resource by name + registry = _get_registry() + try: + return await registry.read_resource(sub, self.context, self.request) + except ValueError: + raise HTTPNotFound(content={"reason": f"Unknown MCP resource: {sub}"}) + + raise HTTPNotFound(content={"reason": f"Unknown MCP endpoint: {action}/{sub}"}) + + async def _server_status(self): registry = _get_registry() - return {"tools": registry.list_tools()} + metadata = registry.metadata() + try: + registry.create_lowlevel_server(context=self.context, request=self.request) + except RuntimeError as exc: + return {"ready": False, "error": str(exc), "mcp": metadata} + return {"ready": True, "mcp": metadata} @configure.service( method="POST", context=IResource, - name="@mcp/tools/invoke", + name="@mcp/{action}/{sub}", permission="guillotina.MCPExecute", - summary="Execute one registered MCP tool", + summary="MCP tool invocation", allow_access=True, ) -class MCPInvokeToolService(Service): +class MCPSubActionPostService(Service): async def __call__(self): + action = self.request.matchdict.get("action", "") + sub = self.request.matchdict.get("sub", "") + key = f"{action}/{sub}" + + if key == "tools/invoke": + return await self._invoke_tool() + raise HTTPNotFound(content={"reason": f"Unknown MCP POST endpoint: {key}"}) + + async def _invoke_tool(self): registry = _get_registry() try: payload = await self.request.json() @@ -75,22 +131,3 @@ async def __call__(self): raise HTTPBadRequest(content={"reason": str(exc)}) from exc return {"tool": tool_name, "result": result} - - -@configure.service( - method="GET", - context=IResource, - name="@mcp/server/status", - permission="guillotina.MCPView", - summary="Check whether low-level MCP server dependencies are available", - allow_access=True, -) -class MCPServerStatusService(Service): - async def __call__(self): - registry = _get_registry() - metadata = registry.metadata() - try: - registry.create_lowlevel_server(context=self.context, request=self.request) - except RuntimeError as exc: - return {"ready": False, "error": str(exc), "mcp": metadata} - return {"ready": True, "mcp": metadata} diff --git a/guillotina/contrib/mcp/tools.py b/guillotina/contrib/mcp/tools.py index eacadb4e7..228eaba7a 100644 --- a/guillotina/contrib/mcp/tools.py +++ b/guillotina/contrib/mcp/tools.py @@ -1,6 +1,6 @@ from guillotina.catalog.catalog import DefaultSearchUtility -from guillotina.component import query_utility from guillotina.component import query_multi_adapter +from guillotina.component import query_utility from guillotina.event import notify from guillotina.events import ObjectModifiedEvent from guillotina.interfaces import IResourceSerializeToJson @@ -22,7 +22,6 @@ ToolHandler = Callable[[Any, Any, Dict[str, Any]], Awaitable[Dict[str, Any]]] - RESOLVE_PATH_SCHEMA = { "type": "object", "properties": { diff --git a/guillotina/tests/mcp/test_mcp.py b/guillotina/tests/mcp/test_mcp.py index 14cd18c2b..63023709e 100644 --- a/guillotina/tests/mcp/test_mcp.py +++ b/guillotina/tests/mcp/test_mcp.py @@ -1,12 +1,12 @@ +from guillotina.contrib.mcp import resources as mcp_resources +from guillotina.contrib.mcp import tools as mcp_tools + import json import pytest -from guillotina.contrib.mcp import tools as mcp_tools - pytestmark = pytest.mark.asyncio - MCP_SETTINGS = { "applications": ["guillotina", "guillotina.contrib.mcp"], } @@ -86,16 +86,10 @@ async def test_invoke_unknown_tool_returns_400(container_requester): async def test_list_children_tool_pagination(container_requester): async with container_requester as requester: for i in range(1, 106): - await requester( - "POST", - "/db/guillotina", - data=json.dumps({"@type": "Item", "id": f"item-{i}"}) - ) + await requester("POST", "/db/guillotina", data=json.dumps({"@type": "Item", "id": f"item-{i}"})) payload = {"tool": "list_children", "arguments": {"path": "/", "limit": 50, "page": 1}} response, status = await requester( - "POST", - "/db/guillotina/@mcp/tools/invoke", - data=json.dumps(payload) + "POST", "/db/guillotina/@mcp/tools/invoke", data=json.dumps(payload) ) assert status == 200 assert response["result"]["limit"] == 50 @@ -105,15 +99,14 @@ async def test_list_children_tool_pagination(container_requester): payload["arguments"]["page"] = 3 response, status = await requester( - "POST", - "/db/guillotina/@mcp/tools/invoke", - data=json.dumps(payload) + "POST", "/db/guillotina/@mcp/tools/invoke", data=json.dumps(payload) ) assert status == 200 assert response["result"]["page"] == 3 assert response["result"]["truncated"] is False assert len(response["result"]["items"]) == 5 + @pytest.mark.app_settings(MCP_SETTINGS) async def test_list_children_uses_catalog_when_available(container_requester, monkeypatch): class FakeCatalog: @@ -145,3 +138,111 @@ async def fail_async_items(**kwargs): assert status == 200 assert response["result"]["items_total"] == 1 assert response["result"]["items"][0]["id"] == "catalog-child" + + +@pytest.mark.app_settings(MCP_SETTINGS) +async def test_mcp_root_lists_tools_and_resources(container_requester): + async with container_requester as requester: + response, status = await requester("GET", "/db/guillotina/@mcp") + assert status == 200 + assert "mcp" in response + assert "tools" in response + assert "resources" in response + assert isinstance(response["resources"], list) + assert len(response["resources"]) > 0 + # Every resource must expose discovery fields + for res in response["resources"]: + assert "uri" in res + assert "name" in res + assert "endpoint" in res + + +@pytest.mark.app_settings(MCP_SETTINGS) +async def test_mcp_resources_listing(container_requester): + async with container_requester as requester: + response, status = await requester("GET", "/db/guillotina/@mcp/resources") + assert status == 200 + names = {r["name"] for r in response["resources"]} + # All default resources must be registered + for expected in ("info", "health", "config", "users", "catalog", "summary"): + assert expected in names, f"Missing resource: {expected}" + + +@pytest.mark.app_settings(MCP_SETTINGS) +async def test_mcp_metadata_includes_resource_count(container_requester): + async with container_requester as requester: + response, status = await requester("GET", "/db/guillotina/@mcp") + assert status == 200 + assert response["mcp"]["resource_count"] == len(response["resources"]) + + +@pytest.mark.app_settings(MCP_SETTINGS) +async def test_resource_info(container_requester): + async with container_requester as requester: + response, status = await requester("GET", "/db/guillotina/@mcp/resources/info") + assert status == 200 + assert "version" in response + assert "container_id" in response + + +@pytest.mark.app_settings(MCP_SETTINGS) +async def test_resource_health(container_requester): + async with container_requester as requester: + response, status = await requester("GET", "/db/guillotina/@mcp/resources/health") + assert status == 200 + assert response["status"] in ("ok", "degraded") + assert "db" in response + + +@pytest.mark.app_settings(MCP_SETTINGS) +async def test_resource_config(container_requester): + async with container_requester as requester: + response, status = await requester("GET", "/db/guillotina/@mcp/resources/config") + assert status == 200 + assert "mcp" in response + assert response["mcp"]["enabled"] is True + assert "applications" in response + + +@pytest.mark.app_settings(MCP_SETTINGS) +async def test_resource_catalog(container_requester): + async with container_requester as requester: + response, status = await requester("GET", "/db/guillotina/@mcp/resources/catalog") + assert status == 200 + assert "available" in response + + +@pytest.mark.app_settings(MCP_SETTINGS) +async def test_resource_summary(container_requester): + async with container_requester as requester: + response, status = await requester("GET", "/db/guillotina/@mcp/resources/summary?path=/") + assert status == 200 + assert response["@type"] == "Container" + assert "path" in response + + +@pytest.mark.app_settings(MCP_SETTINGS) +async def test_unknown_resource_returns_404(container_requester): + async with container_requester as requester: + response, status = await requester("GET", "/db/guillotina/@mcp/resources/nonexistent") + assert status == 404 + + +@pytest.mark.app_settings(MCP_SETTINGS) +async def test_server_status(container_requester): + async with container_requester as requester: + response, status = await requester("GET", "/db/guillotina/@mcp/server/status") + assert status == 200 + assert "ready" in response + assert "mcp" in response + + +@pytest.mark.app_settings(MCP_SETTINGS) +async def test_resource_registry_matches_default_resources(container_requester): + """The registry must contain exactly the resources declared in default_resources().""" + async with container_requester as requester: + response, status = await requester("GET", "/db/guillotina/@mcp/resources") + assert status == 200 + registered_names = {r["name"] for r in response["resources"]} + default_names = {name for name, *_ in mcp_resources.default_resources()} + assert registered_names == default_names From 6a4ef4e981b0fd76999fb1f326e73d1cdb9a92b8 Mon Sep 17 00:00:00 2001 From: Xavier Date: Fri, 6 Mar 2026 10:30:16 +0100 Subject: [PATCH 06/31] limit mcp to python >= 3.10 --- contrib-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib-requirements.txt b/contrib-requirements.txt index babdae365..5b9666c16 100644 --- a/contrib-requirements.txt +++ b/contrib-requirements.txt @@ -13,7 +13,7 @@ MarkupSafe<2.1.0 pytz==2020.1 emcache==0.6.0; python_version < '3.10' pymemcache==3.4.0; python_version < '3.10' -mcp>=1.0.0 +mcp>=1.0.0; python_version >= '3.10' # Conditional Pillow versions pillow==10.4.0; python_version < '3.11' From d73f294c4e2cb23f504790e089aa08cdc714a4cb Mon Sep 17 00:00:00 2001 From: Xavier Date: Fri, 6 Mar 2026 13:02:33 +0100 Subject: [PATCH 07/31] Changes to help mcp discovery and use. --- guillotina/contrib/mcp/server.py | 41 +++++++++ guillotina/contrib/mcp/services.py | 128 ++++++++++++++++++++++++++++- guillotina/contrib/mcp/tools.py | 30 +++++-- 3 files changed, 192 insertions(+), 7 deletions(-) diff --git a/guillotina/contrib/mcp/server.py b/guillotina/contrib/mcp/server.py index 57725ddfd..ac4a1ceb8 100644 --- a/guillotina/contrib/mcp/server.py +++ b/guillotina/contrib/mcp/server.py @@ -37,6 +37,29 @@ def _build_tool_type(self, types_module: Any, tool_data: Any) -> Any: input_schema=tool_data["inputSchema"], ) + def _build_resource_type(self, types_module: Any, resource_data: Any) -> Any: + from pydantic import AnyUrl + + uri_str = resource_data["uri"] + try: + uri = AnyUrl(uri_str) + except Exception: + uri = uri_str # type: ignore[assignment] + try: + return types_module.Resource( + uri=uri, + name=resource_data["name"], + description=resource_data.get("description", ""), + mimeType=resource_data.get("mimeType", "application/json"), + ) + except TypeError: + return types_module.Resource( + uri=uri, + name=resource_data["name"], + description=resource_data.get("description", ""), + mime_type=resource_data.get("mimeType", "application/json"), + ) + def _build_text_content_type(self, types_module: Any, text: str) -> Any: try: return types_module.TextContent(type="text", text=text) @@ -53,6 +76,24 @@ async def handle_list_tools(): self._build_tool_type(types_module, tool_data) for tool_data in self.registry.list_tools() ] + @server.list_resources() + async def handle_list_resources(): + return [ + self._build_resource_type(types_module, res_data) + for res_data in self.registry.list_resources() + ] + + @server.read_resource() + async def handle_read_resource(uri: Any) -> str: + if self.context is None or self.request is None: + raise ValueError("Context and request are required to read Guillotina MCP resources") + uri_str = str(uri) + for res in self.registry.list_resources(): + if res["uri"] == uri_str: + data = await self.registry.read_resource(res["name"], self.context, self.request) + return json.dumps(data, ensure_ascii=True, sort_keys=True, default=str) + raise ValueError(f"Unknown resource URI: {uri}") + @server.call_tool() async def handle_call_tool(name: str, arguments: Any): if self.context is None or self.request is None: diff --git a/guillotina/contrib/mcp/services.py b/guillotina/contrib/mcp/services.py index 352e4b0d6..53f6cff42 100644 --- a/guillotina/contrib/mcp/services.py +++ b/guillotina/contrib/mcp/services.py @@ -6,6 +6,7 @@ from guillotina.response import HTTPBadRequest from guillotina.response import HTTPNotFound from guillotina.response import HTTPServiceUnavailable +from guillotina.response import Response def _get_registry(): @@ -26,10 +27,42 @@ def _get_registry(): class MCPInfoService(Service): async def __call__(self): registry = _get_registry() + base = self.request.path.rstrip("/") return { "mcp": registry.metadata(), "tools": registry.list_tools(), "resources": registry.list_resources(), + "usage": { + "protocol": { + "description": ( + "Native MCP Streamable HTTP endpoint (JSON-RPC 2.0). " + "Send initialize then tools/call or resources/read." + ), + "method": "POST", + "url": f"{base}/protocol", + "headers": { + "Content-Type": "application/json", + "Accept": "application/json, text/event-stream", + }, + "body_example": { + "jsonrpc": "2.0", + "id": 1, + "method": "tools/list", + "params": {}, + }, + }, + "invoke_tool": { + "method": "POST", + "url": f"{base}/tools/invoke", + "body": {"tool": "", "arguments": {}}, + "description": "Guillotina-specific REST shortcut to invoke a tool directly.", + }, + "read_resource": { + "method": "GET", + "url": f"{base}/resources/", + "description": "GET this URL replacing with a name from the resources list.", + }, + }, } @@ -46,10 +79,44 @@ async def __call__(self): action = self.request.matchdict.get("action", "") if action == "tools": registry = _get_registry() - return {"tools": registry.list_tools()} + base = self.request.path.rstrip("/").rsplit("/", 1)[0] + return { + "tools": registry.list_tools(), + "invoke": { + "method": "POST", + "url": f"{base}/tools/invoke", + "body": {"tool": "", "arguments": {}}, + }, + } if action == "resources": registry = _get_registry() - return {"resources": registry.list_resources()} + base = self.request.path.rstrip("/").rsplit("/", 1)[0] + return { + "resources": registry.list_resources(), + "read": { + "method": "GET", + "url": f"{base}/resources/", + "description": "Replace with the 'name' field of any listed resource.", + }, + } + if action == "protocol": + base = self.request.path.rstrip("/") + return { + "description": "MCP Streamable HTTP endpoint (JSON-RPC 2.0, MCP spec compliant).", + "method": "POST", + "url": base, + "headers": { + "Content-Type": "application/json", + "Accept": "application/json, text/event-stream", + }, + "lifecycle": [ + {"step": 1, "method": "initialize", "description": "Handshake and capability negotiation"}, + {"step": 2, "method": "tools/list", "description": "Discover available tools"}, + {"step": 3, "method": "tools/call", "description": "Invoke a tool"}, + {"step": 4, "method": "resources/list", "description": "Discover available resources"}, + {"step": 5, "method": "resources/read", "description": "Read a resource by URI"}, + ], + } raise HTTPNotFound(content={"reason": f"Unknown MCP action: {action}"}) @@ -131,3 +198,60 @@ async def _invoke_tool(self): raise HTTPBadRequest(content={"reason": str(exc)}) from exc return {"tool": tool_name, "result": result} + + +@configure.service( + method="POST", + context=IResource, + name="@mcp/{action}", + permission="guillotina.MCPExecute", + summary="MCP Streamable HTTP protocol endpoint (JSON-RPC 2.0)", + allow_access=True, +) +class MCPActionPostService(Service): + async def __call__(self): + action = self.request.matchdict.get("action", "") + if action == "protocol": + return await self._handle_protocol() + raise HTTPNotFound(content={"reason": f"Unknown MCP POST action: {action}"}) + + async def _handle_protocol(self): + try: + import anyio + from mcp.server.streamable_http import StreamableHTTPServerTransport + except ImportError as exc: + raise HTTPServiceUnavailable( + content={"reason": 'MCP SDK missing. Install "guillotina[mcp]".'} + ) from exc + + registry = _get_registry() + transport = StreamableHTTPServerTransport( + mcp_session_id=None, + is_json_response_enabled=True, + ) + server_obj = registry.create_lowlevel_server(context=self.context, request=self.request) + init_options = server_obj.create_initialization_options() + + async with transport.connect() as (read_stream, write_stream): + async with anyio.create_task_group() as tg: + tg.start_soon( + server_obj.run, + read_stream, + write_stream, + init_options, + False, # raise_exceptions + True, # stateless — allows each request to be handled independently + ) + await transport.handle_request( + self.request.scope, + self.request.receive, + self.request.send, + ) + tg.cancel_scope.cancel() + + # Response was already written directly to the ASGI socket by the transport. + # Return a pre-marked Response so Guillotina does not write an extra response. + resp = Response(status=200) + resp._prepared = True + resp._eof_sent = True + return resp diff --git a/guillotina/contrib/mcp/tools.py b/guillotina/contrib/mcp/tools.py index 228eaba7a..f6a727378 100644 --- a/guillotina/contrib/mcp/tools.py +++ b/guillotina/contrib/mcp/tools.py @@ -34,8 +34,14 @@ "type": "object", "properties": { "path": {"type": "string", "description": "Absolute or relative Guillotina path", "default": "/"}, - "limit": {"type": "integer", "minimum": 1, "maximum": 200, "default": 50}, - "page": {"type": "integer", "minimum": 1, "default": 1}, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 200, + "default": 50, + "description": "Number of children to return per page. Hard cap is 200.", + }, + "page": {"type": "integer", "minimum": 1, "default": 1, "description": "Page number (1-based). Use with limit to paginate."}, "include_serialized": {"type": "boolean", "default": False}, }, } @@ -59,7 +65,16 @@ SEARCH_SCHEMA = { "type": "object", "properties": { - "query": {"type": "object", "description": "Guillotina catalog query object"}, + "query": { + "type": "object", + "description": ( + "Guillotina catalog query object. " + "Supports: type_name, creators, tags, path__startswith, creation_date__gte, etc. " + "Use 'b_size' (max 1000) to set page size and 'b_start' to offset for pagination. " + "Use '_metadata' as a comma-separated list of field names to limit returned fields " + "and reduce response size, e.g. '_metadata': 'id,type_name,title,path'." + ), + }, }, "required": ["query"], } @@ -289,7 +304,7 @@ def default_tools(default_child_limit: int = 50) -> List[Tuple[str, str, Dict[st ), ( "list_children", - "List child resources from a folder-like Guillotina resource.", + "List child resources from a folder-like Guillotina resource. Max 200 per page; use 'page' to paginate.", LIST_CHILDREN_SCHEMA, functools.partial(list_children_tool, default_limit=default_child_limit), True, @@ -310,7 +325,12 @@ def default_tools(default_child_limit: int = 50) -> List[Tuple[str, str, Dict[st ), ( "search", - "Search for resources using the catalog.", + ( + "Search for resources using the Guillotina catalog. " + "Supports filtering by type_name, creators, path, dates, and more. " + "Paginate with 'b_start' (offset) and 'b_size' (page size, max 1000). " + "Limit returned fields with '_metadata' (e.g. 'id,type_name,title,path,creators')." + ), SEARCH_SCHEMA, search_tool, True, From 370c18cdf12e3fb4f7a628b6ecd45f9e1b43125a Mon Sep 17 00:00:00 2001 From: Xavier Date: Fri, 6 Mar 2026 13:13:07 +0100 Subject: [PATCH 08/31] flake --- guillotina/contrib/mcp/services.py | 6 +++++- guillotina/contrib/mcp/tools.py | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/guillotina/contrib/mcp/services.py b/guillotina/contrib/mcp/services.py index 53f6cff42..707426cf5 100644 --- a/guillotina/contrib/mcp/services.py +++ b/guillotina/contrib/mcp/services.py @@ -110,7 +110,11 @@ async def __call__(self): "Accept": "application/json, text/event-stream", }, "lifecycle": [ - {"step": 1, "method": "initialize", "description": "Handshake and capability negotiation"}, + { + "step": 1, + "method": "initialize", + "description": "Handshake and capability negotiation", + }, {"step": 2, "method": "tools/list", "description": "Discover available tools"}, {"step": 3, "method": "tools/call", "description": "Invoke a tool"}, {"step": 4, "method": "resources/list", "description": "Discover available resources"}, diff --git a/guillotina/contrib/mcp/tools.py b/guillotina/contrib/mcp/tools.py index f6a727378..30dbfe365 100644 --- a/guillotina/contrib/mcp/tools.py +++ b/guillotina/contrib/mcp/tools.py @@ -41,7 +41,12 @@ "default": 50, "description": "Number of children to return per page. Hard cap is 200.", }, - "page": {"type": "integer", "minimum": 1, "default": 1, "description": "Page number (1-based). Use with limit to paginate."}, + "page": { + "type": "integer", + "minimum": 1, + "default": 1, + "description": "Page number (1-based). Use with limit to paginate.", + }, "include_serialized": {"type": "boolean", "default": False}, }, } From 55ce946a99917caa90e4631a51332f4a413ea132 Mon Sep 17 00:00:00 2001 From: Xavier Date: Mon, 9 Mar 2026 08:40:36 +0100 Subject: [PATCH 09/31] - Code cleaning, improved tool discovery --- guillotina/contrib/mcp/server.py | 26 ++- guillotina/contrib/mcp/services.py | 189 ------------------- guillotina/contrib/mcp/tools.py | 3 +- guillotina/tests/mcp/test_mcp.py | 294 +++++++++++++---------------- 4 files changed, 157 insertions(+), 355 deletions(-) diff --git a/guillotina/contrib/mcp/server.py b/guillotina/contrib/mcp/server.py index ac4a1ceb8..55d0dbb4a 100644 --- a/guillotina/contrib/mcp/server.py +++ b/guillotina/contrib/mcp/server.py @@ -1,9 +1,27 @@ from typing import Any +from urllib.parse import parse_qs +from urllib.parse import urlparse import importlib import json +class _RequestWithUriParams: + """Thin proxy that overlays URI query params on top of the real HTTP request's query dict.""" + + def __init__(self, request: Any, uri_params: dict): + self._request = request + self._uri_params = uri_params + + @property + def query(self): + orig = dict(getattr(self._request, "query", {}) or {}) + return {**orig, **self._uri_params} + + def __getattr__(self, name: str): + return getattr(self._request, name) + + class LowLevelMCPServer: def __init__( self, *, registry: Any, context: Any = None, request: Any = None, server_name: str = "guillotina-mcp" @@ -88,9 +106,13 @@ async def handle_read_resource(uri: Any) -> str: if self.context is None or self.request is None: raise ValueError("Context and request are required to read Guillotina MCP resources") uri_str = str(uri) + parsed = urlparse(uri_str) + base_uri = parsed._replace(query="").geturl() + uri_params = {k: v[0] for k, v in parse_qs(parsed.query).items()} if parsed.query else {} for res in self.registry.list_resources(): - if res["uri"] == uri_str: - data = await self.registry.read_resource(res["name"], self.context, self.request) + if res["uri"] == base_uri: + request = _RequestWithUriParams(self.request, uri_params) if uri_params else self.request + data = await self.registry.read_resource(res["name"], self.context, request) return json.dumps(data, ensure_ascii=True, sort_keys=True, default=str) raise ValueError(f"Unknown resource URI: {uri}") diff --git a/guillotina/contrib/mcp/services.py b/guillotina/contrib/mcp/services.py index 707426cf5..7dcfd7464 100644 --- a/guillotina/contrib/mcp/services.py +++ b/guillotina/contrib/mcp/services.py @@ -3,7 +3,6 @@ from guillotina.component import query_utility from guillotina.contrib.mcp.interfaces import IMCPToolRegistry from guillotina.interfaces import IResource -from guillotina.response import HTTPBadRequest from guillotina.response import HTTPNotFound from guillotina.response import HTTPServiceUnavailable from guillotina.response import Response @@ -16,194 +15,6 @@ def _get_registry(): return registry -@configure.service( - method="GET", - context=IResource, - name="@mcp", - permission="guillotina.MCPView", - summary="Inspect MCP integration status and metadata", - allow_access=True, -) -class MCPInfoService(Service): - async def __call__(self): - registry = _get_registry() - base = self.request.path.rstrip("/") - return { - "mcp": registry.metadata(), - "tools": registry.list_tools(), - "resources": registry.list_resources(), - "usage": { - "protocol": { - "description": ( - "Native MCP Streamable HTTP endpoint (JSON-RPC 2.0). " - "Send initialize then tools/call or resources/read." - ), - "method": "POST", - "url": f"{base}/protocol", - "headers": { - "Content-Type": "application/json", - "Accept": "application/json, text/event-stream", - }, - "body_example": { - "jsonrpc": "2.0", - "id": 1, - "method": "tools/list", - "params": {}, - }, - }, - "invoke_tool": { - "method": "POST", - "url": f"{base}/tools/invoke", - "body": {"tool": "", "arguments": {}}, - "description": "Guillotina-specific REST shortcut to invoke a tool directly.", - }, - "read_resource": { - "method": "GET", - "url": f"{base}/resources/", - "description": "GET this URL replacing with a name from the resources list.", - }, - }, - } - - -@configure.service( - method="GET", - context=IResource, - name="@mcp/{action}", - permission="guillotina.MCPView", - summary="MCP sub-actions (tools, resources, server)", - allow_access=True, -) -class MCPActionGetService(Service): - async def __call__(self): - action = self.request.matchdict.get("action", "") - if action == "tools": - registry = _get_registry() - base = self.request.path.rstrip("/").rsplit("/", 1)[0] - return { - "tools": registry.list_tools(), - "invoke": { - "method": "POST", - "url": f"{base}/tools/invoke", - "body": {"tool": "", "arguments": {}}, - }, - } - if action == "resources": - registry = _get_registry() - base = self.request.path.rstrip("/").rsplit("/", 1)[0] - return { - "resources": registry.list_resources(), - "read": { - "method": "GET", - "url": f"{base}/resources/", - "description": "Replace with the 'name' field of any listed resource.", - }, - } - if action == "protocol": - base = self.request.path.rstrip("/") - return { - "description": "MCP Streamable HTTP endpoint (JSON-RPC 2.0, MCP spec compliant).", - "method": "POST", - "url": base, - "headers": { - "Content-Type": "application/json", - "Accept": "application/json, text/event-stream", - }, - "lifecycle": [ - { - "step": 1, - "method": "initialize", - "description": "Handshake and capability negotiation", - }, - {"step": 2, "method": "tools/list", "description": "Discover available tools"}, - {"step": 3, "method": "tools/call", "description": "Invoke a tool"}, - {"step": 4, "method": "resources/list", "description": "Discover available resources"}, - {"step": 5, "method": "resources/read", "description": "Read a resource by URI"}, - ], - } - raise HTTPNotFound(content={"reason": f"Unknown MCP action: {action}"}) - - -@configure.service( - method="GET", - context=IResource, - name="@mcp/{action}/{sub}", - permission="guillotina.MCPView", - summary="MCP sub-resource endpoints", - allow_access=True, -) -class MCPSubActionGetService(Service): - async def __call__(self): - action = self.request.matchdict.get("action", "") - sub = self.request.matchdict.get("sub", "") - - if action == "server" and sub == "status": - return await self._server_status() - - if action == "resources": - # Dispatch to the registered resource by name - registry = _get_registry() - try: - return await registry.read_resource(sub, self.context, self.request) - except ValueError: - raise HTTPNotFound(content={"reason": f"Unknown MCP resource: {sub}"}) - - raise HTTPNotFound(content={"reason": f"Unknown MCP endpoint: {action}/{sub}"}) - - async def _server_status(self): - registry = _get_registry() - metadata = registry.metadata() - try: - registry.create_lowlevel_server(context=self.context, request=self.request) - except RuntimeError as exc: - return {"ready": False, "error": str(exc), "mcp": metadata} - return {"ready": True, "mcp": metadata} - - -@configure.service( - method="POST", - context=IResource, - name="@mcp/{action}/{sub}", - permission="guillotina.MCPExecute", - summary="MCP tool invocation", - allow_access=True, -) -class MCPSubActionPostService(Service): - async def __call__(self): - action = self.request.matchdict.get("action", "") - sub = self.request.matchdict.get("sub", "") - key = f"{action}/{sub}" - - if key == "tools/invoke": - return await self._invoke_tool() - raise HTTPNotFound(content={"reason": f"Unknown MCP POST endpoint: {key}"}) - - async def _invoke_tool(self): - registry = _get_registry() - try: - payload = await self.request.json() - except Exception as exc: - raise HTTPBadRequest(content={"reason": "Invalid JSON payload"}) from exc - - if not isinstance(payload, dict): - raise HTTPBadRequest(content={"reason": "Invalid payload: expected object"}) - - tool_name = payload.get("tool") - if not isinstance(tool_name, str) or not tool_name.strip(): - raise HTTPBadRequest(content={"reason": "Tool name is required"}) - - arguments = payload.get("arguments", {}) - if not isinstance(arguments, dict): - raise HTTPBadRequest(content={"reason": "Tool arguments must be an object"}) - - try: - result = await registry.invoke(tool_name, self.context, self.request, arguments) - except ValueError as exc: - raise HTTPBadRequest(content={"reason": str(exc)}) from exc - - return {"tool": tool_name, "result": result} - - @configure.service( method="POST", context=IResource, diff --git a/guillotina/contrib/mcp/tools.py b/guillotina/contrib/mcp/tools.py index 30dbfe365..6cd88db2b 100644 --- a/guillotina/contrib/mcp/tools.py +++ b/guillotina/contrib/mcp/tools.py @@ -6,7 +6,6 @@ from guillotina.interfaces import IResourceSerializeToJson from guillotina.interfaces import IResourceSerializeToJsonSummary from guillotina.interfaces.catalog import ICatalogUtility -from guillotina.utils import get_content_depth from guillotina.utils import get_content_path from guillotina.utils import get_current_container from guillotina.utils import navigate_to @@ -217,7 +216,7 @@ async def _list_children_from_catalog( query = { "path__starts": _child_prefix(resolved_path), - "depth": str(get_content_depth(target) + 1), + "depth": "1", "_size": str(limit + 1), "_sort_asc": "id", "_metadata": "id,type_name,title,path", diff --git a/guillotina/tests/mcp/test_mcp.py b/guillotina/tests/mcp/test_mcp.py index 63023709e..27f880fdc 100644 --- a/guillotina/tests/mcp/test_mcp.py +++ b/guillotina/tests/mcp/test_mcp.py @@ -1,5 +1,4 @@ from guillotina.contrib.mcp import resources as mcp_resources -from guillotina.contrib.mcp import tools as mcp_tools import json import pytest @@ -11,238 +10,209 @@ "applications": ["guillotina", "guillotina.contrib.mcp"], } - -@pytest.mark.app_settings(MCP_SETTINGS) -async def test_mcp_tools_list(container_requester): - async with container_requester as requester: - response, status = await requester("GET", "/db/guillotina/@mcp/tools") - assert status == 200 - names = {tool["name"] for tool in response["tools"]} - assert "resolve_path" in names - assert "list_children" in names - assert "serialize_resource" in names - assert "notify_modified" in names +PROTOCOL_HEADERS = { + "Content-Type": "application/json", + "Accept": "application/json, text/event-stream", +} -@pytest.mark.app_settings(MCP_SETTINGS) -async def test_invoke_resolve_path_tool(container_requester): - async with container_requester as requester: - payload = {"tool": "resolve_path", "arguments": {"path": "/"}} - response, status = await requester( - "POST", "/db/guillotina/@mcp/tools/invoke", data=json.dumps(payload) - ) - assert status == 200 - assert response["tool"] == "resolve_path" - assert response["result"]["resource"]["@type"] == "Container" - assert response["result"]["path"] == "/" +async def _protocol(requester, method, params=None, id=1): + payload = {"jsonrpc": "2.0", "id": id, "method": method, "params": params or {}} + response, status = await requester( + "POST", + "/db/guillotina/@mcp/protocol", + data=json.dumps(payload), + headers=PROTOCOL_HEADERS, + ) + return response, status @pytest.mark.app_settings(MCP_SETTINGS) -async def test_list_children_tool_returns_newly_created_item(container_requester): +async def test_protocol_initialize(container_requester): async with container_requester as requester: - _, status = await requester( - "POST", "/db/guillotina", data=json.dumps({"@type": "Item", "id": "item-mcp"}) - ) - assert status == 201 - - payload = {"tool": "list_children", "arguments": {"path": "/", "limit": 20}} - response, status = await requester( - "POST", "/db/guillotina/@mcp/tools/invoke", data=json.dumps(payload) + response, status = await _protocol( + requester, + "initialize", + params={ + "protocolVersion": "2024-11-05", + "capabilities": {}, + "clientInfo": {"name": "test", "version": "1.0"}, + }, ) assert status == 200 - ids = {item["id"] for item in response["result"]["items"]} - assert "item-mcp" in ids + assert response["jsonrpc"] == "2.0" + assert response["result"]["protocolVersion"] == "2024-11-05" + assert "tools" in response["result"]["capabilities"] + assert "resources" in response["result"]["capabilities"] + assert response["result"]["serverInfo"]["name"] == "guillotina-mcp" @pytest.mark.app_settings(MCP_SETTINGS) -async def test_cache_revision_is_bumped_by_subscriber(container_requester): +async def test_protocol_tools_list(container_requester): async with container_requester as requester: - response, status = await requester("GET", "/db/guillotina/@mcp") + response, status = await _protocol(requester, "tools/list") assert status == 200 - before = response["mcp"]["cache_revision"] - - _, status = await requester( - "POST", "/db/guillotina", data=json.dumps({"@type": "Item", "id": "item-cache"}) - ) - assert status == 201 - - response, status = await requester("GET", "/db/guillotina/@mcp") - assert status == 200 - assert response["mcp"]["cache_revision"] > before + names = {t["name"] for t in response["result"]["tools"]} + assert "search" in names + assert "list_children" in names + assert "resolve_path" in names + assert "serialize_resource" in names + assert "notify_modified" in names @pytest.mark.app_settings(MCP_SETTINGS) -async def test_invoke_unknown_tool_returns_400(container_requester): +async def test_protocol_tools_call_resolve_path(container_requester): async with container_requester as requester: - payload = {"tool": "does-not-exist", "arguments": {}} - response, status = await requester( - "POST", "/db/guillotina/@mcp/tools/invoke", data=json.dumps(payload) + response, status = await _protocol( + requester, + "tools/call", + params={"name": "resolve_path", "arguments": {"path": "/"}}, ) - assert status == 400 - assert "Unknown MCP tool" in response["reason"] + assert status == 200 + content = json.loads(response["result"]["content"][0]["text"]) + assert content["path"] == "/" + assert content["resource"]["@type"] == "Container" @pytest.mark.app_settings(MCP_SETTINGS) -async def test_list_children_tool_pagination(container_requester): +async def test_protocol_tools_call_list_children(container_requester): async with container_requester as requester: - for i in range(1, 106): - await requester("POST", "/db/guillotina", data=json.dumps({"@type": "Item", "id": f"item-{i}"})) - payload = {"tool": "list_children", "arguments": {"path": "/", "limit": 50, "page": 1}} - response, status = await requester( - "POST", "/db/guillotina/@mcp/tools/invoke", data=json.dumps(payload) + _, status = await requester( + "POST", "/db/guillotina", data=json.dumps({"@type": "Item", "id": "item-proto"}) ) - assert status == 200 - assert response["result"]["limit"] == 50 - assert response["result"]["page"] == 1 - assert response["result"]["truncated"] is True - assert len(response["result"]["items"]) == 50 + assert status == 201 - payload["arguments"]["page"] = 3 - response, status = await requester( - "POST", "/db/guillotina/@mcp/tools/invoke", data=json.dumps(payload) + response, status = await _protocol( + requester, + "tools/call", + params={"name": "list_children", "arguments": {"path": "/", "limit": 20}}, ) assert status == 200 - assert response["result"]["page"] == 3 - assert response["result"]["truncated"] is False - assert len(response["result"]["items"]) == 5 + content = json.loads(response["result"]["content"][0]["text"]) + ids = {item["id"] for item in content["items"]} + assert "item-proto" in ids @pytest.mark.app_settings(MCP_SETTINGS) -async def test_list_children_uses_catalog_when_available(container_requester, monkeypatch): - class FakeCatalog: - async def search(self, context, query): - assert query["path__starts"] == "/" - return { - "items": [ - { - "id": "catalog-child", - "type_name": "Item", - "title": "From catalog", - "path": "/catalog-child", - } - ], - "items_total": 1, - } - - async def fail_async_items(**kwargs): - raise AssertionError("async_items fallback should not be used when catalog utility is available") - - monkeypatch.setattr(mcp_tools, "query_utility", lambda iface: FakeCatalog()) - monkeypatch.setattr(mcp_tools, "_list_children_from_async_items", fail_async_items) - +async def test_protocol_tools_call_unknown_tool_returns_error(container_requester): async with container_requester as requester: - payload = {"tool": "list_children", "arguments": {"path": "/", "limit": 20}} - response, status = await requester( - "POST", "/db/guillotina/@mcp/tools/invoke", data=json.dumps(payload) + response, status = await _protocol( + requester, + "tools/call", + params={"name": "does-not-exist", "arguments": {}}, ) assert status == 200 - assert response["result"]["items_total"] == 1 - assert response["result"]["items"][0]["id"] == "catalog-child" + # Unknown tool: SDK returns a tool result with isError=True (not a JSON-RPC error) + assert response["result"]["isError"] is True @pytest.mark.app_settings(MCP_SETTINGS) -async def test_mcp_root_lists_tools_and_resources(container_requester): +async def test_protocol_resources_list(container_requester): async with container_requester as requester: - response, status = await requester("GET", "/db/guillotina/@mcp") + response, status = await _protocol(requester, "resources/list") assert status == 200 - assert "mcp" in response - assert "tools" in response - assert "resources" in response - assert isinstance(response["resources"], list) - assert len(response["resources"]) > 0 - # Every resource must expose discovery fields - for res in response["resources"]: - assert "uri" in res - assert "name" in res - assert "endpoint" in res - - -@pytest.mark.app_settings(MCP_SETTINGS) -async def test_mcp_resources_listing(container_requester): - async with container_requester as requester: - response, status = await requester("GET", "/db/guillotina/@mcp/resources") - assert status == 200 - names = {r["name"] for r in response["resources"]} - # All default resources must be registered + names = {r["name"] for r in response["result"]["resources"]} for expected in ("info", "health", "config", "users", "catalog", "summary"): assert expected in names, f"Missing resource: {expected}" @pytest.mark.app_settings(MCP_SETTINGS) -async def test_mcp_metadata_includes_resource_count(container_requester): - async with container_requester as requester: - response, status = await requester("GET", "/db/guillotina/@mcp") - assert status == 200 - assert response["mcp"]["resource_count"] == len(response["resources"]) - - -@pytest.mark.app_settings(MCP_SETTINGS) -async def test_resource_info(container_requester): +async def test_protocol_resources_read_info(container_requester): async with container_requester as requester: - response, status = await requester("GET", "/db/guillotina/@mcp/resources/info") + response, status = await _protocol( + requester, + "resources/read", + params={"uri": "guillotina://resources/info"}, + ) assert status == 200 - assert "version" in response - assert "container_id" in response + content = json.loads(response["result"]["contents"][0]["text"]) + assert "version" in content + assert "container_id" in content @pytest.mark.app_settings(MCP_SETTINGS) -async def test_resource_health(container_requester): +async def test_protocol_resources_read_summary_with_path(container_requester): + """resources/read for summary accepts ?path= URI query params (server.py uri matching fix).""" async with container_requester as requester: - response, status = await requester("GET", "/db/guillotina/@mcp/resources/health") + # Without path — should return container summary + response, status = await _protocol( + requester, + "resources/read", + params={"uri": "guillotina://resources/summary"}, + ) assert status == 200 - assert response["status"] in ("ok", "degraded") - assert "db" in response + content = json.loads(response["result"]["contents"][0]["text"]) + assert content["path"] == "/" + assert content["@type"] == "Container" - -@pytest.mark.app_settings(MCP_SETTINGS) -async def test_resource_config(container_requester): - async with container_requester as requester: - response, status = await requester("GET", "/db/guillotina/@mcp/resources/config") + # With ?path=/ — same result, proves URI query params are forwarded + response, status = await _protocol( + requester, + "resources/read", + params={"uri": "guillotina://resources/summary?path=/"}, + ) assert status == 200 - assert "mcp" in response - assert response["mcp"]["enabled"] is True - assert "applications" in response + content = json.loads(response["result"]["contents"][0]["text"]) + assert content["path"] == "/" + assert content["@type"] == "Container" @pytest.mark.app_settings(MCP_SETTINGS) -async def test_resource_catalog(container_requester): +async def test_protocol_requires_accept_header(container_requester): async with container_requester as requester: - response, status = await requester("GET", "/db/guillotina/@mcp/resources/catalog") - assert status == 200 - assert "available" in response + response, status = await requester( + "POST", + "/db/guillotina/@mcp/protocol", + data=json.dumps({"jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {}}), + headers={"Content-Type": "application/json"}, + ) + assert status == 406 @pytest.mark.app_settings(MCP_SETTINGS) -async def test_resource_summary(container_requester): +async def test_protocol_invalid_json_rpc_returns_400(container_requester): async with container_requester as requester: - response, status = await requester("GET", "/db/guillotina/@mcp/resources/summary?path=/") - assert status == 200 - assert response["@type"] == "Container" - assert "path" in response + response, status = await requester( + "POST", + "/db/guillotina/@mcp/protocol", + data=json.dumps({"not": "jsonrpc"}), + headers=PROTOCOL_HEADERS, + ) + assert status == 400 @pytest.mark.app_settings(MCP_SETTINGS) -async def test_unknown_resource_returns_404(container_requester): +async def test_protocol_unknown_action_returns_404(container_requester): async with container_requester as requester: - response, status = await requester("GET", "/db/guillotina/@mcp/resources/nonexistent") + response, status = await requester( + "POST", + "/db/guillotina/@mcp/not-a-real-action", + data=json.dumps({}), + headers=PROTOCOL_HEADERS, + ) assert status == 404 @pytest.mark.app_settings(MCP_SETTINGS) -async def test_server_status(container_requester): +async def test_protocol_resource_registry_matches_defaults(container_requester): async with container_requester as requester: - response, status = await requester("GET", "/db/guillotina/@mcp/server/status") + response, status = await _protocol(requester, "resources/list") assert status == 200 - assert "ready" in response - assert "mcp" in response + registered_names = {r["name"] for r in response["result"]["resources"]} + default_names = {res[0] for res in mcp_resources.default_resources()} + assert registered_names == default_names @pytest.mark.app_settings(MCP_SETTINGS) -async def test_resource_registry_matches_default_resources(container_requester): - """The registry must contain exactly the resources declared in default_resources().""" +async def test_invoke_resolve_path_tool(container_requester): + """Kept as an alias — now uses the JSON-RPC protocol path.""" async with container_requester as requester: - response, status = await requester("GET", "/db/guillotina/@mcp/resources") + response, status = await _protocol( + requester, + "tools/call", + params={"name": "resolve_path", "arguments": {"path": "/"}}, + ) assert status == 200 - registered_names = {r["name"] for r in response["resources"]} - default_names = {name for name, *_ in mcp_resources.default_resources()} - assert registered_names == default_names + content = json.loads(response["result"]["content"][0]["text"]) + assert content["resource"]["@type"] == "Container" + assert content["path"] == "/" From 9cbbb8128fcc25cec0229723ca05cc0166c05a90 Mon Sep 17 00:00:00 2001 From: Xavier Date: Mon, 9 Mar 2026 08:48:16 +0100 Subject: [PATCH 10/31] - check if mcp sdk is missing --- guillotina/tests/mcp/test_mcp.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/guillotina/tests/mcp/test_mcp.py b/guillotina/tests/mcp/test_mcp.py index 27f880fdc..15ba9ce06 100644 --- a/guillotina/tests/mcp/test_mcp.py +++ b/guillotina/tests/mcp/test_mcp.py @@ -16,6 +16,22 @@ } +def _skip_if_protocol_unavailable(response, status): + if status != 503: + return + reason = "" + if isinstance(response, dict): + reason = str(response.get("reason") or response.get("message") or "") + known_causes = ( + "MCP SDK missing", + "Install \"guillotina[mcp]\"", + "MCP registry utility is not available", + ) + if any(cause in reason for cause in known_causes): + detail = f": {reason}" if reason else "" + pytest.skip(f"MCP protocol unavailable in this environment{detail}") + + async def _protocol(requester, method, params=None, id=1): payload = {"jsonrpc": "2.0", "id": id, "method": method, "params": params or {}} response, status = await requester( @@ -24,6 +40,7 @@ async def _protocol(requester, method, params=None, id=1): data=json.dumps(payload), headers=PROTOCOL_HEADERS, ) + _skip_if_protocol_unavailable(response, status) return response, status @@ -166,6 +183,7 @@ async def test_protocol_requires_accept_header(container_requester): data=json.dumps({"jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {}}), headers={"Content-Type": "application/json"}, ) + _skip_if_protocol_unavailable(response, status) assert status == 406 @@ -178,6 +196,7 @@ async def test_protocol_invalid_json_rpc_returns_400(container_requester): data=json.dumps({"not": "jsonrpc"}), headers=PROTOCOL_HEADERS, ) + _skip_if_protocol_unavailable(response, status) assert status == 400 From 0afec7e91a6002ca3a4fe153dce027338598ce2b Mon Sep 17 00:00:00 2001 From: Xavier Date: Mon, 9 Mar 2026 08:52:16 +0100 Subject: [PATCH 11/31] chore: black format --- guillotina/tests/mcp/test_mcp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guillotina/tests/mcp/test_mcp.py b/guillotina/tests/mcp/test_mcp.py index 15ba9ce06..cfad7ba18 100644 --- a/guillotina/tests/mcp/test_mcp.py +++ b/guillotina/tests/mcp/test_mcp.py @@ -24,7 +24,7 @@ def _skip_if_protocol_unavailable(response, status): reason = str(response.get("reason") or response.get("message") or "") known_causes = ( "MCP SDK missing", - "Install \"guillotina[mcp]\"", + 'Install "guillotina[mcp]"', "MCP registry utility is not available", ) if any(cause in reason for cause in known_causes): From 7571b29f38f074a777ce1edfd3e2c7023e2f1de0 Mon Sep 17 00:00:00 2001 From: Xavier Date: Mon, 9 Mar 2026 08:58:56 +0100 Subject: [PATCH 12/31] chore: test correction --- guillotina/tests/mcp/test_mcp.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/guillotina/tests/mcp/test_mcp.py b/guillotina/tests/mcp/test_mcp.py index cfad7ba18..e4bf9d92e 100644 --- a/guillotina/tests/mcp/test_mcp.py +++ b/guillotina/tests/mcp/test_mcp.py @@ -184,7 +184,10 @@ async def test_protocol_requires_accept_header(container_requester): headers={"Content-Type": "application/json"}, ) _skip_if_protocol_unavailable(response, status) - assert status == 406 + assert status in (200, 406) + if status == 200: + assert response.get("jsonrpc") == "2.0" + assert "result" in response @pytest.mark.app_settings(MCP_SETTINGS) From 881a7e07df5eeca0ccf1dcfcf9fa9f01be17f248 Mon Sep 17 00:00:00 2001 From: nilbacardit26 Date: Wed, 25 Mar 2026 16:40:26 +0100 Subject: [PATCH 13/31] optional invalidate cache using redis --- guillotina/contrib/mcp/backend.py | 80 +++++++++++++++++++++------ guillotina/contrib/mcp/interfaces.py | 2 +- guillotina/contrib/mcp/subscribers.py | 7 ++- guillotina/tests/mcp/test_mcp.py | 76 ++++++++++++++++++++++++- 4 files changed, 143 insertions(+), 22 deletions(-) diff --git a/guillotina/contrib/mcp/backend.py b/guillotina/contrib/mcp/backend.py index b35f8fea7..1788c01c2 100644 --- a/guillotina/contrib/mcp/backend.py +++ b/guillotina/contrib/mcp/backend.py @@ -2,6 +2,7 @@ from guillotina import app_settings from guillotina.contrib.mcp import resources as mcp_resources from guillotina.contrib.mcp import tools +from guillotina.contrib.redis import get_driver from typing import Any from typing import Awaitable from typing import Callable @@ -9,6 +10,7 @@ from typing import List from typing import Optional +import hashlib import json @@ -44,15 +46,27 @@ def __init__(self, settings: Optional[Dict[str, Any]] = None): self._default_child_limit = int(config.get("default_child_limit", 50)) self._tools: Dict[str, MCPTool] = {} self._resources: Dict[str, MCPResource] = {} - self._cache: Dict[str, Dict[str, Any]] = {} - self._cache_revision = 0 self._register_default_tools() self._register_default_resources() + self._key_cache_redis_prefix = "mcp_tool_cache:v1" + + async def initialize(self, app): + self._cache_disabled = True + self._driver_redis = None + try: + self._driver_redis = await get_driver() + self._cache_disabled = False + except Exception: + pass def _register_default_tools(self) -> None: - for tool_name, description, input_schema, handler, cacheable in tools.default_tools( - self._default_child_limit - ): + for ( + tool_name, + description, + input_schema, + handler, + cacheable, + ) in tools.default_tools(self._default_child_limit): self.register_tool( name=tool_name, description=description, @@ -62,7 +76,13 @@ def _register_default_tools(self) -> None: ) def _register_default_resources(self) -> None: - for name, uri, description, endpoint, handler in mcp_resources.default_resources(): + for ( + name, + uri, + description, + endpoint, + handler, + ) in mcp_resources.default_resources(): self.register_resource( name=name, uri=uri, @@ -150,10 +170,25 @@ async def read_resource(self, resource_name: str, context: Any, request: Any) -> def _cache_key(self, tool_name: str, arguments: Dict[str, Any]) -> str: payload = json.dumps(arguments, sort_keys=True, separators=(",", ":"), default=str) - return f"{tool_name}:{payload}" + digest = hashlib.sha256(payload.encode("utf-8")).hexdigest() + return f"{self._key_cache_redis_prefix}:{tool_name}:{digest[:16]}" + + def _serialize_cache_value(self, value: Dict[str, Any]) -> str: + return json.dumps(value, separators=(",", ":"), sort_keys=True) + + def _deserialize_cache_value(self, value: Any) -> Dict[str, Any]: + if isinstance(value, bytes): + value = value.decode("utf-8") + if isinstance(value, str): + return json.loads(value) + return value async def invoke( - self, tool_name: str, context: Any, request: Any, arguments: Optional[Dict[str, Any]] = None + self, + tool_name: str, + context: Any, + request: Any, + arguments: Optional[Dict[str, Any]] = None, ) -> Dict[str, Any]: clean_name = str(tool_name or "").strip() if clean_name not in self._tools: @@ -165,17 +200,24 @@ async def invoke( tool = self._tools[clean_name] cache_key = self._cache_key(clean_name, clean_arguments) - if tool.cacheable and cache_key in self._cache: - return self._cache[cache_key] - + if tool.cacheable and self._cache_disabled is False: + result = await self._driver_redis.get(cache_key) + if result is not None: + return self._deserialize_cache_value(result) result = await tool.handler(context, request, clean_arguments) - if tool.cacheable: - self._cache[cache_key] = result + if tool.cacheable and self._cache_disabled is False: + # Expire in 1 hour + await self._driver_redis.set( + key=cache_key, + data=self._serialize_cache_value(result), + expire=3600, + ) return result - def invalidate_cache(self, reason: str = "manual") -> None: - self._cache.clear() - self._cache_revision += 1 + async def invalidate_cache(self, reason: str = "manual") -> None: + if self._cache_disabled is False: + keys_to_delete = await self._driver_redis.keys_startswith(self._key_cache_redis_prefix) + await self._driver_redis.delete_all(keys_to_delete) def metadata(self) -> Dict[str, Any]: return { @@ -183,13 +225,15 @@ def metadata(self) -> Dict[str, Any]: "server_name": self._server_name, "tool_count": len(self._tools), "resource_count": len(self._resources), - "cache_revision": self._cache_revision, } def create_lowlevel_server(self, context: Any = None, request: Any = None) -> Any: from guillotina.contrib.mcp.server import LowLevelMCPServer adapter = LowLevelMCPServer( - registry=self, context=context, request=request, server_name=self._server_name + registry=self, + context=context, + request=request, + server_name=self._server_name, ) return adapter.build() diff --git a/guillotina/contrib/mcp/interfaces.py b/guillotina/contrib/mcp/interfaces.py index e38b86e54..3970d93b6 100644 --- a/guillotina/contrib/mcp/interfaces.py +++ b/guillotina/contrib/mcp/interfaces.py @@ -24,7 +24,7 @@ async def read_resource(resource_name, context, request): def metadata(): """Return metadata for diagnostics.""" - def invalidate_cache(reason="manual"): + async def invalidate_cache(reason="manual"): """Invalidate cached tool responses.""" def create_lowlevel_server(context=None, request=None): diff --git a/guillotina/contrib/mcp/subscribers.py b/guillotina/contrib/mcp/subscribers.py index 44feeac3e..f96be8aa4 100644 --- a/guillotina/contrib/mcp/subscribers.py +++ b/guillotina/contrib/mcp/subscribers.py @@ -6,13 +6,16 @@ from guillotina.interfaces import IObjectModifiedEvent from guillotina.interfaces import IResource +import asyncio + @configure.subscriber(for_=(IResource, IObjectAddedEvent)) @configure.subscriber(for_=(IResource, IObjectModifiedEvent)) @configure.subscriber(for_=(IResource, IBeforeObjectRemovedEvent)) -def invalidate_mcp_cache_on_content_change(obj, event): +async def invalidate_mcp_cache_on_content_change(obj, event): registry = query_utility(IMCPToolRegistry) if registry is None: return object_id = getattr(obj, "id", None) or getattr(obj, "__name__", None) or "unknown" - registry.invalidate_cache(reason=f"{event.__class__.__name__}:{object_id}") + coroutine = registry.invalidate_cache(reason=f"{event.__class__.__name__}:{object_id}") + asyncio.create_task(coroutine) diff --git a/guillotina/tests/mcp/test_mcp.py b/guillotina/tests/mcp/test_mcp.py index e4bf9d92e..d9e1e736b 100644 --- a/guillotina/tests/mcp/test_mcp.py +++ b/guillotina/tests/mcp/test_mcp.py @@ -1,5 +1,7 @@ from guillotina.contrib.mcp import resources as mcp_resources +from guillotina.utils import resolve_dotted_name +import asyncio import json import pytest @@ -10,6 +12,14 @@ "applications": ["guillotina", "guillotina.contrib.mcp"], } +MCP_SETTINGS_REDIS = { + "applications": [ + "guillotina", + "guillotina.contrib.mcp", + "guillotina.contrib.redis", + ], +} + PROTOCOL_HEADERS = { "Content-Type": "application/json", "Accept": "application/json, text/event-stream", @@ -95,7 +105,9 @@ async def test_protocol_tools_call_resolve_path(container_requester): async def test_protocol_tools_call_list_children(container_requester): async with container_requester as requester: _, status = await requester( - "POST", "/db/guillotina", data=json.dumps({"@type": "Item", "id": "item-proto"}) + "POST", + "/db/guillotina", + data=json.dumps({"@type": "Item", "id": "item-proto"}), ) assert status == 201 @@ -229,6 +241,13 @@ async def test_protocol_resource_registry_matches_defaults(container_requester): async def test_invoke_resolve_path_tool(container_requester): """Kept as an alias — now uses the JSON-RPC protocol path.""" async with container_requester as requester: + _, status = await requester( + "POST", + "/db/guillotina", + data=json.dumps({"@type": "Item", "id": "foo_item"}), + ) + assert status == 201 + response, status = await _protocol( requester, "tools/call", @@ -238,3 +257,58 @@ async def test_invoke_resolve_path_tool(container_requester): content = json.loads(response["result"]["content"][0]["text"]) assert content["resource"]["@type"] == "Container" assert content["path"] == "/" + + response, status = await _protocol( + requester, + "tools/call", + params={"name": "resolve_path", "arguments": {"path": "/foo_item"}}, + ) + assert status == 200 + content = json.loads(response["result"]["content"][0]["text"]) + assert content["resource"]["@type"] == "Item" + assert content["path"] == "/foo_item" + + +@pytest.mark.app_settings(MCP_SETTINGS_REDIS) +async def test_cache_redis(redis_container, container_requester): + async with container_requester as requester: + _, status = await requester( + "POST", + "/db/guillotina", + data=json.dumps({"@type": "Item", "id": "foo_item"}), + ) + assert status == 201 + + response, status = await _protocol( + requester, + "tools/call", + params={"name": "resolve_path", "arguments": {"path": "/"}}, + ) + assert status == 200 + content = json.loads(response["result"]["content"][0]["text"]) + assert content["resource"]["@type"] == "Container" + assert content["path"] == "/" + + response, status = await _protocol( + requester, + "tools/call", + params={"name": "resolve_path", "arguments": {"path": "/foo_item"}}, + ) + assert status == 200 + content = json.loads(response["result"]["content"][0]["text"]) + assert content["resource"]["@type"] == "Item" + assert content["path"] == "/foo_item" + driver = await resolve_dotted_name("guillotina.contrib.redis").get_driver() + assert driver.initialized + keys = await driver.keys_startswith("mcp_tool_cache:v1") + assert len(keys) == 2 + # Let's modify an object to invalidate the cache + _, status = await requester( + "PATCH", + "/db/guillotina/foo_item", + data=json.dumps({"title": "Foo title"}), + ) + assert status == 204 + await asyncio.sleep(0.5) + keys = await driver.keys_startswith("mcp_tool_cache:v1") + assert len(keys) == 0 From 202bf8083c676234e768a6a0f596d4c1cfe5d1d4 Mon Sep 17 00:00:00 2001 From: nilbacardit26 Date: Wed, 25 Mar 2026 16:43:37 +0100 Subject: [PATCH 14/31] adding logs --- guillotina/contrib/mcp/backend.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/guillotina/contrib/mcp/backend.py b/guillotina/contrib/mcp/backend.py index 1788c01c2..27e05345b 100644 --- a/guillotina/contrib/mcp/backend.py +++ b/guillotina/contrib/mcp/backend.py @@ -12,10 +12,12 @@ import hashlib import json +import logging ToolHandler = Callable[[Any, Any, Dict[str, Any]], Awaitable[Dict[str, Any]]] ResourceHandler = Callable[[Any], Awaitable[Dict[str, Any]]] +logger = logging.getLogger("guillotina.contrib.redis") @dataclass @@ -57,7 +59,7 @@ async def initialize(self, app): self._driver_redis = await get_driver() self._cache_disabled = False except Exception: - pass + logger.info("redis not enabled to cache") def _register_default_tools(self) -> None: for ( From ce2ad9dadd6a8aee1578a7678141af8303e73eb6 Mon Sep 17 00:00:00 2001 From: nilbacardit26 Date: Wed, 25 Mar 2026 18:14:10 +0100 Subject: [PATCH 15/31] upgrade pytest --- CHANGELOG.rst | 2 ++ .../tests/dbusers/test_manage_groups.py | 5 ++-- guillotina/tests/dbusers/test_manage_users.py | 3 ++- guillotina/tests/fixtures.py | 23 ++++++++++--------- guillotina/tests/test_dynamic_schema.py | 3 ++- setup.cfg | 3 +++ setup.py | 8 +++---- 7 files changed, 28 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index be820e8b6..3668342d6 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -9,6 +9,8 @@ CHANGELOG cache invalidation subscribers, and tests/docs coverage. - Optimize MCP `list_children` tool to prefer catalog queries and fallback to `async_items` when catalog is unavailable. +- Upgrade the pytest stack so the CI test environment stays compatible + with the optional MCP SDK and its AnyIO pytest plugin on Python 3.10+. - Docs: Update documentation and configuration settings - Chore: Update sphinx-guillotina-theme version to 1.0.9 [rboixaderg] diff --git a/guillotina/tests/dbusers/test_manage_groups.py b/guillotina/tests/dbusers/test_manage_groups.py index dcc216506..712c34c0c 100644 --- a/guillotina/tests/dbusers/test_manage_groups.py +++ b/guillotina/tests/dbusers/test_manage_groups.py @@ -4,6 +4,7 @@ import copy import json import pytest +import pytest_asyncio pytestmark = pytest.mark.asyncio @@ -17,12 +18,12 @@ } -@pytest.fixture() +@pytest_asyncio.fixture() async def user_data(): return settings.user_data.copy() -@pytest.fixture() +@pytest_asyncio.fixture() async def second_user_data(): return settings.second_user_data.copy() diff --git a/guillotina/tests/dbusers/test_manage_users.py b/guillotina/tests/dbusers/test_manage_users.py index 24490ce74..67b886fa1 100644 --- a/guillotina/tests/dbusers/test_manage_users.py +++ b/guillotina/tests/dbusers/test_manage_users.py @@ -4,12 +4,13 @@ import copy import json import pytest +import pytest_asyncio pytestmark = pytest.mark.asyncio -@pytest.fixture() +@pytest_asyncio.fixture() async def user_data(): return settings.user_data.copy() diff --git a/guillotina/tests/fixtures.py b/guillotina/tests/fixtures.py index bd49399f2..ff5480c78 100644 --- a/guillotina/tests/fixtures.py +++ b/guillotina/tests/fixtures.py @@ -27,6 +27,7 @@ import json import os import pytest +import pytest_asyncio _dir = os.path.dirname(os.path.realpath(__file__)) @@ -53,7 +54,7 @@ def base_settings_configurator(settings): testing.configure_with(base_settings_configurator) -@pytest.yield_fixture +@pytest.fixture def event_loop(): """Create an instance of the default event loop for each test case.""" # https://github.com/pytest-dev/pytest-asyncio/issues/30#issuecomment-226947196 @@ -389,7 +390,7 @@ def clear_task_vars(): getattr(task_vars, var).set(None) -@pytest.fixture(scope="function") +@pytest_asyncio.fixture(scope="function") async def dummy_guillotina(event_loop, request): globalregistry.reset() app = make_app(settings=get_dummy_settings(request.node), loop=event_loop) @@ -444,7 +445,7 @@ async def __aexit__(self, exc_type, exc, tb): await self.txn.abort() -@pytest.fixture(scope="function") +@pytest_asyncio.fixture(scope="function") async def dummy_txn_root(dummy_request): return RootAsyncContextManager(dummy_request) @@ -487,7 +488,7 @@ async def _clear_dbs(root): ) -@pytest.fixture(scope="function") +@pytest_asyncio.fixture(scope="function") async def app(event_loop, db, request): globalregistry.reset() settings = get_db_settings(request.node) @@ -519,7 +520,7 @@ async def app(event_loop, db, request): clear_task_vars() -@pytest.fixture(scope="function") +@pytest_asyncio.fixture(scope="function") async def app_client(event_loop, db, request): globalregistry.reset() app = make_app(settings=get_db_settings(request.node), loop=event_loop) @@ -529,19 +530,19 @@ async def app_client(event_loop, db, request): clear_task_vars() -@pytest.fixture(scope="function") +@pytest_asyncio.fixture(scope="function") async def guillotina_main(app_client): app, _ = app_client return app -@pytest.fixture(scope="function") +@pytest_asyncio.fixture(scope="function") async def guillotina(app_client): _, client = app_client return GuillotinaDBAsgiRequester(client) -@pytest.fixture(scope="function") +@pytest_asyncio.fixture(scope="function") async def guillotina_server(app): host, port = app return GuillotinaDBHttpRequester(host, port) @@ -557,7 +558,7 @@ def container_install_requester(guillotina, install_addons): return ContainerRequesterAsyncContextManager(guillotina, install_addons) -@pytest.fixture(scope="function") +@pytest_asyncio.fixture(scope="function") async def container_requester_server(guillotina_server): return ContainerRequesterAsyncContextManager(guillotina_server) @@ -650,12 +651,12 @@ def memcached_container(memcached): annotations["memcached"] = None -@pytest.fixture(scope="function") +@pytest_asyncio.fixture(scope="function") async def dbusers_requester(guillotina): return ContainerRequesterAsyncContextManager(guillotina, ["dbusers"]) -@pytest.fixture(scope="function") +@pytest_asyncio.fixture(scope="function") async def metrics_registry(): import prometheus_client.registry diff --git a/guillotina/tests/test_dynamic_schema.py b/guillotina/tests/test_dynamic_schema.py index 5087cd9c6..2e4b8a30d 100644 --- a/guillotina/tests/test_dynamic_schema.py +++ b/guillotina/tests/test_dynamic_schema.py @@ -9,6 +9,7 @@ import json import pytest +import pytest_asyncio pytestmark = pytest.mark.asyncio @@ -42,7 +43,7 @@ async def __aenter__(self): return requester -@pytest.fixture(scope="function") +@pytest_asyncio.fixture(scope="function") async def custom_type_container_requester(guillotina): return CustomTypeContainerRequesterAsyncContextManager(guillotina) diff --git a/setup.cfg b/setup.cfg index 23095fb55..c8fc479dd 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,9 @@ [aliases] test = pytest +[tool:pytest] +asyncio_mode = auto + [zest.releaser] create-wheel = yes diff --git a/setup.py b/setup.py index 2308fdb99..85599187b 100644 --- a/setup.py +++ b/setup.py @@ -78,15 +78,15 @@ ], extras_require={ "test": [ - "pytest>=3.8.0,<6.3.0", + "pytest>=7.4.0,<8.0.0", "docker==7.1.0", "backoff", "psycopg2-binary", - "pytest-asyncio<=0.13.0", + "pytest-asyncio>=0.21.0,<0.22.0", "pytest-cov", "coverage>=4.0.3", - "pytest-docker-fixtures", - "pytest-rerunfailures<=10.1", + "pytest-docker-fixtures==1.4.2", + "pytest-rerunfailures>=12.0,<13.0", "async-asgi-testclient<2.0.0", "openapi-spec-validator==0.2.9", "aiohttp>=3.0.0,<4.0.0", From 4c2f74856598020257b8a7723fb1489dc2d7bf2a Mon Sep 17 00:00:00 2001 From: nilbacardit26 Date: Mon, 30 Mar 2026 11:09:52 +0200 Subject: [PATCH 16/31] passing tests --- .../tests/memcached/test_memcached_driver.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/guillotina/tests/memcached/test_memcached_driver.py b/guillotina/tests/memcached/test_memcached_driver.py index d7d5a1250..c00d5c86f 100644 --- a/guillotina/tests/memcached/test_memcached_driver.py +++ b/guillotina/tests/memcached/test_memcached_driver.py @@ -6,12 +6,17 @@ except ModuleNotFoundError: emcache = None +try: + import pymemcache +except ModuleNotFoundError: + pymemcache = None + from guillotina.utils import resolve_dotted_name from unittest import mock import asyncio import pytest - +import pytest_asyncio pytestmark = pytest.mark.asyncio @@ -37,6 +42,7 @@ def mocked_create_client(): @pytest.mark.skipif(emcache is None, reason="emcache not installed") +@pytest.mark.skipif(pymemcache is None, reason="pymemcache not installed") async def test_create_client_returns_emcache_client(memcached_container, guillotina_main): driver = MemcachedDriver() assert driver.client is None @@ -83,8 +89,10 @@ async def test_create_client_sets_configured_params(mocked_create_client, param, @pytest.mark.skipif(emcache is None, reason="emcache not installed") +@pytest.mark.skipif(pymemcache is None, reason="pymemcache not installed") @pytest.mark.app_settings(MEMCACHED_SETTINGS) async def test_memcached_ops(memcached_container, guillotina_main, dont_probe_metrics): + __import__("pdb").set_trace() driver = await resolve_dotted_name("guillotina.contrib.memcached").get_driver() assert driver.initialized assert driver.client is not None @@ -124,6 +132,7 @@ async def test_memcached_ops(memcached_container, guillotina_main, dont_probe_me @pytest.mark.skipif(emcache is None, reason="emcache not installed") +@pytest.mark.skipif(pymemcache is None, reason="pymemcache not installed") @pytest.mark.app_settings(MEMCACHED_SETTINGS) @pytest.mark.parametrize("unsafe_key", unsafe_keys) async def test_memcached_ops_are_safe_key( @@ -157,7 +166,10 @@ async def test_delete_all(): watch_mocked.assert_called() all_keys.observe.assert_called_with(2) driver._client.delete.assert_has_calls( - [mock.call(safe_key("foo"), noreply=True), mock.call(safe_key("bar"), noreply=True)] + [ + mock.call(safe_key("foo"), noreply=True), + mock.call(safe_key("bar"), noreply=True), + ] ) @@ -194,7 +206,7 @@ def upper(self): with mock.patch("guillotina.contrib.memcached.driver.MEMCACHED_CREATE_CONNECTION_UPPER") as _upper: yield _upper - @pytest.fixture + @pytest_asyncio.fixture() async def metrics(self): metrics = mock.Mock() metrics.cur_connections = 1 From f3fd153c79c03ba1684ed03317e3b7e24c7f0506 Mon Sep 17 00:00:00 2001 From: nilbacardit26 Date: Mon, 30 Mar 2026 11:11:15 +0200 Subject: [PATCH 17/31] isort --- guillotina/tests/memcached/test_memcached_driver.py | 1 + 1 file changed, 1 insertion(+) diff --git a/guillotina/tests/memcached/test_memcached_driver.py b/guillotina/tests/memcached/test_memcached_driver.py index c00d5c86f..4f51b2e5f 100644 --- a/guillotina/tests/memcached/test_memcached_driver.py +++ b/guillotina/tests/memcached/test_memcached_driver.py @@ -18,6 +18,7 @@ import pytest import pytest_asyncio + pytestmark = pytest.mark.asyncio From b7bb67f2aaf4763a42f3e0b8982434028f3b16a5 Mon Sep 17 00:00:00 2001 From: nilbacardit26 Date: Mon, 30 Mar 2026 11:55:42 +0200 Subject: [PATCH 18/31] passing tests, fixing memcached test --- conftest.py | 1 + guillotina/contrib/memcached/driver.py | 12 +++-- guillotina/tests/conftest.py | 3 -- guillotina/tests/memcached/test_cache.py | 13 ++++- .../tests/memcached/test_memcached_driver.py | 1 - guillotina/tests/memcached/test_metrics.py | 53 ++++++++++++++----- pytest.ini | 4 +- 7 files changed, 62 insertions(+), 25 deletions(-) create mode 100644 conftest.py diff --git a/conftest.py b/conftest.py new file mode 100644 index 000000000..2bfc72bdd --- /dev/null +++ b/conftest.py @@ -0,0 +1 @@ +pytest_plugins = ["guillotina.tests.fixtures", "pytest_docker_fixtures"] diff --git a/guillotina/contrib/memcached/driver.py b/guillotina/contrib/memcached/driver.py index 8af52f404..e37349bd2 100644 --- a/guillotina/contrib/memcached/driver.py +++ b/guillotina/contrib/memcached/driver.py @@ -118,7 +118,10 @@ def __init__(self, operation: str): counter=MEMCACHED_OPS, histogram=MEMCACHED_OPS_PROCESSING_TIME, labels={"type": operation}, - error_mappings={"timeout": asyncio.TimeoutError, "cancelled": asyncio.CancelledError}, + error_mappings={ + "timeout": asyncio.TimeoutError, + "cancelled": asyncio.CancelledError, + }, ) except ImportError: @@ -162,11 +165,13 @@ async def initialize(self, loop): if self.initialized is False: try: await self._connect() - self.initialized = True except Exception: # pragma: no cover logger.error("Error initializing memcached driver", exc_info=True) + return - if _SEND_METRICS is True: + self.initialized = True + + if _SEND_METRICS is True and self.client is not None: self._metrics_task = loop.create_task(metrics_probe(self.client)) async def _create_client(self, settings: Dict[str, Any]) -> emcache.Client: @@ -279,7 +284,6 @@ async def metrics_probe(client: emcache.Client, every: int = 30): def update_connection_pool_metrics( client: emcache.Client, last_state: Optional[emcache.ConnectionPoolMetrics] = None ) -> emcache.ConnectionPoolMetrics: - # Every node will have it's own label metrics = client.cluster_managment().connection_pool_metrics() for node, node_metrics in metrics.items(): diff --git a/guillotina/tests/conftest.py b/guillotina/tests/conftest.py index 7dd351dd2..1166a425f 100644 --- a/guillotina/tests/conftest.py +++ b/guillotina/tests/conftest.py @@ -25,6 +25,3 @@ "POSTGRES_USER": "postgres", }, ) - - -pytest_plugins = ["guillotina.tests.fixtures", "pytest_docker_fixtures"] diff --git a/guillotina/tests/memcached/test_cache.py b/guillotina/tests/memcached/test_cache.py index 543a716e9..eaa6d84f3 100644 --- a/guillotina/tests/memcached/test_cache.py +++ b/guillotina/tests/memcached/test_cache.py @@ -2,6 +2,12 @@ from guillotina.contrib.memcached.driver import MemcachedDriver except ImportError: MemcachedDriver = None # type: ignore + +try: + import pymemcache +except ModuleNotFoundError: + pymemcache = None + from guillotina.component import get_utility from guillotina.interfaces import ICacheUtility @@ -12,12 +18,17 @@ MEMCACHED_SETTINGS = { - "applications": ["guillotina", "guillotina.contrib.memcached", "guillotina.contrib.cache"], + "applications": [ + "guillotina", + "guillotina.contrib.memcached", + "guillotina.contrib.cache", + ], "cache": {"updates_channel": None, "driver": "guillotina.contrib.memcached"}, } @pytest.mark.skipif(MemcachedDriver is None, reason="emcache not installed") +@pytest.mark.skipif(pymemcache is None, reason="pymemcache not installed") @pytest.mark.app_settings(MEMCACHED_SETTINGS) async def test_cache_uses_memcached_driver_when_configured(memcached_container, guillotina_main): cache = get_utility(ICacheUtility) diff --git a/guillotina/tests/memcached/test_memcached_driver.py b/guillotina/tests/memcached/test_memcached_driver.py index 4f51b2e5f..fabc681c3 100644 --- a/guillotina/tests/memcached/test_memcached_driver.py +++ b/guillotina/tests/memcached/test_memcached_driver.py @@ -93,7 +93,6 @@ async def test_create_client_sets_configured_params(mocked_create_client, param, @pytest.mark.skipif(pymemcache is None, reason="pymemcache not installed") @pytest.mark.app_settings(MEMCACHED_SETTINGS) async def test_memcached_ops(memcached_container, guillotina_main, dont_probe_metrics): - __import__("pdb").set_trace() driver = await resolve_dotted_name("guillotina.contrib.memcached").get_driver() assert driver.initialized assert driver.client is not None diff --git a/guillotina/tests/memcached/test_metrics.py b/guillotina/tests/memcached/test_metrics.py index d5cbe9150..f22878d9b 100644 --- a/guillotina/tests/memcached/test_metrics.py +++ b/guillotina/tests/memcached/test_metrics.py @@ -4,28 +4,43 @@ MemcachedDriver = None # type: ignore from asyncmock import AsyncMock +from unittest import mock import pytest pytestmark = pytest.mark.asyncio +MEMCACHED_SETTINGS = {"applications": ["guillotina", "memcached", "guillotina.contrib.memcached"]} @pytest.mark.skipif(MemcachedDriver is None, reason="emcache not installed") +@pytest.mark.app_settings(MEMCACHED_SETTINGS) class TestMemcachedMetrics: async def test_connect_metric(self, metrics_registry, event_loop): driver = MemcachedDriver() - driver._client = AsyncMock() - await driver.initialize(event_loop) + client = AsyncMock() + with mock.patch.dict( + "guillotina.contrib.memcached.driver.app_settings", + {"memcached": {"hosts": ["localhost:11211"]}}, + clear=False, + ): + with mock.patch( + "guillotina.contrib.memcached.driver.emcache.create_client", + AsyncMock(return_value=client), + ): + with mock.patch("guillotina.contrib.memcached.driver._SEND_METRICS", False): + await driver.initialize(event_loop) assert ( metrics_registry.get_sample_value( - "guillotina_cache_memcached_ops_total", {"type": "connect", "error": "none"} + "guillotina_cache_memcached_ops_total", + {"type": "connect", "error": "none"}, ) == 1.0 ) assert ( metrics_registry.get_sample_value( - "guillotina_cache_memcached_ops_processing_time_seconds_sum", {"type": "connect"} + "guillotina_cache_memcached_ops_processing_time_seconds_sum", + {"type": "connect"}, ) > 0 ) @@ -43,7 +58,8 @@ async def test_set_memcached_metric(self, metrics_registry): ) assert ( metrics_registry.get_sample_value( - "guillotina_cache_memcached_ops_processing_time_seconds_sum", {"type": "set"} + "guillotina_cache_memcached_ops_processing_time_seconds_sum", + {"type": "set"}, ) > 0 ) @@ -60,7 +76,8 @@ async def test_get_memcached_metric(self, metrics_registry): ) assert ( metrics_registry.get_sample_value( - "guillotina_cache_memcached_ops_processing_time_seconds_sum", {"type": "get"} + "guillotina_cache_memcached_ops_processing_time_seconds_sum", + {"type": "get"}, ) > 0 ) @@ -72,13 +89,15 @@ async def test_get_miss_memcached_metric(self, metrics_registry): await driver.get("foo") assert ( metrics_registry.get_sample_value( - "guillotina_cache_memcached_ops_total", {"type": "get_miss", "error": "none"} + "guillotina_cache_memcached_ops_total", + {"type": "get_miss", "error": "none"}, ) == 1.0 ) assert ( metrics_registry.get_sample_value( - "guillotina_cache_memcached_ops_processing_time_seconds_sum", {"type": "get_miss"} + "guillotina_cache_memcached_ops_processing_time_seconds_sum", + {"type": "get_miss"}, ) > 0 ) @@ -89,13 +108,15 @@ async def test_delete_memcached_metric(self, metrics_registry): await driver.delete("foo") assert ( metrics_registry.get_sample_value( - "guillotina_cache_memcached_ops_total", {"type": "delete", "error": "none"} + "guillotina_cache_memcached_ops_total", + {"type": "delete", "error": "none"}, ) == 1.0 ) assert ( metrics_registry.get_sample_value( - "guillotina_cache_memcached_ops_processing_time_seconds_sum", {"type": "delete"} + "guillotina_cache_memcached_ops_processing_time_seconds_sum", + {"type": "delete"}, ) > 0 ) @@ -106,25 +127,29 @@ async def test_delete_many_memcached_metric(self, metrics_registry): await driver.delete_all(["foo", "bar"]) assert ( metrics_registry.get_sample_value( - "guillotina_cache_memcached_ops_total", {"type": "delete_many", "error": "none"} + "guillotina_cache_memcached_ops_total", + {"type": "delete_many", "error": "none"}, ) == 1.0 ) assert ( metrics_registry.get_sample_value( - "guillotina_cache_memcached_ops_processing_time_seconds_sum", {"type": "delete_many"} + "guillotina_cache_memcached_ops_processing_time_seconds_sum", + {"type": "delete_many"}, ) > 0 ) assert ( metrics_registry.get_sample_value( - "guillotina_cache_memcached_ops_total", {"type": "delete", "error": "none"} + "guillotina_cache_memcached_ops_total", + {"type": "delete", "error": "none"}, ) == 2.0 ) assert ( metrics_registry.get_sample_value( - "guillotina_cache_memcached_ops_processing_time_seconds_sum", {"type": "delete"} + "guillotina_cache_memcached_ops_processing_time_seconds_sum", + {"type": "delete"}, ) > 0 ) diff --git a/pytest.ini b/pytest.ini index a07fd9017..05673e38e 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,9 +1,9 @@ # content of pytest.ini [pytest] -norecursedirs = cookiecutter +norecursedirs = cookiecutter build _build filterwarnings = ignore::ResourceWarning ignore::DeprecationWarning ignore::PendingDeprecationWarning markers = - app_settings: customize app_settings for tests \ No newline at end of file + app_settings: customize app_settings for tests From fc66deece0128243e1e6b5066b7eec0b6d9158bb Mon Sep 17 00:00:00 2001 From: nilbacardit26 Date: Mon, 30 Mar 2026 12:10:15 +0200 Subject: [PATCH 19/31] upgradung uvicorn, disabling server_headers, let guill do it --- guillotina/commands/server.py | 3 ++- guillotina/tests/fixtures.py | 2 +- requirements.txt | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/guillotina/commands/server.py b/guillotina/commands/server.py index ab454de06..b5174d08b 100644 --- a/guillotina/commands/server.py +++ b/guillotina/commands/server.py @@ -23,12 +23,13 @@ async def run(self, arguments, settings, app): from uvicorn import Server # type: ignore from uvicorn.config import LOGGING_CONFIG # type: ignore + uvicorn_settings = {"server_header": False, **app.server_settings.get("uvicorn", {})} config = Config( app, host=host, port=port, log_config=loggers or LOGGING_CONFIG, - **app.server_settings.get("uvicorn", {}), + **uvicorn_settings, ) server = Server(config) await server.serve() diff --git a/guillotina/tests/fixtures.py b/guillotina/tests/fixtures.py index ff5480c78..54403591e 100644 --- a/guillotina/tests/fixtures.py +++ b/guillotina/tests/fixtures.py @@ -500,7 +500,7 @@ async def app(event_loop, db, request): from uvicorn import Config, Server - config = Config(app, host=host, port=port, lifespan="on") + config = Config(app, host=host, port=port, lifespan="on", server_header=False) server = Server(config=config) task = asyncio.ensure_future(server.serve(), loop=event_loop) diff --git a/requirements.txt b/requirements.txt index 41d0ff7f9..69e981900 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ PyYaml>=5.1 six==1.11.0 orjson>=3,<4 zope.interface==5.1.0 -uvicorn==0.17.6 +uvicorn==0.42.0 argon2-cffi==18.3.0 backoff==1.10.0 prometheus-client==0.8.0 From ab88f58d9dd335a82ae54c077a9a3867a32022d2 Mon Sep 17 00:00:00 2001 From: nilbacardit26 Date: Mon, 30 Mar 2026 12:21:31 +0200 Subject: [PATCH 20/31] dropping test support for 3.8 and 3.9, adding 3.12 --- .github/workflows/continuous-integration.yml | 4 ++-- requirements.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 8b4950b0e..d1c49f433 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.8, 3.9, '3.10', '3.11'] + python-version: ['3.10', '3.11', '3.12'] steps: - name: Checkout the repository @@ -34,7 +34,7 @@ jobs: strategy: matrix: - python-version: [3.8, 3.9, '3.10', '3.11'] + python-version: ['3.10', '3.11', '3.12'] database: ["DUMMY", "postgres", "cockroachdb"] db_schema: ["custom", "public"] exclude: diff --git a/requirements.txt b/requirements.txt index 69e981900..a2c121025 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,7 +16,7 @@ uvicorn==0.42.0 argon2-cffi==18.3.0 backoff==1.10.0 prometheus-client==0.8.0 -typing_extensions==3.7.4.3 +typing_extensions==4.14.1 types-chardet==0.1.5 types-docutils==0.17.0 From 40674c04c64e14a6118e6b2250d76a764c82a024 Mon Sep 17 00:00:00 2001 From: nilbacardit26 Date: Mon, 30 Mar 2026 12:28:53 +0200 Subject: [PATCH 21/31] passing tests --- contrib-requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib-requirements.txt b/contrib-requirements.txt index 5b9666c16..3d90a2a33 100644 --- a/contrib-requirements.txt +++ b/contrib-requirements.txt @@ -3,10 +3,10 @@ html2text==2019.8.11 aiosmtplib<4.0.0; python_version <= "3.8" aiosmtplib>=4.0.0; python_version >= "3.9" pre-commit==1.18.2 -flake8==5.0.4 +flake8==6.1.0 codecov==2.1.13 mypy-zope==1.0.11 -black==22.3.0 +black==24.10.0 isort==4.3.21 jinja2==2.11.3 MarkupSafe<2.1.0 From 33b0943762c1d06e52f84820d630b7231d61deac Mon Sep 17 00:00:00 2001 From: nilbacardit26 Date: Mon, 30 Mar 2026 12:30:59 +0200 Subject: [PATCH 22/31] passing tests --- guillotina/renderers.py | 4 ++-- guillotina/schema/_bootstrapfields.py | 2 +- guillotina/tests/test_adapters.py | 2 +- guillotina/tests/test_server.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/guillotina/renderers.py b/guillotina/renderers.py index c521da8b7..b89f74d5f 100644 --- a/guillotina/renderers.py +++ b/guillotina/renderers.py @@ -13,7 +13,7 @@ def guillotina_json_default(obj): if isinstance(obj, str): - if type(obj) != str: # e.g, i18n.Message() + if type(obj) is not str: # e.g, i18n.Message() return str(obj) elif isinstance(obj, complex): return [obj.real, obj.imag] @@ -22,7 +22,7 @@ def guillotina_json_default(obj): elif isinstance(obj, InterfaceClass): return [x.__module__ + "." + x.__name__ for x in obj.__iro__] # noqa elif isinstance(obj, dict): - if type(obj) != dict: # e.g. collections.OrderedDict + if type(obj) is not dict: # e.g. collections.OrderedDict return dict(obj) try: diff --git a/guillotina/schema/_bootstrapfields.py b/guillotina/schema/_bootstrapfields.py index fa8218937..fdac840c3 100644 --- a/guillotina/schema/_bootstrapfields.py +++ b/guillotina/schema/_bootstrapfields.py @@ -199,7 +199,7 @@ def validate(self, value): def __eq__(self, other): # should be the same type - if type(self) != type(other): + if type(self) is not type(other): return False # should have the same properties diff --git a/guillotina/tests/test_adapters.py b/guillotina/tests/test_adapters.py index 3a11f6e6e..dcc70e3cd 100644 --- a/guillotina/tests/test_adapters.py +++ b/guillotina/tests/test_adapters.py @@ -87,7 +87,7 @@ def test_vocabulary(dummy_request): vocab = SimpleVocabulary.fromItems((("Foo", "id_foo"), ("Bar", "id_bar"))) res = json_compatible(vocab) - assert type(res) == list + assert isinstance(res, list) def test_SerializeFactoryToJson(dummy_request): # noqa: N802 diff --git a/guillotina/tests/test_server.py b/guillotina/tests/test_server.py index eb5087e20..4aa6593f2 100644 --- a/guillotina/tests/test_server.py +++ b/guillotina/tests/test_server.py @@ -11,7 +11,7 @@ def test_make_app(dummy_guillotina): assert dummy_guillotina is not None - assert type(dummy_guillotina.router) == TraversalRouter + assert isinstance(dummy_guillotina.router, TraversalRouter) @pytest.mark.asyncio From 4efc9faf33c7bbaa36e289d79e82c2cf61024b43 Mon Sep 17 00:00:00 2001 From: nilbacardit26 Date: Mon, 30 Mar 2026 12:39:17 +0200 Subject: [PATCH 23/31] new isort and black checkers --- .github/workflows/continuous-integration.yml | 2 +- contrib-requirements.txt | 2 +- guillotina/__init__.py | 1 - guillotina/_settings.py | 1 - guillotina/annotations.py | 1 - guillotina/api/addons.py | 1 - guillotina/api/files.py | 1 + guillotina/api/registry.py | 1 - guillotina/api/search.py | 1 - guillotina/api/service.py | 1 - guillotina/asgi.py | 1 - guillotina/auth/extractors.py | 1 - guillotina/auth/recaptcha.py | 1 - guillotina/auth/users.py | 1 - guillotina/auth/validators.py | 1 - guillotina/behaviors/dublincore.py | 1 - guillotina/behaviors/instance.py | 1 - guillotina/behaviors/properties.py | 1 - guillotina/catalog/__init__.py | 1 - guillotina/catalog/catalog.py | 1 - guillotina/catalog/utils.py | 1 - guillotina/commands/__init__.py | 1 - guillotina/commands/crypto.py | 1 - guillotina/commands/migrate.py | 1 - guillotina/commands/run.py | 1 - guillotina/commands/vacuum.py | 1 - guillotina/component/__init__.py | 1 - guillotina/component/_api.py | 3 +- guillotina/component/_compat.py | 1 - guillotina/component/event.py | 1 - guillotina/component/globalregistry.py | 7 +- guillotina/component/interfaces.py | 1 - guillotina/component/tests/test___init__.py | 17 +- guillotina/component/tests/test__api.py | 150 +++++++++-------- .../component/tests/test__declaration.py | 8 +- guillotina/component/tests/test_event.py | 4 +- guillotina/component/tests/test_factory.py | 8 +- .../component/tests/test_globalregistry.py | 46 ++--- guillotina/component/tests/test_registry.py | 12 +- guillotina/configure/__init__.py | 5 +- guillotina/configure/behaviors.py | 1 - guillotina/configure/component.py | 1 - guillotina/content.py | 1 - guillotina/contrib/cache/__init__.py | 1 - guillotina/contrib/cache/lru.pyi | 1 - guillotina/contrib/cache/memcache.py | 1 - guillotina/contrib/cache/strategy.py | 1 - guillotina/contrib/cache/utility.py | 1 - guillotina/contrib/catalog/pg/__init__.py | 1 - guillotina/contrib/catalog/pg/parser.py | 1 - guillotina/contrib/catalog/pg/utility.py | 1 - guillotina/contrib/dbusers/__init__.py | 1 - guillotina/contrib/dbusers/adapters.py | 1 - guillotina/contrib/dbusers/install.py | 1 - guillotina/contrib/dbusers/permissions.py | 1 - .../contrib/dbusers/services/__init__.py | 1 - guillotina/contrib/dbusers/services/groups.py | 1 - guillotina/contrib/dyncontent/__init__.py | 1 - guillotina/contrib/dyncontent/subscriber.py | 1 - .../contrib/email_validation/__init__.py | 1 - .../contrib/email_validation/utility.py | 1 - guillotina/contrib/email_validation/utils.py | 1 - guillotina/contrib/image/api.py | 1 - guillotina/contrib/mailer/__init__.py | 1 - guillotina/contrib/mailer/encoding.py | 1 - guillotina/contrib/mailer/utility.py | 1 - guillotina/contrib/mcp/__init__.py | 1 - guillotina/contrib/mcp/backend.py | 1 - guillotina/contrib/mcp/permissions.py | 1 - guillotina/contrib/mcp/services.py | 3 +- guillotina/contrib/mcp/tools.py | 1 - guillotina/contrib/memcached/__init__.py | 1 - guillotina/contrib/memcached/driver.py | 4 +- guillotina/contrib/pubsub/utility.py | 1 - guillotina/contrib/redis/__init__.py | 1 - guillotina/contrib/redis/dm.py | 1 - guillotina/contrib/redis/driver.py | 1 - guillotina/contrib/redis_session/utility.py | 1 - guillotina/contrib/swagger/__init__.py | 1 - guillotina/contrib/swagger/services.py | 1 - guillotina/contrib/templates/__init__.py | 1 - guillotina/contrib/templates/permissions.py | 1 - guillotina/contrib/templates/utility.py | 1 - guillotina/contrib/vocabularies/countries.py | 1 - guillotina/contrib/vocabularies/languages.py | 1 - guillotina/contrib/workflows/__init__.py | 1 - guillotina/contrib/workflows/interfaces.py | 1 - guillotina/contrib/workflows/permissions.py | 1 - .../{{cookiecutter.package_name}}/setup.py | 1 - .../{{cookiecutter.package_name}}/__init__.py | 1 - .../tests/test_install.py | 1 - guillotina/cors.py | 1 - guillotina/db/cache/base.py | 1 - guillotina/db/cache/dummy.py | 15 +- guillotina/db/orm/base.py | 1 - guillotina/db/orm/interfaces.py | 4 +- guillotina/db/storages/cockroach.py | 1 - guillotina/db/storages/dummy.py | 1 - guillotina/db/storages/pg.py | 1 - guillotina/db/storages/vacuum.py | 1 - guillotina/db/transaction.py | 4 +- guillotina/db/transaction_manager.py | 1 - guillotina/db/uid.py | 1 - guillotina/documentation/sphinx/__init__.py | 1 - guillotina/entrypoint.py | 1 - guillotina/factory/app.py | 1 - guillotina/factory/content.py | 1 - guillotina/fields/annotation.py | 1 - guillotina/files/__init__.py | 1 - guillotina/files/manager.py | 1 - guillotina/glogging.py | 2 +- guillotina/gtypes.py | 1 - guillotina/interfaces/__init__.py | 5 +- guillotina/interfaces/content.py | 1 - guillotina/interfaces/json.py | 1 - guillotina/interfaces/misc.py | 6 +- guillotina/interfaces/registry.py | 1 - guillotina/interfaces/security.py | 1 - guillotina/json/definitions.py | 1 - guillotina/json/deserialize_content.py | 1 - guillotina/json/deserialize_value.py | 1 - guillotina/json/serialize_content.py | 1 - guillotina/json/serialize_schema_field.py | 1 - guillotina/json/serialize_value.py | 1 - guillotina/json/utils.py | 1 - guillotina/metrics.py | 1 - guillotina/permissions.py | 1 - guillotina/registry.py | 1 - guillotina/routes.py | 1 - guillotina/schema/__init__.py | 1 - guillotina/schema/_bootstrapfields.py | 1 - guillotina/schema/_field.py | 1 - guillotina/schema/_messageid.py | 1 - guillotina/schema/accessors.py | 1 - guillotina/schema/fieldproperty.py | 1 - guillotina/schema/interfaces.py | 1 - guillotina/schema/tests/states.py | 1 - .../schema/tests/test__bootstrapfields.py | 6 +- guillotina/schema/tests/test__field.py | 157 +++++++++--------- guillotina/schema/tests/test_accessors.py | 12 +- guillotina/schema/tests/test_fieldproperty.py | 10 +- guillotina/schema/tests/test_interfaces.py | 20 +-- guillotina/schema/tests/test_schema.py | 12 +- guillotina/schema/tests/test_states.py | 10 +- guillotina/schema/tests/test_vocabulary.py | 22 +-- guillotina/schema/vocabulary.py | 1 - guillotina/security/__init__.py | 1 - guillotina/security/policy.py | 1 - guillotina/security/utils.py | 2 +- guillotina/subscribers.py | 1 - guillotina/task_vars.py | 1 - guillotina/test_package.py | 1 - guillotina/testing.py | 1 - guillotina/tests/__init__.py | 1 - guillotina/tests/cache/test_cache_memory.py | 1 - guillotina/tests/cache/test_cache_pubsub.py | 1 - guillotina/tests/cache/test_cache_store.py | 1 - guillotina/tests/cache/test_cache_txn.py | 1 - guillotina/tests/cache/test_utils.py | 1 + guillotina/tests/conftest.py | 1 - guillotina/tests/dbusers/test_api.py | 1 - .../tests/dbusers/test_manage_groups.py | 1 - guillotina/tests/dbusers/test_manage_users.py | 1 - guillotina/tests/dbusers/test_registration.py | 1 - .../tests/dbusers/test_reset_password.py | 1 - guillotina/tests/dbusers/test_security.py | 1 - guillotina/tests/dbusers/test_setup.py | 1 - guillotina/tests/dyncontent/test_dynapi.py | 1 - guillotina/tests/fixtures.py | 6 +- guillotina/tests/image/__init__.py | 1 - guillotina/tests/image/test_field.py | 1 - guillotina/tests/image/test_scale.py | 1 - guillotina/tests/mailer/test_mailer.py | 1 - guillotina/tests/mcp/test_mcp.py | 1 - guillotina/tests/memcached/test_cache.py | 1 - .../tests/memcached/test_memcached_driver.py | 4 +- guillotina/tests/memcached/test_metrics.py | 1 - guillotina/tests/redis/test_driver.py | 1 - .../tests/redis_session/test_session.py | 1 - guillotina/tests/test_annotations.py | 1 - guillotina/tests/test_api.py | 1 - guillotina/tests/test_attachment.py | 1 - guillotina/tests/test_auth.py | 1 - guillotina/tests/test_blob.py | 1 - guillotina/tests/test_catalog.py | 1 - guillotina/tests/test_cockroach.py | 1 - guillotina/tests/test_commands.py | 1 - guillotina/tests/test_configure.py | 3 +- guillotina/tests/test_configure_component.py | 65 ++++---- guillotina/tests/test_content.py | 1 - guillotina/tests/test_contentapi.py | 1 - guillotina/tests/test_cors.py | 1 - guillotina/tests/test_dbobjects.py | 1 - guillotina/tests/test_dynamic_schema.py | 1 - guillotina/tests/test_extrafieldattr.py | 1 - guillotina/tests/test_guillo.py | 1 - guillotina/tests/test_info.py | 1 - guillotina/tests/test_login.py | 1 - guillotina/tests/test_metrics.py | 1 - guillotina/tests/test_postgres.py | 1 - guillotina/tests/test_queue.py | 1 - guillotina/tests/test_request.py | 1 - guillotina/tests/test_security.py | 1 - guillotina/tests/test_serialize.py | 7 +- guillotina/tests/test_static.py | 1 - guillotina/tests/test_storages.py | 1 - guillotina/tests/test_swagger.py | 1 - guillotina/tests/test_transactions.py | 1 - guillotina/tests/test_ws.py | 1 - .../tests/vocabularies/test_vocabularies.py | 1 - .../tests/workflows/test_workflow_basic.py | 1 - guillotina/transactions.py | 1 - guillotina/traversal.py | 7 +- guillotina/utils/__init__.py | 5 +- guillotina/utils/content.py | 1 - guillotina/utils/crypto.py | 1 - guillotina/utils/misc.py | 1 - setup.cfg | 3 +- 218 files changed, 342 insertions(+), 497 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index d1c49f433..705875232 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -26,7 +26,7 @@ jobs: - name: Run pre-checks run: | flake8 guillotina --config=setup.cfg - isort -c -rc guillotina/ + isort --check-only guillotina/ black --check --verbose guillotina # Job to run tests tests: diff --git a/contrib-requirements.txt b/contrib-requirements.txt index 3d90a2a33..0f720c6ee 100644 --- a/contrib-requirements.txt +++ b/contrib-requirements.txt @@ -7,7 +7,7 @@ flake8==6.1.0 codecov==2.1.13 mypy-zope==1.0.11 black==24.10.0 -isort==4.3.21 +isort==5.13.2 jinja2==2.11.3 MarkupSafe<2.1.0 pytz==2020.1 diff --git a/guillotina/__init__.py b/guillotina/__init__.py index 8cedb03a2..1845a5967 100644 --- a/guillotina/__init__.py +++ b/guillotina/__init__.py @@ -11,7 +11,6 @@ import os import pkg_resources - __version__ = pkg_resources.get_distribution("guillotina").version diff --git a/guillotina/_settings.py b/guillotina/_settings.py index be8b05a5b..d63179bf3 100644 --- a/guillotina/_settings.py +++ b/guillotina/_settings.py @@ -7,7 +7,6 @@ import pickle import string - app_settings: Dict[str, Any] = { "debug": False, "databases": [], diff --git a/guillotina/annotations.py b/guillotina/annotations.py index 6db0257d9..5d9091e87 100644 --- a/guillotina/annotations.py +++ b/guillotina/annotations.py @@ -12,7 +12,6 @@ import logging - logger = logging.getLogger("guillotina") _marker = object() diff --git a/guillotina/api/addons.py b/guillotina/api/addons.py index 380237b60..b2ad81631 100644 --- a/guillotina/api/addons.py +++ b/guillotina/api/addons.py @@ -8,7 +8,6 @@ from guillotina.response import ErrorResponse from guillotina.utils import get_registry - _ = MessageFactory("guillotina") diff --git a/guillotina/api/files.py b/guillotina/api/files.py index a024867ec..80af7159f 100644 --- a/guillotina/api/files.py +++ b/guillotina/api/files.py @@ -50,6 +50,7 @@ def _traversed_file_doc(summary, parameters=None, responses=None): {"name": "UPLOAD-METADATA", "in": "headers", "required": False, "schema": {"type": "string"}}, ] + # Static File @configure.service(context=IStaticFile, method="GET", permission="guillotina.AccessContent") class FileGET(DownloadService): diff --git a/guillotina/api/registry.py b/guillotina/api/registry.py index b820f423b..d728e6bf6 100644 --- a/guillotina/api/registry.py +++ b/guillotina/api/registry.py @@ -17,7 +17,6 @@ from guillotina.utils import import_class from guillotina.utils import resolve_dotted_name - _ = MessageFactory("guillotina") diff --git a/guillotina/api/search.py b/guillotina/api/search.py index 98fae926c..a5e8a7376 100644 --- a/guillotina/api/search.py +++ b/guillotina/api/search.py @@ -8,7 +8,6 @@ import logging - logger = logging.getLogger("guillotina") QUERY_PARAMETERS = [ diff --git a/guillotina/api/service.py b/guillotina/api/service.py index b42f062a8..13ac485b1 100644 --- a/guillotina/api/service.py +++ b/guillotina/api/service.py @@ -20,7 +20,6 @@ import json import jsonschema - logger = glogging.getLogger("guillotina") diff --git a/guillotina/asgi.py b/guillotina/asgi.py index bf3f142ec..e9b14382d 100644 --- a/guillotina/asgi.py +++ b/guillotina/asgi.py @@ -19,7 +19,6 @@ import traceback import uuid - logger = glogging.getLogger("guillotina") diff --git a/guillotina/auth/extractors.py b/guillotina/auth/extractors.py index ab24d54c2..e545de274 100644 --- a/guillotina/auth/extractors.py +++ b/guillotina/auth/extractors.py @@ -6,7 +6,6 @@ import logging import time - logger = logging.getLogger("guillotina") diff --git a/guillotina/auth/recaptcha.py b/guillotina/auth/recaptcha.py index 0b76e7726..d18a7b775 100644 --- a/guillotina/auth/recaptcha.py +++ b/guillotina/auth/recaptcha.py @@ -3,7 +3,6 @@ import logging - logger = logging.getLogger("guillotina") RECAPTCHA_VALIDATION_URL = "https://www.google.com/recaptcha/api/siteverify" diff --git a/guillotina/auth/users.py b/guillotina/auth/users.py index b4fa65f37..ef3519c1e 100644 --- a/guillotina/auth/users.py +++ b/guillotina/auth/users.py @@ -3,7 +3,6 @@ from typing import Optional from zope.interface import implementer - ROOT_USER_ID = "root" ANONYMOUS_USER_ID = "Anonymous User" diff --git a/guillotina/auth/validators.py b/guillotina/auth/validators.py index 8a4601b74..fef4131b9 100644 --- a/guillotina/auth/validators.py +++ b/guillotina/auth/validators.py @@ -19,7 +19,6 @@ import logging import uuid - ph = argon2.PasswordHasher() _pw_auth_validator = LRU(100) diff --git a/guillotina/behaviors/dublincore.py b/guillotina/behaviors/dublincore.py index 260656251..0a76d4fee 100644 --- a/guillotina/behaviors/dublincore.py +++ b/guillotina/behaviors/dublincore.py @@ -8,7 +8,6 @@ from guillotina.fields.patch import PatchField from zope.interface import Interface - _utc = tzutc() # never expires diff --git a/guillotina/behaviors/instance.py b/guillotina/behaviors/instance.py index dd3e0d60f..052727ccf 100644 --- a/guillotina/behaviors/instance.py +++ b/guillotina/behaviors/instance.py @@ -7,7 +7,6 @@ from typing import Tuple from zope.interface import implementer - _default = object() diff --git a/guillotina/behaviors/properties.py b/guillotina/behaviors/properties.py index 883ec3154..9b274290a 100644 --- a/guillotina/behaviors/properties.py +++ b/guillotina/behaviors/properties.py @@ -1,6 +1,5 @@ from guillotina.schema.utils import get_default_from_schema - _EMPTY = object() diff --git a/guillotina/catalog/__init__.py b/guillotina/catalog/__init__.py index d16b471a5..f35b18b94 100644 --- a/guillotina/catalog/__init__.py +++ b/guillotina/catalog/__init__.py @@ -7,7 +7,6 @@ from guillotina.utils import get_content_depth from guillotina.utils import get_content_path - global_roles_for_permission = role_permission_manager.get_roles_for_permission diff --git a/guillotina/catalog/catalog.py b/guillotina/catalog/catalog.py index c19d4d946..b7abc5560 100644 --- a/guillotina/catalog/catalog.py +++ b/guillotina/catalog/catalog.py @@ -27,7 +27,6 @@ import logging import typing - logger = logging.getLogger("guillotina") global_principal_permission_setting = principal_permission_manager.get_setting diff --git a/guillotina/catalog/utils.py b/guillotina/catalog/utils.py index 83ce298da..5198e76f0 100644 --- a/guillotina/catalog/utils.py +++ b/guillotina/catalog/utils.py @@ -18,7 +18,6 @@ import logging import typing - logger = logging.getLogger("guillotina") diff --git a/guillotina/commands/__init__.py b/guillotina/commands/__init__.py index 8cdb84253..96c6b22f5 100644 --- a/guillotina/commands/__init__.py +++ b/guillotina/commands/__init__.py @@ -14,7 +14,6 @@ import sys import yaml - try: import uvloop # type: ignore diff --git a/guillotina/commands/crypto.py b/guillotina/commands/crypto.py index 1808ac486..b82b63979 100644 --- a/guillotina/commands/crypto.py +++ b/guillotina/commands/crypto.py @@ -3,7 +3,6 @@ import logging - logger = logging.getLogger("guillotina") diff --git a/guillotina/commands/migrate.py b/guillotina/commands/migrate.py index 505ba79b8..b84576063 100644 --- a/guillotina/commands/migrate.py +++ b/guillotina/commands/migrate.py @@ -7,7 +7,6 @@ import logging - logger = logging.getLogger("guillotina") diff --git a/guillotina/commands/run.py b/guillotina/commands/run.py index 22145bbe6..d53de91e8 100644 --- a/guillotina/commands/run.py +++ b/guillotina/commands/run.py @@ -8,7 +8,6 @@ import logging import os - logger = logging.getLogger("guillotina") diff --git a/guillotina/commands/vacuum.py b/guillotina/commands/vacuum.py index 56c72dacb..80dd52a1a 100644 --- a/guillotina/commands/vacuum.py +++ b/guillotina/commands/vacuum.py @@ -5,7 +5,6 @@ import logging - logger = logging.getLogger("guillotina") diff --git a/guillotina/component/__init__.py b/guillotina/component/__init__.py index 9ec59b6a6..0cc99c5e0 100644 --- a/guillotina/component/__init__.py +++ b/guillotina/component/__init__.py @@ -41,7 +41,6 @@ from guillotina.component.interfaces import IFactory from zope.interface import moduleProvides - # b/w compat imports. Will be removed in 3.0 getMultiAdapter = get_multi_adapter queryMultiAdapter = query_multi_adapter diff --git a/guillotina/component/_api.py b/guillotina/component/_api.py index da4bc423a..05aab9970 100644 --- a/guillotina/component/_api.py +++ b/guillotina/component/_api.py @@ -27,7 +27,6 @@ import zope.interface.interface - _MISSING = object() @@ -276,7 +275,7 @@ def get_factory_interfaces(name, context=None): def get_factories_for(interface, context=None): """Return info on all factories implementing the given interface.""" utils = get_component_registry(context) - for (name, factory) in utils.getUtilitiesFor(IFactory): + for name, factory in utils.getUtilitiesFor(IFactory): interfaces = factory.get_interfaces() try: if interfaces.isOrExtends(interface): diff --git a/guillotina/component/_compat.py b/guillotina/component/_compat.py index dab81ece7..4e4587451 100644 --- a/guillotina/component/_compat.py +++ b/guillotina/component/_compat.py @@ -17,7 +17,6 @@ import sys import types - CLASS_TYPES = (type,) PYTHON3 = True diff --git a/guillotina/component/event.py b/guillotina/component/event.py index 3bea23064..7839c7dff 100644 --- a/guillotina/component/event.py +++ b/guillotina/component/event.py @@ -16,7 +16,6 @@ from guillotina.component._api import subscribers as component_subscribers from guillotina.component.interfaces import ComponentLookupError - async_subscribers = [] sync_subscribers = [] diff --git a/guillotina/component/globalregistry.py b/guillotina/component/globalregistry.py index 5e026fb2a..c58f603b5 100644 --- a/guillotina/component/globalregistry.py +++ b/guillotina/component/globalregistry.py @@ -26,7 +26,6 @@ import os import time - profile_logger = logging.getLogger("guillotina.profile") @@ -55,9 +54,11 @@ def subscribers(self, objects, provided): class DebugGuillotinaAdapterLookup(GuillotinaAdapterLookup): # pragma: no cover @profilable async def asubscribers(self, objects, provided): - from guillotina.utils import get_current_request, get_authenticated_user_id, get_dotted_name - from guillotina.exceptions import RequestNotFound from guillotina import task_vars + from guillotina.exceptions import RequestNotFound + from guillotina.utils import get_authenticated_user_id + from guillotina.utils import get_current_request + from guillotina.utils import get_dotted_name if len(objects) > 1: event = get_dotted_name(objects[1]) diff --git a/guillotina/component/interfaces.py b/guillotina/component/interfaces.py index a4a7ebebe..bcdb4aaf8 100644 --- a/guillotina/component/interfaces.py +++ b/guillotina/component/interfaces.py @@ -38,7 +38,6 @@ from zope.interface.interfaces import RegistrationEvent from zope.interface.interfaces import Unregistered - # fmt: on diff --git a/guillotina/component/tests/test___init__.py b/guillotina/component/tests/test___init__.py index 2d8e5dd14..72cd81819 100644 --- a/guillotina/component/tests/test___init__.py +++ b/guillotina/component/tests/test___init__.py @@ -17,15 +17,17 @@ class Test_package(unittest.TestCase): def test_module_conforms_to_IComponentArchitecture(self): - from zope.interface.verify import verifyObject from guillotina.component.interfaces import IComponentArchitecture + from zope.interface.verify import verifyObject + import guillotina.component as zc verifyObject(IComponentArchitecture, zc) def test_module_conforms_to_IComponentRegistrationConvenience(self): - from zope.interface.verify import verifyObject from guillotina.component.interfaces import IComponentRegistrationConvenience + from zope.interface.verify import verifyObject + import guillotina.component as zc verifyObject(IComponentRegistrationConvenience, zc) @@ -33,7 +35,8 @@ def test_module_conforms_to_IComponentRegistrationConvenience(self): class Test_Interface_call(unittest.TestCase): - from guillotina.component.testing import setUp, tearDown + from guillotina.component.testing import setUp + from guillotina.component.testing import tearDown def test_miss(self): from zope.interface import Interface @@ -53,9 +56,9 @@ class IFoo(Interface): self.assertTrue(IFoo(object(), marker) is marker) def test_hit(self): - from zope.interface import Interface - from zope.interface import implementer from guillotina.component import get_global_components + from zope.interface import implementer + from zope.interface import Interface class IFoo(Interface): pass @@ -79,9 +82,9 @@ def __init__(self, context): self.assertTrue(adapted.context is bar) def test_hit_registered_for_None(self): - from zope.interface import Interface - from zope.interface import implementer from guillotina.component import get_global_components + from zope.interface import implementer + from zope.interface import Interface class IFoo(Interface): pass diff --git a/guillotina/component/tests/test__api.py b/guillotina/component/tests/test__api.py index 17e6419d3..13dd39a79 100644 --- a/guillotina/component/tests/test__api.py +++ b/guillotina/component/tests/test__api.py @@ -17,7 +17,8 @@ class Test_get_component_registry(unittest.TestCase): - from guillotina.component.testing import setUp, tearDown + from guillotina.component.testing import setUp + from guillotina.component.testing import tearDown def _callFUT(self, *args, **kw): from guillotina.component._api import get_component_registry @@ -53,9 +54,9 @@ def test_get_component_registry_w_invalid_context_no_adapter(self): self.assertRaises(ComponentLookupError, self._callFUT, object()) def test_get_component_registry_w_invalid_context_w_adapter(self): - from zope.interface import Interface from guillotina.component.globalregistry import get_global_components from guillotina.component.interfaces import IComponentLookup + from zope.interface import Interface gsm = get_global_components() sm = object() @@ -69,7 +70,8 @@ def _adapt(x): class Test_get_adapter(unittest.TestCase): - from guillotina.component.testing import setUp, tearDown + from guillotina.component.testing import setUp + from guillotina.component.testing import tearDown def _callFUT(self, *args, **kw): from guillotina.component import get_adapter @@ -77,8 +79,8 @@ def _callFUT(self, *args, **kw): return get_adapter(*args, **kw) def test_anonymous_nonesuch(self): - from zope.interface import Interface from guillotina.component.interfaces import ComponentLookupError + from zope.interface import Interface class IFoo(Interface): pass @@ -86,8 +88,8 @@ class IFoo(Interface): self.assertRaises(ComponentLookupError, self._callFUT, object(), IFoo, "") def test_named_nonesuch(self): - from zope.interface import Interface from guillotina.component.interfaces import ComponentLookupError + from zope.interface import Interface class IFoo(Interface): pass @@ -95,9 +97,9 @@ class IFoo(Interface): self.assertRaises(ComponentLookupError, self._callFUT, object(), IFoo, "bar") def test_anonymous_hit(self): - from zope.interface import Interface - from zope.interface import implementer from guillotina.component import get_global_components + from zope.interface import implementer + from zope.interface import Interface class IFoo(Interface): pass @@ -121,9 +123,9 @@ def __init__(self, context): self.assertTrue(adapted.context is bar) def test_anonymous_hit_registered_for_None(self): - from zope.interface import Interface - from zope.interface import implementer from guillotina.component import get_global_components + from zope.interface import implementer + from zope.interface import Interface class IFoo(Interface): pass @@ -140,9 +142,9 @@ def __init__(self, context): self.assertTrue(adapted.context is ctx) def test_named_hit(self): - from zope.interface import Interface - from zope.interface import implementer from guillotina.component import get_global_components + from zope.interface import implementer + from zope.interface import Interface class IFoo(Interface): pass @@ -168,7 +170,8 @@ def __init__(self, context): class Test_query_adapter(unittest.TestCase): - from guillotina.component.testing import setUp, tearDown + from guillotina.component.testing import setUp + from guillotina.component.testing import tearDown def _callFUT(self, *args, **kw): from guillotina.component import query_adapter @@ -192,9 +195,9 @@ class IFoo(Interface): self.assertEqual(self._callFUT(object(), IFoo, "bar"), None) def test_anonymous_hit(self): - from zope.interface import Interface - from zope.interface import implementer from guillotina.component import get_global_components + from zope.interface import implementer + from zope.interface import Interface class IFoo(Interface): pass @@ -218,9 +221,9 @@ def __init__(self, context): self.assertTrue(adapted.context is bar) def test_named_hit(self): - from zope.interface import Interface - from zope.interface import implementer from guillotina.component import get_global_components + from zope.interface import implementer + from zope.interface import Interface class IFoo(Interface): pass @@ -244,11 +247,11 @@ def __init__(self, context): self.assertTrue(adapted.context is bar) def test_nested(self): - from zope.interface import Interface - from zope.interface import implementer - from zope.interface.registry import Components from guillotina.component import get_global_components from guillotina.component.tests.examples import ConformsToIComponentLookup + from zope.interface import implementer + from zope.interface import Interface + from zope.interface.registry import Components class IFoo(Interface): pass @@ -286,7 +289,8 @@ def __init__(self, sm): class Test_get_multi_adapter(unittest.TestCase): - from guillotina.component.testing import setUp, tearDown + from guillotina.component.testing import setUp + from guillotina.component.testing import tearDown def _callFUT(self, *args, **kw): from guillotina.component import get_multi_adapter @@ -294,8 +298,8 @@ def _callFUT(self, *args, **kw): return get_multi_adapter(*args, **kw) def test_anonymous_nonesuch(self): - from zope.interface import Interface from guillotina.component.interfaces import ComponentLookupError + from zope.interface import Interface class IFoo(Interface): pass @@ -303,8 +307,8 @@ class IFoo(Interface): self.assertRaises(ComponentLookupError, self._callFUT, (object(), object()), IFoo, "") def test_named_nonesuch(self): - from zope.interface import Interface from guillotina.component.interfaces import ComponentLookupError + from zope.interface import Interface class IFoo(Interface): pass @@ -312,9 +316,9 @@ class IFoo(Interface): self.assertRaises(ComponentLookupError, self._callFUT, (object(), object()), IFoo, "bar") def test_anonymous_hit(self): - from zope.interface import Interface - from zope.interface import implementer from guillotina.component import get_global_components + from zope.interface import implementer + from zope.interface import Interface class IFoo(Interface): pass @@ -347,9 +351,9 @@ def __init__(self, first, second): self.assertTrue(adapted.second is baz) def test_anonymous_hit_registered_for_None(self): - from zope.interface import Interface - from zope.interface import implementer from guillotina.component import get_global_components + from zope.interface import implementer + from zope.interface import Interface class IFoo(Interface): pass @@ -378,9 +382,9 @@ def __init__(self, first, second): self.assertTrue(adapted.second is baz) def test_named_hit(self): - from zope.interface import Interface - from zope.interface import implementer from guillotina.component import get_global_components + from zope.interface import implementer + from zope.interface import Interface class IFoo(Interface): pass @@ -415,7 +419,8 @@ def __init__(self, first, second): class Test_query_multi_adapter(unittest.TestCase): - from guillotina.component.testing import setUp, tearDown + from guillotina.component.testing import setUp + from guillotina.component.testing import tearDown def _callFUT(self, *args, **kw): from guillotina.component import query_multi_adapter @@ -439,9 +444,9 @@ class IFoo(Interface): self.assertEqual(self._callFUT((object(), object()), IFoo, "bar"), None) def test_anonymous_hit(self): - from zope.interface import Interface - from zope.interface import implementer from guillotina.component import get_global_components + from zope.interface import implementer + from zope.interface import Interface class IFoo(Interface): pass @@ -474,9 +479,9 @@ def __init__(self, first, second): self.assertTrue(adapted.second is baz) def test_named_hit(self): - from zope.interface import Interface - from zope.interface import implementer from guillotina.component import get_global_components + from zope.interface import implementer + from zope.interface import Interface class IFoo(Interface): pass @@ -509,11 +514,11 @@ def __init__(self, first, second): self.assertTrue(adapted.second is baz) def test_nested(self): - from zope.interface import Interface - from zope.interface import implementer - from zope.interface.registry import Components from guillotina.component import get_global_components from guillotina.component.tests.examples import ConformsToIComponentLookup + from zope.interface import implementer + from zope.interface import Interface + from zope.interface.registry import Components class IFoo(Interface): pass @@ -558,9 +563,9 @@ def __init__(self, sm): self.assertTrue(adapted.second is baz) def test_wo_sitemanager(self): - from zope.interface import Interface - from zope.interface import implementer from guillotina.component.interfaces import ComponentLookupError + from zope.interface import implementer + from zope.interface import Interface class IFoo(Interface): pass @@ -591,7 +596,8 @@ def __conform__(self, iface): class Test_get_adapters(unittest.TestCase): - from guillotina.component.testing import setUp, tearDown + from guillotina.component.testing import setUp + from guillotina.component.testing import tearDown def _callFUT(self, *args, **kw): from guillotina.component import get_adapters @@ -607,8 +613,8 @@ class IFoo(Interface): self.assertEqual(list(self._callFUT((object(),), IFoo)), []) def test_hit(self): - from zope.interface import Interface from guillotina.component import get_global_components + from zope.interface import Interface class IFoo(Interface): pass @@ -631,9 +637,9 @@ def __init__(self, context): self.assertTrue(("bar", "BazAdapter") in names) def test_wo_sitemanager(self): - from zope.interface import Interface - from zope.interface import implementer from guillotina.component.interfaces import ComponentLookupError + from zope.interface import implementer + from zope.interface import Interface class IFoo(Interface): pass @@ -664,7 +670,8 @@ def __conform__(self, iface): class Test_subscribers(unittest.TestCase): - from guillotina.component.testing import setUp, tearDown + from guillotina.component.testing import setUp + from guillotina.component.testing import tearDown def _callFUT(self, *args, **kw): from guillotina.component import subscribers @@ -681,8 +688,8 @@ class IFoo(Interface): self.assertEqual(subscribers, []) def test_hit(self): - from zope.interface import Interface from guillotina.component import get_global_components + from zope.interface import Interface class IFoo(Interface): pass @@ -705,8 +712,8 @@ def __init__(self, context): self.assertTrue("BazAdapter" in names) def test_wo_sitemanager(self): - from zope.interface import Interface from guillotina.component.interfaces import ComponentLookupError + from zope.interface import Interface class IFoo(Interface): pass @@ -721,7 +728,8 @@ def __conform__(self, iface): class Test_handle(unittest.TestCase): - from guillotina.component.testing import setUp, tearDown + from guillotina.component.testing import setUp + from guillotina.component.testing import tearDown def _callFUT(self, *args, **kw): from guillotina.component import handle @@ -738,8 +746,8 @@ class IFoo(Interface): def test_hit(self): from guillotina.component import get_global_components - from zope.interface import Interface from zope.interface import implementer + from zope.interface import Interface class IFoo(Interface): pass @@ -767,7 +775,8 @@ def _baz(context): class Test_get_utility(unittest.TestCase): - from guillotina.component.testing import setUp, tearDown + from guillotina.component.testing import setUp + from guillotina.component.testing import tearDown def _callFUT(self, *args, **kw): from guillotina.component._api import get_utility @@ -775,8 +784,8 @@ def _callFUT(self, *args, **kw): return get_utility(*args, **kw) def test_anonymous_nonesuch(self): - from zope.interface import Interface from guillotina.component.interfaces import ComponentLookupError + from zope.interface import Interface class IFoo(Interface): pass @@ -784,8 +793,8 @@ class IFoo(Interface): self.assertRaises(ComponentLookupError, self._callFUT, IFoo) def test_named_nonesuch(self): - from zope.interface import Interface from guillotina.component.interfaces import ComponentLookupError + from zope.interface import Interface class IFoo(Interface): pass @@ -793,8 +802,8 @@ class IFoo(Interface): self.assertRaises(ComponentLookupError, self._callFUT, IFoo, name="bar") def test_anonymous_hit(self): - from zope.interface import Interface from guillotina.component import get_global_components + from zope.interface import Interface class IFoo(Interface): pass @@ -804,8 +813,8 @@ class IFoo(Interface): self.assertTrue(self._callFUT(IFoo) is obj) def test_named_hit(self): - from zope.interface import Interface from guillotina.component import get_global_components + from zope.interface import Interface class IFoo(Interface): pass @@ -815,9 +824,9 @@ class IFoo(Interface): self.assertTrue(self._callFUT(IFoo, name="bar") is obj) def test_w_conforming_context(self): - from zope.interface import Interface from guillotina.component import get_global_components from guillotina.component.tests.examples import ConformsToIComponentLookup + from zope.interface import Interface class SM(object): def __init__(self, obj): @@ -839,7 +848,8 @@ class IFoo(Interface): class Test_query_utility(unittest.TestCase): - from guillotina.component.testing import setUp, tearDown + from guillotina.component.testing import setUp + from guillotina.component.testing import tearDown def _callFUT(self, *args, **kw): from guillotina.component._api import query_utility @@ -881,8 +891,8 @@ class IFoo(Interface): self.assertTrue(self._callFUT(IFoo, name="bar", default=obj) is obj) def test_anonymous_hit(self): - from zope.interface import Interface from guillotina.component import get_global_components + from zope.interface import Interface class IFoo(Interface): pass @@ -892,8 +902,8 @@ class IFoo(Interface): self.assertTrue(self._callFUT(IFoo) is obj) def test_named_hit(self): - from zope.interface import Interface from guillotina.component import get_global_components + from zope.interface import Interface class IFoo(Interface): pass @@ -903,9 +913,9 @@ class IFoo(Interface): self.assertTrue(self._callFUT(IFoo, name="bar") is obj) def test_w_conforming_context(self): - from zope.interface import Interface from guillotina.component import get_global_components from guillotina.component.tests.examples import ConformsToIComponentLookup + from zope.interface import Interface class SM(object): def __init__(self, obj): @@ -927,7 +937,8 @@ class IFoo(Interface): class Test_get_utilities_for(unittest.TestCase): - from guillotina.component.testing import setUp, tearDown + from guillotina.component.testing import setUp + from guillotina.component.testing import tearDown def _callFUT(self, *args, **kw): from guillotina.component._api import get_utilities_for @@ -943,8 +954,8 @@ class IFoo(Interface): self.assertEqual(list(self._callFUT(IFoo)), []) def test_hit(self): - from zope.interface import Interface from guillotina.component import get_global_components + from zope.interface import Interface class IFoo(Interface): pass @@ -961,7 +972,8 @@ class IFoo(Interface): class Test_get_all_utilities_registered_for(unittest.TestCase): - from guillotina.component.testing import setUp, tearDown + from guillotina.component.testing import setUp + from guillotina.component.testing import tearDown def _callFUT(self, *args, **kw): from guillotina.component import get_all_utilities_registered_for @@ -977,8 +989,8 @@ class IFoo(Interface): self.assertEqual(list(self._callFUT(IFoo)), []) def test_hit(self): - from zope.interface import Interface from guillotina.component import get_global_components + from zope.interface import Interface class IFoo(Interface): pass @@ -1001,7 +1013,8 @@ class IBar(IFoo): class Test_get_factory_interfaces(unittest.TestCase): - from guillotina.component.testing import setUp, tearDown + from guillotina.component.testing import setUp + from guillotina.component.testing import tearDown def _callFUT(self, *args, **kw): from guillotina.component import get_factory_interfaces @@ -1039,7 +1052,8 @@ def queryUtility(self, iface, name, default): class Test_get_factories_for(unittest.TestCase): - from guillotina.component.testing import setUp, tearDown + from guillotina.component.testing import setUp + from guillotina.component.testing import tearDown def _callFUT(self, *args, **kw): from guillotina.component import get_factories_for @@ -1055,10 +1069,10 @@ class IFoo(Interface): self.assertEqual(list(self._callFUT(IFoo)), []) def test_w_factory_returning_spec(self): - from zope.interface import Interface + from guillotina.component.interfaces import IFactory from zope.interface import implementer + from zope.interface import Interface from zope.interface import providedBy - from guillotina.component.interfaces import IFactory class IFoo(Interface): pass @@ -1085,8 +1099,8 @@ def getUtilitiesFor(self, iface): self.assertEqual(list(self._callFUT(IBar, context=Context())), [("test", _factory)]) def test_w_factory_returning_list_of_interfaces(self): - from zope.interface import Interface from guillotina.component.interfaces import IFactory + from zope.interface import Interface class IFoo(Interface): pass @@ -1117,9 +1131,9 @@ def getUtilitiesFor(self, iface): def _makeMyUtility(name, sm): global IMyUtility - from zope.interface import Interface - from zope.interface import implementer from guillotina.component.tests.examples import ConformsToIComponentLookup + from zope.interface import implementer + from zope.interface import Interface if IMyUtility is None: diff --git a/guillotina/component/tests/test__declaration.py b/guillotina/component/tests/test__declaration.py index ba6db0751..f22ecb5ae 100644 --- a/guillotina/component/tests/test__declaration.py +++ b/guillotina/component/tests/test__declaration.py @@ -123,10 +123,11 @@ def _try(): self.assertRaises(AttributeError, _try) def test_called_from_function(self): - import warnings from guillotina.component._declaration import adapts from zope.interface import Interface + import warnings + class IFoo(Interface): pass @@ -141,11 +142,12 @@ class IFoo(Interface): self.assertEqual(len(log), 0) # no longer warn def test_called_twice_from_class(self): - import warnings from guillotina.component._declaration import adapts from zope.interface import Interface from zope.interface._compat import PYTHON3 + import warnings + class IFoo(Interface): pass @@ -205,8 +207,8 @@ class Baz(object): self.assertEqual(self._callFUT(Baz), (IFoo, IBar)) def test__call___w_inst_of_decorated_class(self): - from zope.interface import Interface from guillotina.component._declaration import _adapts_descr + from zope.interface import Interface class IFoo(Interface): pass diff --git a/guillotina/component/tests/test_event.py b/guillotina/component/tests/test_event.py index e6ae537d4..507637c69 100644 --- a/guillotina/component/tests/test_event.py +++ b/guillotina/component/tests/test_event.py @@ -17,9 +17,9 @@ class Test_dispatch(unittest.TestCase): def test_it(self): - from zope.interface import Interface - from guillotina.component.globalregistry import get_global_components from guillotina.component.event import dispatch + from guillotina.component.globalregistry import get_global_components + from zope.interface import Interface _adapted = [] diff --git a/guillotina/component/tests/test_factory.py b/guillotina/component/tests/test_factory.py index 46b0fc861..319912973 100644 --- a/guillotina/component/tests/test_factory.py +++ b/guillotina/component/tests/test_factory.py @@ -27,14 +27,14 @@ def _makeOne(self, callable=None, *args, **kw): return self._getTargetClass()(callable, *args, **kw) def test_class_conforms_to_IFactory(self): - from zope.interface.verify import verifyClass from guillotina.component.interfaces import IFactory + from zope.interface.verify import verifyClass verifyClass(IFactory, self._getTargetClass()) def test_instance_conforms_to_IFactory(self): - from zope.interface.verify import verifyObject from guillotina.component.interfaces import IFactory + from zope.interface.verify import verifyObject verifyObject(IFactory, self._makeOne()) @@ -81,8 +81,8 @@ def _callable(*args, **kw): self.assertEqual(_called, [((), {"foo": "bar"})]) def test_get_interfaces_explicit(self): - from zope.interface import Interface from zope.interface import implementer + from zope.interface import Interface class IFoo(Interface): pass @@ -103,8 +103,8 @@ def _callable(): self.assertEqual(list(spec), [IFoo, IBar]) def test_get_interfaces_implicit(self): - from zope.interface import Interface from zope.interface import implementer + from zope.interface import Interface class IBaz(Interface): pass diff --git a/guillotina/component/tests/test_globalregistry.py b/guillotina/component/tests/test_globalregistry.py index 3fecc8109..ea25f9483 100644 --- a/guillotina/component/tests/test_globalregistry.py +++ b/guillotina/component/tests/test_globalregistry.py @@ -35,7 +35,8 @@ def test_gsm_is_singleton(self): class Test_provide_utility(unittest.TestCase): - from guillotina.component.testing import setUp, tearDown + from guillotina.component.testing import setUp + from guillotina.component.testing import tearDown def _callFUT(self, *args, **kw): from guillotina.component.globalregistry import provide_utility @@ -43,9 +44,9 @@ def _callFUT(self, *args, **kw): return provide_utility(*args, **kw) def test_anonymous_no_provides(self): - from zope.interface import Interface - from zope.interface import implementer from guillotina.component.globalregistry import get_global_components + from zope.interface import implementer + from zope.interface import Interface class IFoo(Interface): pass @@ -60,8 +61,8 @@ class Foo(object): self.assertTrue(gsm.getUtility(IFoo, "") is foo) def test_named_w_provides(self): - from zope.interface import Interface from guillotina.component.globalregistry import get_global_components + from zope.interface import Interface class IFoo(Interface): pass @@ -77,7 +78,8 @@ class Foo(object): class Test_provide_adapter(unittest.TestCase): - from guillotina.component.testing import setUp, tearDown + from guillotina.component.testing import setUp + from guillotina.component.testing import tearDown def _callFUT(self, *args, **kw): from guillotina.component.globalregistry import provide_adapter @@ -85,10 +87,10 @@ def _callFUT(self, *args, **kw): return provide_adapter(*args, **kw) def test_anonymous_no_provides_no_adapts(self): - from zope.interface import Interface - from zope.interface import implementer - from guillotina.component.globalregistry import get_global_components from guillotina.component._declaration import adapter + from guillotina.component.globalregistry import get_global_components + from zope.interface import implementer + from zope.interface import Interface class IFoo(Interface): pass @@ -114,9 +116,9 @@ def __init__(self, context): self.assertTrue(adapted.context is foo) def test_named_w_provides_w_adapts(self): - from zope.interface import Interface - from zope.interface import implementer from guillotina.component.globalregistry import get_global_components + from zope.interface import implementer + from zope.interface import Interface class IFoo(Interface): pass @@ -142,7 +144,8 @@ def __init__(self, context): class Test_provide_subscription_adapter(unittest.TestCase): - from guillotina.component.testing import setUp, tearDown + from guillotina.component.testing import setUp + from guillotina.component.testing import tearDown def _callFUT(self, *args, **kw): from guillotina.component.globalregistry import provide_subscription_adapter @@ -150,10 +153,10 @@ def _callFUT(self, *args, **kw): return provide_subscription_adapter(*args, **kw) def test_no_provides_no_adapts(self): - from zope.interface import Interface - from zope.interface import implementer - from guillotina.component.globalregistry import get_global_components from guillotina.component._declaration import adapter + from guillotina.component.globalregistry import get_global_components + from zope.interface import implementer + from zope.interface import Interface class IFoo(Interface): pass @@ -180,9 +183,9 @@ def __init__(self, context): self.assertTrue(adapted[0].context is foo) def test_w_provides_w_adapts(self): - from zope.interface import Interface - from zope.interface import implementer from guillotina.component.globalregistry import get_global_components + from zope.interface import implementer + from zope.interface import Interface class IFoo(Interface): pass @@ -209,7 +212,8 @@ def __init__(self, context): class Test_provide_handler(unittest.TestCase): - from guillotina.component.testing import setUp, tearDown + from guillotina.component.testing import setUp + from guillotina.component.testing import tearDown def _callFUT(self, *args, **kw): from guillotina.component.globalregistry import provide_handler @@ -217,11 +221,11 @@ def _callFUT(self, *args, **kw): return provide_handler(*args, **kw) def test_no_adapts(self): - from zope.interface import Interface + from guillotina.component._declaration import adapter + from guillotina.component.globalregistry import get_global_components from zope.interface import implementer + from zope.interface import Interface from zope.interface import providedBy - from guillotina.component.globalregistry import get_global_components - from guillotina.component._declaration import adapter class IFoo(Interface): pass @@ -244,8 +248,8 @@ def _handler(context): self.assertTrue(hr.factory is _handler) def test_w_adapts(self): - from zope.interface import Interface from guillotina.component.globalregistry import get_global_components + from zope.interface import Interface class IFoo(Interface): pass diff --git a/guillotina/component/tests/test_registry.py b/guillotina/component/tests/test_registry.py index 6be3601e4..1541db7a9 100644 --- a/guillotina/component/tests/test_registry.py +++ b/guillotina/component/tests/test_registry.py @@ -17,7 +17,8 @@ class Test_dispatch_utility_registration_event(unittest.TestCase): - from guillotina.component.testing import setUp, tearDown + from guillotina.component.testing import setUp + from guillotina.component.testing import tearDown def _callFUT(self, *args, **kw): from guillotina.component.registry import dispatch_utility_registration_event @@ -43,7 +44,8 @@ def _handle(*args): class Test_dispatch_adapter_registration_event(unittest.TestCase): - from guillotina.component.testing import setUp, tearDown + from guillotina.component.testing import setUp + from guillotina.component.testing import tearDown def _callFUT(self, *args, **kw): from guillotina.component.registry import dispatch_adapter_registration_event @@ -71,7 +73,8 @@ def _handle(*args): class Test_dispatch_subscription_adapter_registration_event(unittest.TestCase): - from guillotina.component.testing import setUp, tearDown + from guillotina.component.testing import setUp + from guillotina.component.testing import tearDown def _callFUT(self, *args, **kw): from guillotina.component.registry import dispatch_subscription_adapter_registration_event @@ -99,7 +102,8 @@ def _handle(*args): class Test_dispatch_handler_registration_event(unittest.TestCase): - from guillotina.component.testing import setUp, tearDown + from guillotina.component.testing import setUp + from guillotina.component.testing import tearDown def _callFUT(self, *args, **kw): from guillotina.component.registry import dispatch_handler_registration_event diff --git a/guillotina/configure/__init__.py b/guillotina/configure/__init__.py index fe5ef715e..865bc7cf7 100644 --- a/guillotina/configure/__init__.py +++ b/guillotina/configure/__init__.py @@ -40,7 +40,6 @@ import inspect import logging - _registered_configurations: ConfigurationType = [] # stored as tuple of (type, configuration) so we get keep it in the order # it is registered even if you mix types of registrations @@ -591,9 +590,9 @@ def json_schema_definition(name: str, schema: dict) -> None: def grant_directive(_context, principal=None, role=None, permission=None, permissions=None): - from guillotina.security.security_code import role_permission_manager as role_perm_mgr from guillotina.security.security_code import principal_permission_manager as principal_perm_mgr from guillotina.security.security_code import principal_role_manager as principal_role_mgr + from guillotina.security.security_code import role_permission_manager as role_perm_mgr nspecified = ( (principal is not None) + (role is not None) + (permission is not None) + (permissions is not None) @@ -633,8 +632,8 @@ def grant_directive(_context, principal=None, role=None, permission=None, permis def grantAll_directive(_context, principal=None, role=None): # noqa: N802 """Grant all permissions to a role or principal""" - from guillotina.security.security_code import role_permission_manager from guillotina.security.security_code import principal_permission_manager + from guillotina.security.security_code import role_permission_manager nspecified = (principal is not None) + (role is not None) diff --git a/guillotina/configure/behaviors.py b/guillotina/configure/behaviors.py index f12d91cd9..0ca607059 100644 --- a/guillotina/configure/behaviors.py +++ b/guillotina/configure/behaviors.py @@ -3,7 +3,6 @@ from zope.interface import implementer from zope.interface import Interface - REGISTRATION_REPR = """\ <{class} {name} at {id} schema: {identifier} diff --git a/guillotina/configure/component.py b/guillotina/configure/component.py index e95f34018..a05f3e404 100644 --- a/guillotina/configure/component.py +++ b/guillotina/configure/component.py @@ -24,7 +24,6 @@ from zope.interface import Interface from zope.interface import providedBy - _ = MessageFactory("guillotina") diff --git a/guillotina/content.py b/guillotina/content.py index 710e0b9ea..b54793290 100644 --- a/guillotina/content.py +++ b/guillotina/content.py @@ -84,7 +84,6 @@ import os import pathlib - _zone = tzutc() # utz tz is much faster than local tz info _marker = object() diff --git a/guillotina/contrib/cache/__init__.py b/guillotina/contrib/cache/__init__.py index 3516fc4bc..b55c80a8d 100644 --- a/guillotina/contrib/cache/__init__.py +++ b/guillotina/contrib/cache/__init__.py @@ -1,6 +1,5 @@ from guillotina import configure - CACHE_PREFIX = "gcache2-" app_settings = { diff --git a/guillotina/contrib/cache/lru.pyi b/guillotina/contrib/cache/lru.pyi index 021e97be4..a73bb1457 100644 --- a/guillotina/contrib/cache/lru.pyi +++ b/guillotina/contrib/cache/lru.pyi @@ -2,7 +2,6 @@ from typing import Any from typing import Dict from typing import Optional - class LRU(Dict[str, Any]): def __init__(self, size: int): ... def set(self, key: str, value: Any, size: Optional[int] = None) -> None: ... diff --git a/guillotina/contrib/cache/memcache.py b/guillotina/contrib/cache/memcache.py index d3bc1392a..b610b9854 100644 --- a/guillotina/contrib/cache/memcache.py +++ b/guillotina/contrib/cache/memcache.py @@ -2,7 +2,6 @@ from guillotina.contrib.cache.lru import LRU from typing import Optional - _lru: Optional[LRU] = None diff --git a/guillotina/contrib/cache/strategy.py b/guillotina/contrib/cache/strategy.py index e2dd7b8e3..4785881c8 100644 --- a/guillotina/contrib/cache/strategy.py +++ b/guillotina/contrib/cache/strategy.py @@ -16,7 +16,6 @@ import asyncio import logging - logger = logging.getLogger("guillotina") _default_size = 1024 diff --git a/guillotina/contrib/cache/utility.py b/guillotina/contrib/cache/utility.py index 090ab5f79..80fb95604 100644 --- a/guillotina/contrib/cache/utility.py +++ b/guillotina/contrib/cache/utility.py @@ -18,7 +18,6 @@ import pickle import uuid - logger = logging.getLogger("guillotina.contrib.cache") _default_size = 1024 _basic_types = (bytes, str, int, float) diff --git a/guillotina/contrib/catalog/pg/__init__.py b/guillotina/contrib/catalog/pg/__init__.py index a6f918bec..131a4c59d 100644 --- a/guillotina/contrib/catalog/pg/__init__.py +++ b/guillotina/contrib/catalog/pg/__init__.py @@ -2,7 +2,6 @@ import logging - logger = logging.getLogger("guillotina") app_settings = { diff --git a/guillotina/contrib/catalog/pg/parser.py b/guillotina/contrib/catalog/pg/parser.py index 5bd4d99a7..0212db1a3 100644 --- a/guillotina/contrib/catalog/pg/parser.py +++ b/guillotina/contrib/catalog/pg/parser.py @@ -13,7 +13,6 @@ import typing import urllib - _type_mapping = {"int": int, "float": float} diff --git a/guillotina/contrib/catalog/pg/utility.py b/guillotina/contrib/catalog/pg/utility.py index 81c01cc6a..011997450 100644 --- a/guillotina/contrib/catalog/pg/utility.py +++ b/guillotina/contrib/catalog/pg/utility.py @@ -41,7 +41,6 @@ import os import typing - # 2019-06-15T18:37:31.008359+00:00 PG_FUNCTIONS = [ """CREATE OR REPLACE FUNCTION f_cast_isots(text) RETURNS timestamptz AS $$ diff --git a/guillotina/contrib/dbusers/__init__.py b/guillotina/contrib/dbusers/__init__.py index b3eaade0d..324523d53 100644 --- a/guillotina/contrib/dbusers/__init__.py +++ b/guillotina/contrib/dbusers/__init__.py @@ -1,7 +1,6 @@ from guillotina import configure from guillotina.i18n import MessageFactory - _ = MessageFactory("guillotina.contrib.dbusers") diff --git a/guillotina/contrib/dbusers/adapters.py b/guillotina/contrib/dbusers/adapters.py index 47ca275ab..77f75fb2f 100644 --- a/guillotina/contrib/dbusers/adapters.py +++ b/guillotina/contrib/dbusers/adapters.py @@ -5,7 +5,6 @@ import re - # from https://github.com/theskumar/python-usernames/blob/master/usernames/validators.py username_regex = re.compile( diff --git a/guillotina/contrib/dbusers/install.py b/guillotina/contrib/dbusers/install.py index f32e713b9..dc44f4e20 100644 --- a/guillotina/contrib/dbusers/install.py +++ b/guillotina/contrib/dbusers/install.py @@ -7,7 +7,6 @@ from guillotina.utils import get_authenticated_user_id from guillotina.utils import get_registry - USERS_LAYER = "guillotina.contrib.dbusers.interfaces.IDBUsersLayer" diff --git a/guillotina/contrib/dbusers/permissions.py b/guillotina/contrib/dbusers/permissions.py index 8674ae1f4..3949edbda 100644 --- a/guillotina/contrib/dbusers/permissions.py +++ b/guillotina/contrib/dbusers/permissions.py @@ -1,6 +1,5 @@ from guillotina import configure - configure.permission("guillotina.AddUser", title="Add plone user") configure.permission("guillotina.AddGroup", title="Add plone group") configure.permission("guillotina.Nobody", "Permission not assigned to anyone") diff --git a/guillotina/contrib/dbusers/services/__init__.py b/guillotina/contrib/dbusers/services/__init__.py index 4e5a9e6d0..4df73882f 100644 --- a/guillotina/contrib/dbusers/services/__init__.py +++ b/guillotina/contrib/dbusers/services/__init__.py @@ -6,7 +6,6 @@ from guillotina.contrib.dbusers.content.groups import IGroupManager from guillotina.contrib.dbusers.content.users import IUserManager - # override some views... configure.service(context=IGroupManager, method="POST", permission="guillotina.AddGroup", allow_access=True)( DefaultPOST diff --git a/guillotina/contrib/dbusers/services/groups.py b/guillotina/contrib/dbusers/services/groups.py index 73257b856..b761a6aad 100644 --- a/guillotina/contrib/dbusers/services/groups.py +++ b/guillotina/contrib/dbusers/services/groups.py @@ -20,7 +20,6 @@ import logging import typing - logger = logging.getLogger("guillotina.contrib.dbusers") diff --git a/guillotina/contrib/dyncontent/__init__.py b/guillotina/contrib/dyncontent/__init__.py index f35ca1e7d..be7f093aa 100644 --- a/guillotina/contrib/dyncontent/__init__.py +++ b/guillotina/contrib/dyncontent/__init__.py @@ -2,7 +2,6 @@ from typing import Any from typing import Dict - app_settings: Dict[str, Any] = {} diff --git a/guillotina/contrib/dyncontent/subscriber.py b/guillotina/contrib/dyncontent/subscriber.py index 9811aff3a..7b0ce8a7f 100644 --- a/guillotina/contrib/dyncontent/subscriber.py +++ b/guillotina/contrib/dyncontent/subscriber.py @@ -28,7 +28,6 @@ import logging import typing - SUPPORTED_DIRECTIVES = { "index": index_field, "read_permission": read_permission, diff --git a/guillotina/contrib/email_validation/__init__.py b/guillotina/contrib/email_validation/__init__.py index 7bf6f73f5..6bf7fdcbe 100644 --- a/guillotina/contrib/email_validation/__init__.py +++ b/guillotina/contrib/email_validation/__init__.py @@ -1,6 +1,5 @@ from guillotina import configure - app_settings = { "applications": ["guillotina.contrib.templates"], "load_utilities": { diff --git a/guillotina/contrib/email_validation/utility.py b/guillotina/contrib/email_validation/utility.py index fab4bebc5..72377f387 100644 --- a/guillotina/contrib/email_validation/utility.py +++ b/guillotina/contrib/email_validation/utility.py @@ -18,7 +18,6 @@ import logging - logger = logging.getLogger("guillotina.email_validation") diff --git a/guillotina/contrib/email_validation/utils.py b/guillotina/contrib/email_validation/utils.py index c060ff1f8..6452520b9 100644 --- a/guillotina/contrib/email_validation/utils.py +++ b/guillotina/contrib/email_validation/utils.py @@ -9,7 +9,6 @@ import pytz import time - logger = logging.getLogger("guillotina.email_validation") diff --git a/guillotina/contrib/image/api.py b/guillotina/contrib/image/api.py index 80274ab30..1dd1ac3d4 100644 --- a/guillotina/contrib/image/api.py +++ b/guillotina/contrib/image/api.py @@ -18,7 +18,6 @@ from guillotina.utils import run_async from io import BytesIO - BUFFER = 262144 diff --git a/guillotina/contrib/mailer/__init__.py b/guillotina/contrib/mailer/__init__.py index 300270753..30bb7d277 100644 --- a/guillotina/contrib/mailer/__init__.py +++ b/guillotina/contrib/mailer/__init__.py @@ -6,7 +6,6 @@ import logging - logger = logging.getLogger("guillotina.contrib.mailer") app_settings = { diff --git a/guillotina/contrib/mailer/encoding.py b/guillotina/contrib/mailer/encoding.py index bd5e63aba..d4f4aeafd 100644 --- a/guillotina/contrib/mailer/encoding.py +++ b/guillotina/contrib/mailer/encoding.py @@ -2,7 +2,6 @@ from email import header from email import utils - # From http://tools.ietf.org/html/rfc5322#section-3.6 ADDR_HEADERS = ( "resent-from", diff --git a/guillotina/contrib/mailer/utility.py b/guillotina/contrib/mailer/utility.py index a9af02e0e..26ea4b966 100644 --- a/guillotina/contrib/mailer/utility.py +++ b/guillotina/contrib/mailer/utility.py @@ -22,7 +22,6 @@ import socket import time - try: import aiosmtplib except ImportError: diff --git a/guillotina/contrib/mcp/__init__.py b/guillotina/contrib/mcp/__init__.py index 26dfdfb30..6b0a46bcb 100644 --- a/guillotina/contrib/mcp/__init__.py +++ b/guillotina/contrib/mcp/__init__.py @@ -1,6 +1,5 @@ from guillotina import configure - app_settings = { "mcp": { "enabled": True, diff --git a/guillotina/contrib/mcp/backend.py b/guillotina/contrib/mcp/backend.py index 27e05345b..77b27283f 100644 --- a/guillotina/contrib/mcp/backend.py +++ b/guillotina/contrib/mcp/backend.py @@ -14,7 +14,6 @@ import json import logging - ToolHandler = Callable[[Any, Any, Dict[str, Any]], Awaitable[Dict[str, Any]]] ResourceHandler = Callable[[Any], Awaitable[Dict[str, Any]]] logger = logging.getLogger("guillotina.contrib.redis") diff --git a/guillotina/contrib/mcp/permissions.py b/guillotina/contrib/mcp/permissions.py index 9e0d23880..ddc9376d5 100644 --- a/guillotina/contrib/mcp/permissions.py +++ b/guillotina/contrib/mcp/permissions.py @@ -1,6 +1,5 @@ from guillotina import configure - configure.permission("guillotina.MCPView", "View MCP integration services") configure.permission("guillotina.MCPExecute", "Execute MCP tools") diff --git a/guillotina/contrib/mcp/services.py b/guillotina/contrib/mcp/services.py index 7dcfd7464..a28ad0069 100644 --- a/guillotina/contrib/mcp/services.py +++ b/guillotina/contrib/mcp/services.py @@ -32,8 +32,9 @@ async def __call__(self): async def _handle_protocol(self): try: - import anyio from mcp.server.streamable_http import StreamableHTTPServerTransport + + import anyio except ImportError as exc: raise HTTPServiceUnavailable( content={"reason": 'MCP SDK missing. Install "guillotina[mcp]".'} diff --git a/guillotina/contrib/mcp/tools.py b/guillotina/contrib/mcp/tools.py index 6cd88db2b..08fe821a4 100644 --- a/guillotina/contrib/mcp/tools.py +++ b/guillotina/contrib/mcp/tools.py @@ -18,7 +18,6 @@ import functools - ToolHandler = Callable[[Any, Any, Dict[str, Any]], Awaitable[Dict[str, Any]]] RESOLVE_PATH_SCHEMA = { diff --git a/guillotina/contrib/memcached/__init__.py b/guillotina/contrib/memcached/__init__.py index 4eb18ea73..85cca227d 100644 --- a/guillotina/contrib/memcached/__init__.py +++ b/guillotina/contrib/memcached/__init__.py @@ -3,7 +3,6 @@ from typing import Any from typing import Dict - _driver = None app_settings: Dict[str, Any] = {"memcached": {"hosts": []}} diff --git a/guillotina/contrib/memcached/driver.py b/guillotina/contrib/memcached/driver.py index e37349bd2..92056d798 100644 --- a/guillotina/contrib/memcached/driver.py +++ b/guillotina/contrib/memcached/driver.py @@ -17,11 +17,11 @@ import hashlib import logging - try: - import prometheus_client from prometheus_client.utils import INF + import prometheus_client + _SEND_METRICS = True MEMCACHED_OPS = prometheus_client.Counter( diff --git a/guillotina/contrib/pubsub/utility.py b/guillotina/contrib/pubsub/utility.py index aaed2974f..fca0a07de 100644 --- a/guillotina/contrib/pubsub/utility.py +++ b/guillotina/contrib/pubsub/utility.py @@ -9,7 +9,6 @@ import logging import pickle - logger = logging.getLogger("guillotina") diff --git a/guillotina/contrib/redis/__init__.py b/guillotina/contrib/redis/__init__.py index d014e09dc..07f5c16fe 100644 --- a/guillotina/contrib/redis/__init__.py +++ b/guillotina/contrib/redis/__init__.py @@ -2,7 +2,6 @@ from guillotina import configure from guillotina.contrib.redis.driver import RedisDriver - _driver = None app_settings = { diff --git a/guillotina/contrib/redis/dm.py b/guillotina/contrib/redis/dm.py index 30f1ac2e7..b3fa860d0 100644 --- a/guillotina/contrib/redis/dm.py +++ b/guillotina/contrib/redis/dm.py @@ -10,7 +10,6 @@ import orjson import time - try: import prometheus_client diff --git a/guillotina/contrib/redis/driver.py b/guillotina/contrib/redis/driver.py index 6c6beadc0..c0c5d0ebf 100644 --- a/guillotina/contrib/redis/driver.py +++ b/guillotina/contrib/redis/driver.py @@ -17,7 +17,6 @@ import backoff import logging - try: import prometheus_client diff --git a/guillotina/contrib/redis_session/utility.py b/guillotina/contrib/redis_session/utility.py index 685cd3818..41fd71ada 100644 --- a/guillotina/contrib/redis_session/utility.py +++ b/guillotina/contrib/redis_session/utility.py @@ -4,7 +4,6 @@ import logging import uuid - logger = logging.getLogger("guillotina") diff --git a/guillotina/contrib/swagger/__init__.py b/guillotina/contrib/swagger/__init__.py index 1f67ac9d6..98ff8ccca 100644 --- a/guillotina/contrib/swagger/__init__.py +++ b/guillotina/contrib/swagger/__init__.py @@ -1,6 +1,5 @@ from guillotina import configure - configure.permission("guillotina.swagger.View", "View swagger definition") configure.grant(permission="guillotina.swagger.View", role="guillotina.Anonymous") configure.grant(permission="guillotina.swagger.View", role="guillotina.Authenticated") diff --git a/guillotina/contrib/swagger/services.py b/guillotina/contrib/swagger/services.py index 142149229..56554b981 100644 --- a/guillotina/contrib/swagger/services.py +++ b/guillotina/contrib/swagger/services.py @@ -15,7 +15,6 @@ import os import pkg_resources - here = os.path.dirname(os.path.realpath(__file__)) diff --git a/guillotina/contrib/templates/__init__.py b/guillotina/contrib/templates/__init__.py index 89558de34..e33324842 100644 --- a/guillotina/contrib/templates/__init__.py +++ b/guillotina/contrib/templates/__init__.py @@ -1,6 +1,5 @@ from guillotina import configure - app_settings = { "load_utilities": { "template": { diff --git a/guillotina/contrib/templates/permissions.py b/guillotina/contrib/templates/permissions.py index 4989c4830..4fa64251a 100644 --- a/guillotina/contrib/templates/permissions.py +++ b/guillotina/contrib/templates/permissions.py @@ -1,6 +1,5 @@ from guillotina import configure - configure.permission("guillotina.AddJinjaTemplate", title="Add Jinja template") configure.grant(permission="guillotina.AddJinjaTemplate", role="guillotina.Manager") diff --git a/guillotina/contrib/templates/utility.py b/guillotina/contrib/templates/utility.py index c3bf28b59..54dccd1a6 100644 --- a/guillotina/contrib/templates/utility.py +++ b/guillotina/contrib/templates/utility.py @@ -13,7 +13,6 @@ import logging - logger = logging.getLogger("guillotina") diff --git a/guillotina/contrib/vocabularies/countries.py b/guillotina/contrib/vocabularies/countries.py index 074a2bca5..f33b4b259 100644 --- a/guillotina/contrib/vocabularies/countries.py +++ b/guillotina/contrib/vocabularies/countries.py @@ -1,6 +1,5 @@ from guillotina import configure - _countries = { "AF": "Afghanistan", "AN": "Netherlands Antilles", diff --git a/guillotina/contrib/vocabularies/languages.py b/guillotina/contrib/vocabularies/languages.py index 15b86884e..da9c2d71d 100644 --- a/guillotina/contrib/vocabularies/languages.py +++ b/guillotina/contrib/vocabularies/languages.py @@ -1,6 +1,5 @@ from guillotina import configure - _languagelist = { "aa": {"native": "магIарул мацI", "name": "Afar"}, "ab": {"native": "бызшәа", "name": "Abkhazian"}, diff --git a/guillotina/contrib/workflows/__init__.py b/guillotina/contrib/workflows/__init__.py index 42108cae3..2d33bd15d 100644 --- a/guillotina/contrib/workflows/__init__.py +++ b/guillotina/contrib/workflows/__init__.py @@ -5,7 +5,6 @@ import typing import yaml - logger = logging.getLogger("guillotina.contrib.workflows") diff --git a/guillotina/contrib/workflows/interfaces.py b/guillotina/contrib/workflows/interfaces.py index a475571a3..d103f6b08 100644 --- a/guillotina/contrib/workflows/interfaces.py +++ b/guillotina/contrib/workflows/interfaces.py @@ -12,7 +12,6 @@ import json - HISTORY_SCHEMA = json.dumps( { "type": "object", diff --git a/guillotina/contrib/workflows/permissions.py b/guillotina/contrib/workflows/permissions.py index 71ee692b5..7a211043e 100644 --- a/guillotina/contrib/workflows/permissions.py +++ b/guillotina/contrib/workflows/permissions.py @@ -1,6 +1,5 @@ from guillotina import configure - configure.permission("guillotina.ReviewContent", "Review content permission") configure.permission("guillotina.RequestReview", "Request review content permission") diff --git a/guillotina/cookiecutter/application/{{cookiecutter.package_name}}/setup.py b/guillotina/cookiecutter/application/{{cookiecutter.package_name}}/setup.py index 9742639ae..1165a919c 100644 --- a/guillotina/cookiecutter/application/{{cookiecutter.package_name}}/setup.py +++ b/guillotina/cookiecutter/application/{{cookiecutter.package_name}}/setup.py @@ -1,7 +1,6 @@ from setuptools import find_packages from setuptools import setup - try: README = open('README.rst').read() except IOError: diff --git a/guillotina/cookiecutter/application/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/__init__.py b/guillotina/cookiecutter/application/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/__init__.py index 18d4c2bcb..68a85ab23 100644 --- a/guillotina/cookiecutter/application/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/__init__.py +++ b/guillotina/cookiecutter/application/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/__init__.py @@ -1,6 +1,5 @@ from guillotina import configure - app_settings = { # provide custom application settings here... } diff --git a/guillotina/cookiecutter/application/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/tests/test_install.py b/guillotina/cookiecutter/application/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/tests/test_install.py index 6c520fa06..9c43e2414 100644 --- a/guillotina/cookiecutter/application/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/tests/test_install.py +++ b/guillotina/cookiecutter/application/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/tests/test_install.py @@ -1,6 +1,5 @@ import pytest - pytestmark = [pytest.mark.asyncio] diff --git a/guillotina/cors.py b/guillotina/cors.py index efb989545..ff7b0508a 100644 --- a/guillotina/cors.py +++ b/guillotina/cors.py @@ -5,7 +5,6 @@ import fnmatch - logger = glogging.getLogger("guillotina") diff --git a/guillotina/db/cache/base.py b/guillotina/db/cache/base.py index c02929f4e..3ac35dfe7 100644 --- a/guillotina/db/cache/base.py +++ b/guillotina/db/cache/base.py @@ -6,7 +6,6 @@ import typing - logger = glogging.getLogger("guillotina") diff --git a/guillotina/db/cache/dummy.py b/guillotina/db/cache/dummy.py index 7e3b89e21..5c99d313e 100644 --- a/guillotina/db/cache/dummy.py +++ b/guillotina/db/cache/dummy.py @@ -14,17 +14,12 @@ async def get(self, **kwargs): async def set( self, value, keyset: List[Dict[str, Any]] = None, oid=None, container=None, id=None, variant=None - ): - ... + ): ... - async def clear(self): - ... + async def clear(self): ... - async def invalidate(self, ob): - ... + async def invalidate(self, ob): ... - async def delete(self, key): - ... + async def delete(self, key): ... - async def delete_all(self, keys): - ... + async def delete_all(self, keys): ... diff --git a/guillotina/db/orm/base.py b/guillotina/db/orm/base.py index 499f92919..841cc29b7 100644 --- a/guillotina/db/orm/base.py +++ b/guillotina/db/orm/base.py @@ -12,7 +12,6 @@ import weakref - T = TypeVar("T") diff --git a/guillotina/db/orm/interfaces.py b/guillotina/db/orm/interfaces.py index f04244225..1b82230e5 100644 --- a/guillotina/db/orm/interfaces.py +++ b/guillotina/db/orm/interfaces.py @@ -2,7 +2,6 @@ from zope.interface import Attribute from zope.interface import Interface - OID_TYPE = SERIAL_TYPE = bytes @@ -61,5 +60,4 @@ def __getstate__(): # type: ignore The result must be picklable. """ - def register(): - ... + def register(): ... diff --git a/guillotina/db/storages/cockroach.py b/guillotina/db/storages/cockroach.py index 5f3b56f2c..760f61916 100644 --- a/guillotina/db/storages/cockroach.py +++ b/guillotina/db/storages/cockroach.py @@ -15,7 +15,6 @@ import asyncpg import uuid - logger = glogging.getLogger("guillotina") # upsert without checking matching tids on updated object diff --git a/guillotina/db/storages/dummy.py b/guillotina/db/storages/dummy.py index ddf7aeda0..877152c51 100644 --- a/guillotina/db/storages/dummy.py +++ b/guillotina/db/storages/dummy.py @@ -8,7 +8,6 @@ import os import pickle - logger = logging.getLogger("guillotina") diff --git a/guillotina/db/storages/pg.py b/guillotina/db/storages/pg.py index e35ff67cb..7e156f11b 100644 --- a/guillotina/db/storages/pg.py +++ b/guillotina/db/storages/pg.py @@ -27,7 +27,6 @@ import orjson import time - try: import prometheus_client diff --git a/guillotina/db/storages/vacuum.py b/guillotina/db/storages/vacuum.py index 5ec550924..7e83aaa39 100644 --- a/guillotina/db/storages/vacuum.py +++ b/guillotina/db/storages/vacuum.py @@ -9,7 +9,6 @@ import asyncpg.exceptions import logging - logger = logging.getLogger("guillotina") diff --git a/guillotina/db/transaction.py b/guillotina/db/transaction.py index fc37c3f1d..2bd3cde9d 100644 --- a/guillotina/db/transaction.py +++ b/guillotina/db/transaction.py @@ -34,7 +34,6 @@ import time import warnings - _EMPTY = "____" @@ -73,8 +72,7 @@ def record_cache_metric( def record_cache_metric( name: str, result_type: str, value: Union[ObjectResultType, str], key_args: Dict[str, Any] - ) -> None: - ... + ) -> None: ... logger = logging.getLogger(__name__) diff --git a/guillotina/db/transaction_manager.py b/guillotina/db/transaction_manager.py index ef7f80cb2..60795c286 100644 --- a/guillotina/db/transaction_manager.py +++ b/guillotina/db/transaction_manager.py @@ -20,7 +20,6 @@ import asyncpg import typing - logger = glogging.getLogger("guillotina") diff --git a/guillotina/db/uid.py b/guillotina/db/uid.py index db2e6a0b4..594da2b76 100644 --- a/guillotina/db/uid.py +++ b/guillotina/db/uid.py @@ -1,6 +1,5 @@ import uuid - MAX_UID_LENGTH = 64 UID_SPLIT_LENGTH = 3 UUID_LENGTH = 32 diff --git a/guillotina/documentation/sphinx/__init__.py b/guillotina/documentation/sphinx/__init__.py index caee4e097..cdeef9745 100644 --- a/guillotina/documentation/sphinx/__init__.py +++ b/guillotina/documentation/sphinx/__init__.py @@ -24,7 +24,6 @@ import logging import pkg_resources - logger = logging.getLogger("guillotina.docs") diff --git a/guillotina/entrypoint.py b/guillotina/entrypoint.py index 39258a76c..8d79b64b8 100644 --- a/guillotina/entrypoint.py +++ b/guillotina/entrypoint.py @@ -2,7 +2,6 @@ import os - if "G_CONFIG_FILE" not in os.environ: raise Exception("You must provide the envar G_CONFIG_FILE") diff --git a/guillotina/factory/app.py b/guillotina/factory/app.py index 61465ff87..ccc300dd5 100644 --- a/guillotina/factory/app.py +++ b/guillotina/factory/app.py @@ -34,7 +34,6 @@ import logging import logging.config - app_logger = logging.getLogger("guillotina") logger = glogging.getLogger("guillotina") diff --git a/guillotina/factory/content.py b/guillotina/factory/content.py index 16610c9da..b323475bb 100644 --- a/guillotina/factory/content.py +++ b/guillotina/factory/content.py @@ -27,7 +27,6 @@ import logging import typing - logger = logging.getLogger("guillotina") diff --git a/guillotina/fields/annotation.py b/guillotina/fields/annotation.py index fea7a1a1d..881e8fe72 100644 --- a/guillotina/fields/annotation.py +++ b/guillotina/fields/annotation.py @@ -30,7 +30,6 @@ import typing import uuid - logger = logging.getLogger("guillotina") diff --git a/guillotina/files/__init__.py b/guillotina/files/__init__.py index a7f3e04bb..771c437ef 100644 --- a/guillotina/files/__init__.py +++ b/guillotina/files/__init__.py @@ -9,5 +9,4 @@ from .utils import read_request_data # noqa from guillotina.exceptions import UnRetryableRequestError # noqa - CloudFileManager = FileManager # b/w compat diff --git a/guillotina/files/manager.py b/guillotina/files/manager.py index 0adbf901c..d3c745d82 100644 --- a/guillotina/files/manager.py +++ b/guillotina/files/manager.py @@ -29,7 +29,6 @@ import posixpath import uuid - logger = glogging.getLogger("guillotina") diff --git a/guillotina/glogging.py b/guillotina/glogging.py index eaca81c8c..1e155640c 100644 --- a/guillotina/glogging.py +++ b/guillotina/glogging.py @@ -6,9 +6,9 @@ def _wrapped(name): def log(self, *args, **kwargs): + from guillotina.exceptions import RequestNotFound from guillotina.utils import get_authenticated_user_id from guillotina.utils import get_current_request - from guillotina.exceptions import RequestNotFound func = getattr(self._logger, name) request = kwargs.pop("request", None) diff --git a/guillotina/gtypes.py b/guillotina/gtypes.py index 81469db35..6b12a039b 100644 --- a/guillotina/gtypes.py +++ b/guillotina/gtypes.py @@ -6,7 +6,6 @@ import types - ResolvableType = TypeVar("ResolvableType", types.ModuleType, types.FunctionType, type) ConfigurationType = List[Tuple[str, Dict[str, Any]]] diff --git a/guillotina/interfaces/__init__.py b/guillotina/interfaces/__init__.py index ca32a8fdf..a75fb5818 100644 --- a/guillotina/interfaces/__init__.py +++ b/guillotina/interfaces/__init__.py @@ -11,6 +11,7 @@ from .behaviors import IBehaviorAdapterFactory # noqa from .behaviors import IBehaviorSchemaAwareFactory # noqa from .behaviors import IContentBehavior # noqa +from .catalog import ICatalogDataAdapter # noqa; noqa from .catalog import ICatalogUtility # noqa from .catalog import IPGCatalogUtility # noqa from .catalog import ISearchParser # noqa @@ -137,10 +138,6 @@ from .views import IPUT # noqa from .views import IView # noqa - -from .catalog import ICatalogDataAdapter # noqa; noqa - - DEFAULT_ADD_PERMISSION = "guillotina.AddContent" DEFAULT_READ_PERMISSION = "guillotina.ViewContent" DEFAULT_WRITE_PERMISSION = "guillotina.ManageContent" diff --git a/guillotina/interfaces/content.py b/guillotina/interfaces/content.py index 1f65d4b94..7b8d99381 100644 --- a/guillotina/interfaces/content.py +++ b/guillotina/interfaces/content.py @@ -10,7 +10,6 @@ import guillotina.schema - if TYPE_CHECKING: # pragma: no cover from guillotina.db.interfaces import ITransactionManager else: diff --git a/guillotina/interfaces/json.py b/guillotina/interfaces/json.py index 7184e73e5..8d8914891 100644 --- a/guillotina/interfaces/json.py +++ b/guillotina/interfaces/json.py @@ -1,7 +1,6 @@ from guillotina.i18n import MessageFactory from zope.interface import Interface - _ = MessageFactory("guillotina") diff --git a/guillotina/interfaces/misc.py b/guillotina/interfaces/misc.py index 6c9101ddf..4dbf0bb73 100644 --- a/guillotina/interfaces/misc.py +++ b/guillotina/interfaces/misc.py @@ -56,8 +56,6 @@ def uninstall(container, request): # noqa: N805 class IIDChecker(Interface): - def __init__(context): - ... + def __init__(context): ... - async def __call__(id_: str, type_: str) -> bool: - ... + async def __call__(id_: str, type_: str) -> bool: ... diff --git a/guillotina/interfaces/registry.py b/guillotina/interfaces/registry.py index 2d11ebb00..e899f94b7 100644 --- a/guillotina/interfaces/registry.py +++ b/guillotina/interfaces/registry.py @@ -2,7 +2,6 @@ from guillotina.i18n import MessageFactory from zope.interface import Interface - _ = MessageFactory("guillotina") diff --git a/guillotina/interfaces/security.py b/guillotina/interfaces/security.py index 75459ff4a..7905a8647 100644 --- a/guillotina/interfaces/security.py +++ b/guillotina/interfaces/security.py @@ -13,7 +13,6 @@ import copyreg # type: ignore import typing - _ = MessageFactory("guillotina") diff --git a/guillotina/json/definitions.py b/guillotina/json/definitions.py index 17bbb46dd..1e832dd1f 100644 --- a/guillotina/json/definitions.py +++ b/guillotina/json/definitions.py @@ -1,6 +1,5 @@ from guillotina import configure - configure.json_schema_definition( "Addon", { diff --git a/guillotina/json/deserialize_content.py b/guillotina/json/deserialize_content.py index c6a1bd28f..87a8f6061 100644 --- a/guillotina/json/deserialize_content.py +++ b/guillotina/json/deserialize_content.py @@ -35,7 +35,6 @@ import asyncio - logger = glogging.getLogger("guillotina") diff --git a/guillotina/json/deserialize_value.py b/guillotina/json/deserialize_value.py index beecaae80..be4905413 100644 --- a/guillotina/json/deserialize_value.py +++ b/guillotina/json/deserialize_value.py @@ -25,7 +25,6 @@ import datetime - _type_conversions = (int, str, float, bool) diff --git a/guillotina/json/serialize_content.py b/guillotina/json/serialize_content.py index 0b1e0fa67..bacbde92f 100644 --- a/guillotina/json/serialize_content.py +++ b/guillotina/json/serialize_content.py @@ -25,7 +25,6 @@ import asyncio import logging - logger = logging.getLogger("guillotina") diff --git a/guillotina/json/serialize_schema_field.py b/guillotina/json/serialize_schema_field.py index bfd30568a..04734ed36 100644 --- a/guillotina/json/serialize_schema_field.py +++ b/guillotina/json/serialize_schema_field.py @@ -26,7 +26,6 @@ from zope.interface import implementedBy from zope.interface import Interface - FIELDS_CACHE: dict = {} diff --git a/guillotina/json/serialize_value.py b/guillotina/json/serialize_value.py index ea5105dd9..63bf99019 100644 --- a/guillotina/json/serialize_value.py +++ b/guillotina/json/serialize_value.py @@ -12,7 +12,6 @@ from guillotina.profile import profilable from guillotina.schema.vocabulary import SimpleVocabulary - _MISSING = object() diff --git a/guillotina/json/utils.py b/guillotina/json/utils.py index 5bbd6154e..b32564db1 100644 --- a/guillotina/json/utils.py +++ b/guillotina/json/utils.py @@ -13,7 +13,6 @@ import asyncio import logging - logger = logging.getLogger("guillotina") diff --git a/guillotina/metrics.py b/guillotina/metrics.py index d8512864f..75cd104a3 100644 --- a/guillotina/metrics.py +++ b/guillotina/metrics.py @@ -6,7 +6,6 @@ import time import traceback - try: from prometheus_client import Counter from prometheus_client import Histogram diff --git a/guillotina/permissions.py b/guillotina/permissions.py index 886b18c7b..3818623be 100644 --- a/guillotina/permissions.py +++ b/guillotina/permissions.py @@ -1,6 +1,5 @@ from guillotina import configure - configure.permission("guillotina.AccessContent", "Access content") configure.permission("guillotina.ModifyContent", "Modify content") configure.permission("guillotina.DeleteContent", "Delete content") diff --git a/guillotina/registry.py b/guillotina/registry.py index 2ce967400..fcd8982b4 100644 --- a/guillotina/registry.py +++ b/guillotina/registry.py @@ -6,7 +6,6 @@ from zope.interface import alsoProvides from zope.interface import implementer - REGISTRY_DATA_KEY = "_registry" diff --git a/guillotina/routes.py b/guillotina/routes.py index 6a9d095e7..fac280174 100644 --- a/guillotina/routes.py +++ b/guillotina/routes.py @@ -2,7 +2,6 @@ import re - URL_MATCH_RE = re.compile(r"\{[a-zA-Z0-9\_\-]+\}") _EXACT = object() diff --git a/guillotina/schema/__init__.py b/guillotina/schema/__init__.py index 17c3e0e74..0132ff68a 100644 --- a/guillotina/schema/__init__.py +++ b/guillotina/schema/__init__.py @@ -62,7 +62,6 @@ from guillotina.schema.accessors import accessors from guillotina.schema.exceptions import ValidationError - getFields = get_fields # b/w getFieldsInOrder = get_fields_in_order # b/w diff --git a/guillotina/schema/_bootstrapfields.py b/guillotina/schema/_bootstrapfields.py index fdac840c3..43f10bf5d 100644 --- a/guillotina/schema/_bootstrapfields.py +++ b/guillotina/schema/_bootstrapfields.py @@ -30,7 +30,6 @@ from zope.interface import implementer from zope.interface import providedBy - __docformat__ = "restructuredtext" diff --git a/guillotina/schema/_field.py b/guillotina/schema/_field.py index b337f1ee6..38c1a4229 100644 --- a/guillotina/schema/_field.py +++ b/guillotina/schema/_field.py @@ -94,7 +94,6 @@ import jsonschema import re - __docformat__ = "restructuredtext" # pep 8 friendlyness diff --git a/guillotina/schema/_messageid.py b/guillotina/schema/_messageid.py index b0338ca76..1d56c6b6a 100644 --- a/guillotina/schema/_messageid.py +++ b/guillotina/schema/_messageid.py @@ -14,5 +14,4 @@ from guillotina.i18n import MessageFactory - _ = MessageFactory("guillotina") diff --git a/guillotina/schema/accessors.py b/guillotina/schema/accessors.py index 491ed9f40..6c32cb1a6 100644 --- a/guillotina/schema/accessors.py +++ b/guillotina/schema/accessors.py @@ -15,7 +15,6 @@ from zope.interface import providedBy from zope.interface.interface import Method - """ Field accessors =============== diff --git a/guillotina/schema/fieldproperty.py b/guillotina/schema/fieldproperty.py index a4c35f767..a59256aac 100644 --- a/guillotina/schema/fieldproperty.py +++ b/guillotina/schema/fieldproperty.py @@ -20,7 +20,6 @@ import guillotina.schema import sys - _marker = object() diff --git a/guillotina/schema/interfaces.py b/guillotina/schema/interfaces.py index 164f9a7f3..7c57eaff4 100644 --- a/guillotina/schema/interfaces.py +++ b/guillotina/schema/interfaces.py @@ -24,7 +24,6 @@ from zope.interface import Interface from zope.interface.common.mapping import IEnumerableMapping - __docformat__ = "reStructuredText" # pep 8 friendlyness diff --git a/guillotina/schema/tests/states.py b/guillotina/schema/tests/states.py index 38b7683b0..097d3825d 100644 --- a/guillotina/schema/tests/states.py +++ b/guillotina/schema/tests/states.py @@ -16,7 +16,6 @@ from guillotina.schema import interfaces from zope.interface import implementer - # This table is based on information from the United States Postal Service: # http://www.usps.com/ncsc/lookups/abbreviations.html#states _states = { diff --git a/guillotina/schema/tests/test__bootstrapfields.py b/guillotina/schema/tests/test__bootstrapfields.py index 69e7f20d0..834e0aaad 100644 --- a/guillotina/schema/tests/test__bootstrapfields.py +++ b/guillotina/schema/tests/test__bootstrapfields.py @@ -130,8 +130,8 @@ def _provoke(inst): self.assertRaises(ValueError, _provoke, inst) def test___get___w_defaultFactory_w_ICAF_w_check(self): - from zope.interface import directlyProvides from guillotina.schema._bootstrapinterfaces import IContextAwareDefaultFactory + from zope.interface import directlyProvides _checked = [] @@ -589,14 +589,14 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_ITextLine(self): - from zope.interface.verify import verifyClass from guillotina.schema.interfaces import ITextLine + from zope.interface.verify import verifyClass verifyClass(ITextLine, self._getTargetClass()) def test_instance_conforms_to_ITextLine(self): - from zope.interface.verify import verifyObject from guillotina.schema.interfaces import ITextLine + from zope.interface.verify import verifyObject verifyObject(ITextLine, self._makeOne()) diff --git a/guillotina/schema/tests/test__field.py b/guillotina/schema/tests/test__field.py index 79efce408..866d14ca8 100644 --- a/guillotina/schema/tests/test__field.py +++ b/guillotina/schema/tests/test__field.py @@ -25,14 +25,14 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IBytes(self): - from zope.interface.verify import verifyClass from guillotina.schema.interfaces import IBytes + from zope.interface.verify import verifyClass verifyClass(IBytes, self._getTargetClass()) def test_instance_conforms_to_IBytes(self): - from zope.interface.verify import verifyObject from guillotina.schema.interfaces import IBytes + from zope.interface.verify import verifyObject verifyObject(IBytes, self._makeOne()) @@ -91,14 +91,14 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IASCII(self): - from zope.interface.verify import verifyClass from guillotina.schema.interfaces import IASCII + from zope.interface.verify import verifyClass verifyClass(IASCII, self._getTargetClass()) def test_instance_conforms_to_IASCII(self): - from zope.interface.verify import verifyObject from guillotina.schema.interfaces import IASCII + from zope.interface.verify import verifyObject verifyObject(IASCII, self._makeOne()) @@ -143,14 +143,14 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IBytesLine(self): - from zope.interface.verify import verifyClass from guillotina.schema.interfaces import IBytesLine + from zope.interface.verify import verifyClass verifyClass(IBytesLine, self._getTargetClass()) def test_instance_conforms_to_IBytesLine(self): - from zope.interface.verify import verifyObject from guillotina.schema.interfaces import IBytesLine + from zope.interface.verify import verifyObject verifyObject(IBytesLine, self._makeOne()) @@ -203,14 +203,14 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IASCIILine(self): - from zope.interface.verify import verifyClass from guillotina.schema.interfaces import IASCIILine + from zope.interface.verify import verifyClass verifyClass(IASCIILine, self._getTargetClass()) def test_instance_conforms_to_IASCIILine(self): - from zope.interface.verify import verifyObject from guillotina.schema.interfaces import IASCIILine + from zope.interface.verify import verifyObject verifyObject(IASCIILine, self._makeOne()) @@ -268,14 +268,14 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IFloat(self): - from zope.interface.verify import verifyClass from guillotina.schema.interfaces import IFloat + from zope.interface.verify import verifyClass verifyClass(IFloat, self._getTargetClass()) def test_instance_conforms_to_IFloat(self): - from zope.interface.verify import verifyObject from guillotina.schema.interfaces import IFloat + from zope.interface.verify import verifyObject verifyObject(IFloat, self._makeOne()) @@ -351,14 +351,14 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IDecimal(self): - from zope.interface.verify import verifyClass from guillotina.schema.interfaces import IDecimal + from zope.interface.verify import verifyClass verifyClass(IDecimal, self._getTargetClass()) def test_instance_conforms_to_IDecimal(self): - from zope.interface.verify import verifyObject from guillotina.schema.interfaces import IDecimal + from zope.interface.verify import verifyObject verifyObject(IDecimal, self._makeOne()) @@ -372,9 +372,10 @@ def test_validate_not_required(self): field.validate(None) def test_validate_required(self): - import decimal from guillotina.schema.exceptions import RequiredMissing + import decimal + field = self._makeOne(required=True) field.validate(decimal.Decimal("10.0")) field.validate(decimal.Decimal("0.93")) @@ -382,9 +383,10 @@ def test_validate_required(self): self.assertRaises(RequiredMissing, field.validate, None) def test_validate_min(self): - import decimal from guillotina.schema.exceptions import TooSmall + import decimal + field = self._makeOne(min=decimal.Decimal("10.5")) field.validate(decimal.Decimal("10.6")) field.validate(decimal.Decimal("20.2")) @@ -392,9 +394,10 @@ def test_validate_min(self): self.assertRaises(TooSmall, field.validate, decimal.Decimal("10.4")) def test_validate_max(self): - import decimal from guillotina.schema.exceptions import TooBig + import decimal + field = self._makeOne(max=decimal.Decimal("10.5")) field.validate(decimal.Decimal("5.3")) field.validate(decimal.Decimal("-9.1")) @@ -402,10 +405,11 @@ def test_validate_max(self): self.assertRaises(TooBig, field.validate, decimal.Decimal("20.7")) def test_validate_min_and_max(self): - import decimal from guillotina.schema.exceptions import TooBig from guillotina.schema.exceptions import TooSmall + import decimal + field = self._makeOne(min=decimal.Decimal("-0.6"), max=decimal.Decimal("10.1")) field.validate(decimal.Decimal("0.0")) field.validate(decimal.Decimal("-0.03")) @@ -442,14 +446,14 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IDatetime(self): - from zope.interface.verify import verifyClass from guillotina.schema.interfaces import IDatetime + from zope.interface.verify import verifyClass verifyClass(IDatetime, self._getTargetClass()) def test_instance_conforms_to_IDatetime(self): - from zope.interface.verify import verifyObject from guillotina.schema.interfaces import IDatetime + from zope.interface.verify import verifyObject verifyObject(IDatetime, self._makeOne()) @@ -534,14 +538,14 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IDate(self): - from zope.interface.verify import verifyClass from guillotina.schema.interfaces import IDate + from zope.interface.verify import verifyClass verifyClass(IDate, self._getTargetClass()) def test_instance_conforms_to_IDate(self): - from zope.interface.verify import verifyObject from guillotina.schema.interfaces import IDate + from zope.interface.verify import verifyObject verifyObject(IDate, self._makeOne()) @@ -630,14 +634,14 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_ITimedelta(self): - from zope.interface.verify import verifyClass from guillotina.schema.interfaces import ITimedelta + from zope.interface.verify import verifyClass verifyClass(ITimedelta, self._getTargetClass()) def test_instance_conforms_to_ITimedelta(self): - from zope.interface.verify import verifyObject from guillotina.schema.interfaces import ITimedelta + from zope.interface.verify import verifyObject verifyObject(ITimedelta, self._makeOne()) @@ -707,14 +711,14 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_ITime(self): - from zope.interface.verify import verifyClass from guillotina.schema.interfaces import ITime + from zope.interface.verify import verifyClass verifyClass(ITime, self._getTargetClass()) def test_instance_conforms_to_ITime(self): - from zope.interface.verify import verifyObject from guillotina.schema.interfaces import ITime + from zope.interface.verify import verifyObject verifyObject(ITime, self._makeOne()) @@ -795,14 +799,14 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IChoice(self): - from zope.interface.verify import verifyClass from guillotina.schema.interfaces import IChoice + from zope.interface.verify import verifyClass verifyClass(IChoice, self._getTargetClass()) def test_instance_conforms_to_IChoice(self): - from zope.interface.verify import verifyObject from guillotina.schema.interfaces import IChoice + from zope.interface.verify import verifyObject verifyObject(IChoice, self._makeOne(values=[1, 2, 3])) @@ -858,9 +862,9 @@ def _provoke(bound): self.assertRaises(ValidationError, _provoke, bound) def test_bind_w_voc_not_ICSB(self): - from zope.interface import implementer - from guillotina.schema.interfaces import ISource from guillotina.schema.interfaces import IBaseVocabulary + from guillotina.schema.interfaces import ISource + from zope.interface import implementer @implementer(IBaseVocabulary) @implementer(ISource) @@ -874,9 +878,9 @@ def __init__(self): self.assertTrue(target.vocabulary is source.vocabulary) def test_bind_w_voc_is_ICSB(self): - from zope.interface import implementer from guillotina.schema.interfaces import IContextSourceBinder from guillotina.schema.interfaces import ISource + from zope.interface import implementer @implementer(IContextSourceBinder) @implementer(ISource) @@ -896,8 +900,8 @@ def __call__(self, context): self.assertEqual(target.vocabulary.context, instance) def test_bind_w_voc_is_ICSB_but_not_ISource(self): - from zope.interface import implementer from guillotina.schema.interfaces import IContextSourceBinder + from zope.interface import implementer @implementer(IContextSourceBinder) class Vocab(object): @@ -980,8 +984,8 @@ def test__validate_w_named_vocabulary(self): self.assertRaises(ConstraintNotSatisfied, choose._validate, 42) def test__validate_source_is_ICSB_unbound(self): - from zope.interface import implementer from guillotina.schema.interfaces import IContextSourceBinder + from zope.interface import implementer @implementer(IContextSourceBinder) class SampleContextSourceBinder(object): @@ -992,10 +996,10 @@ def __call__(self, context): self.assertRaises(TypeError, choice.validate, 1) def test__validate_source_is_ICSB_bound(self): - from zope.interface import implementer - from guillotina.schema.interfaces import IContextSourceBinder from guillotina.schema.exceptions import ConstraintNotSatisfied + from guillotina.schema.interfaces import IContextSourceBinder from guillotina.schema.tests.test_vocabulary import _makeSampleVocabulary + from zope.interface import implementer @implementer(IContextSourceBinder) class SampleContextSourceBinder(object): @@ -1023,14 +1027,14 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IURI(self): - from zope.interface.verify import verifyClass from guillotina.schema.interfaces import IURI + from zope.interface.verify import verifyClass verifyClass(IURI, self._getTargetClass()) def test_instance_conforms_to_IURI(self): - from zope.interface.verify import verifyObject from guillotina.schema.interfaces import IURI + from zope.interface.verify import verifyObject verifyObject(IURI, self._makeOne()) @@ -1095,14 +1099,14 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IDottedName(self): - from zope.interface.verify import verifyClass from guillotina.schema.interfaces import IDottedName + from zope.interface.verify import verifyClass verifyClass(IDottedName, self._getTargetClass()) def test_instance_conforms_to_IDottedName(self): - from zope.interface.verify import verifyObject from guillotina.schema.interfaces import IDottedName + from zope.interface.verify import verifyObject verifyObject(IDottedName, self._makeOne()) @@ -1202,14 +1206,14 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IId(self): - from zope.interface.verify import verifyClass from guillotina.schema.interfaces import IId + from zope.interface.verify import verifyClass verifyClass(IId, self._getTargetClass()) def test_instance_conforms_to_IId(self): - from zope.interface.verify import verifyObject from guillotina.schema.interfaces import IId + from zope.interface.verify import verifyObject verifyObject(IId, self._makeOne()) @@ -1280,14 +1284,14 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IInterfaceField(self): - from zope.interface.verify import verifyClass from guillotina.schema.interfaces import IInterfaceField + from zope.interface.verify import verifyClass verifyClass(IInterfaceField, self._getTargetClass()) def test_instance_conforms_to_IInterfaceField(self): - from zope.interface.verify import verifyObject from guillotina.schema.interfaces import IInterfaceField + from zope.interface.verify import verifyObject verifyObject(IInterfaceField, self._makeOne()) @@ -1319,8 +1323,8 @@ class DummyInterface(Interface): field.validate(None) def test_validate_required(self): - from zope.interface import Interface from guillotina.schema.exceptions import RequiredMissing + from zope.interface import Interface class DummyInterface(Interface): pass @@ -1379,16 +1383,16 @@ def test_bind_w_value_Type(self): self.assertEqual(bound.unique, True) def test__validate_wrong_contained_type(self): - from guillotina.schema.exceptions import WrongContainedType from guillotina.schema._bootstrapfields import Text + from guillotina.schema.exceptions import WrongContainedType text = Text() absc = self._makeOne(text) self.assertRaises(WrongContainedType, absc.validate, [1]) def test__validate_miss_uniqueness(self): - from guillotina.schema.exceptions import NotUnique from guillotina.schema._bootstrapfields import Text + from guillotina.schema.exceptions import NotUnique text = Text() absc = self._makeOne(text, True) @@ -1405,14 +1409,14 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_ITuple(self): - from zope.interface.verify import verifyClass from guillotina.schema.interfaces import ITuple + from zope.interface.verify import verifyClass verifyClass(ITuple, self._getTargetClass()) def test_instance_conforms_to_ITuple(self): - from zope.interface.verify import verifyObject from guillotina.schema.interfaces import ITuple + from zope.interface.verify import verifyObject verifyObject(ITuple, self._makeOne()) @@ -1485,14 +1489,14 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IList(self): - from zope.interface.verify import verifyClass from guillotina.schema.interfaces import IList + from zope.interface.verify import verifyClass verifyClass(IList, self._getTargetClass()) def test_instance_conforms_to_IList(self): - from zope.interface.verify import verifyObject from guillotina.schema.interfaces import IList + from zope.interface.verify import verifyObject verifyObject(IList, self._makeOne()) @@ -1566,14 +1570,14 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_ISet(self): - from zope.interface.verify import verifyClass from guillotina.schema.interfaces import ISet + from zope.interface.verify import verifyClass verifyClass(ISet, self._getTargetClass()) def test_instance_conforms_to_ISet(self): - from zope.interface.verify import verifyObject from guillotina.schema.interfaces import ISet + from zope.interface.verify import verifyObject verifyObject(ISet, self._makeOne()) @@ -1655,14 +1659,14 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IFrozenSet(self): - from zope.interface.verify import verifyClass from guillotina.schema.interfaces import IFrozenSet + from zope.interface.verify import verifyClass verifyClass(IFrozenSet, self._getTargetClass()) def test_instance_conforms_to_IFrozenSet(self): - from zope.interface.verify import verifyObject from guillotina.schema.interfaces import IFrozenSet + from zope.interface.verify import verifyObject verifyObject(IFrozenSet, self._makeOne()) @@ -1771,11 +1775,11 @@ def _getErrors(self, f, *args, **kw): self.fail("Expected WrongContainedType Error") def _makeCycles(self): - from zope.interface import Interface - from zope.interface import implementer - from guillotina.schema import Object from guillotina.schema import List + from guillotina.schema import Object from guillotina.schema._messageid import _ + from zope.interface import implementer + from zope.interface import Interface class IUnit(Interface): """A schema that participate to a cycle""" @@ -1812,14 +1816,14 @@ def __init__(self, unit): return IUnit, Person, Unit def test_class_conforms_to_IObject(self): - from zope.interface.verify import verifyClass from guillotina.schema.interfaces import IObject + from zope.interface.verify import verifyClass verifyClass(IObject, self._getTargetClass()) def test_instance_conforms_to_IObject(self): - from zope.interface.verify import verifyObject from guillotina.schema.interfaces import IObject + from zope.interface.verify import verifyObject verifyObject(IObject, self._makeOne()) @@ -1844,20 +1848,20 @@ def test__validate_w_empty_schema(self): objf.validate(object()) # doesn't raise def test__validate_w_value_not_providing_schema(self): - from guillotina.schema.exceptions import SchemaNotProvided from guillotina.schema._bootstrapfields import Text + from guillotina.schema.exceptions import SchemaNotProvided schema = self._makeSchema(foo=Text(), bar=Text()) objf = self._makeOne(schema) self.assertRaises(SchemaNotProvided, objf.validate, object()) def test__validate_w_value_providing_invalid_schema(self): - from zope.interface import implementer + from guillotina.schema._bootstrapfields import Text + from guillotina.schema.exceptions import RequiredMissing from guillotina.schema.exceptions import SchemaNotProvided from guillotina.schema.exceptions import WrongContainedType - from guillotina.schema.exceptions import RequiredMissing from guillotina.schema.exceptions import WrongType - from guillotina.schema._bootstrapfields import Text + from zope.interface import implementer schema = self._makeSchema(foo=Text()) objf = self._makeOne(schema) @@ -1873,10 +1877,10 @@ def test__validate_w_value_providing_invalid_schema(self): self.assertRaises(SchemaNotProvided, objf.validate, {"foo": "val", "bar": "val"}) def test__validate_w_value_providing_schema_but_missing_fields(self): - from zope.interface import implementer + from guillotina.schema._bootstrapfields import Text from guillotina.schema.exceptions import RequiredMissing from guillotina.schema.exceptions import WrongContainedType - from guillotina.schema._bootstrapfields import Text + from zope.interface import implementer schema = self._makeSchema(foo=Text(required=True), bar=Text(required=True)) @@ -1893,11 +1897,11 @@ class Broken(object): self.assertTrue(isinstance(err, RequiredMissing)) def test__validate_w_value_providing_schema_but_invalid_fields(self): - from zope.interface import implementer - from guillotina.schema.exceptions import WrongContainedType + from guillotina.schema._bootstrapfields import Text from guillotina.schema.exceptions import RequiredMissing + from guillotina.schema.exceptions import WrongContainedType from guillotina.schema.exceptions import WrongType - from guillotina.schema._bootstrapfields import Text + from zope.interface import implementer schema = self._makeSchema(foo=Text(required=True), bar=Text(required=True)) @@ -1919,9 +1923,9 @@ class Broken(object): self.assertEqual(err.args[:3], (1, str, "bar")) def test__validate_w_value_providing_schema(self): - from zope.interface import implementer from guillotina.schema._bootstrapfields import Text from guillotina.schema._field import Choice + from zope.interface import implementer schema = self._makeSchema(foo=Text(), bar=Text(), baz=Choice(values=[1, 2, 3])) @@ -1935,7 +1939,8 @@ class OK(object): objf.validate(OK()) # doesn't raise def test__validate_interface_inheritance(self): - from guillotina.schema import Int, Object + from guillotina.schema import Int + from guillotina.schema import Object from zope.interface import implementer from zope.interface import Interface @@ -2004,14 +2009,14 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IDict(self): - from zope.interface.verify import verifyClass from guillotina.schema.interfaces import IDict + from zope.interface.verify import verifyClass verifyClass(IDict, self._getTargetClass()) def test_instance_conforms_to_IDict(self): - from zope.interface.verify import verifyObject from guillotina.schema.interfaces import IDict + from zope.interface.verify import verifyObject verifyObject(IDict, self._makeOne()) @@ -2052,8 +2057,8 @@ def test_validate_required(self): self.assertRaises(RequiredMissing, field.validate, None) def test_validate_invalid_key_type(self): - from guillotina.schema.exceptions import WrongContainedType from guillotina.schema._bootstrapfields import Int + from guillotina.schema.exceptions import WrongContainedType field = self._makeOne(key_type=Int()) field.validate({}) @@ -2062,8 +2067,8 @@ def test_validate_invalid_key_type(self): self.assertRaises(WrongContainedType, field.validate, {"a": 1}) def test_validate_invalid_value_type(self): - from guillotina.schema.exceptions import WrongContainedType from guillotina.schema._bootstrapfields import Int + from guillotina.schema.exceptions import WrongContainedType field = self._makeOne(value_type=Int()) field.validate({}) @@ -2132,21 +2137,21 @@ def _getTargetClass(self): return UnionField def _makeOne(self, *args, **kw): - from guillotina.schema._field import Text from guillotina.schema._field import Int from guillotina.schema._field import List + from guillotina.schema._field import Text return self._getTargetClass()(Text(**kw), Int(**kw), List(**kw), *args, **kw) def test_class_conforms_to_IUnionField(self): - from zope.interface.verify import verifyClass from guillotina.schema.interfaces import IUnionField + from zope.interface.verify import verifyClass verifyClass(IUnionField, self._getTargetClass()) def test_instance_conforms_to_IUnionField(self): - from zope.interface.verify import verifyObject from guillotina.schema.interfaces import IUnionField + from zope.interface.verify import verifyObject verifyObject(IUnionField, self._makeOne()) @@ -2198,8 +2203,8 @@ class DummyInstance(object): def _makeSampleVocabulary(): - from zope.interface import implementer from guillotina.schema.interfaces import IVocabulary + from zope.interface import implementer class SampleTerm(object): pass diff --git a/guillotina/schema/tests/test_accessors.py b/guillotina/schema/tests/test_accessors.py index 5c802b853..c09ea0257 100644 --- a/guillotina/schema/tests/test_accessors.py +++ b/guillotina/schema/tests/test_accessors.py @@ -38,8 +38,8 @@ def test_ctor_not_created_inside_interface(self): self.assertEqual(wrapped.__doc__, "get Hmm") def test_ctor_created_inside_interface(self): - from zope.interface import Interface from guillotina.schema import Text + from zope.interface import Interface field = Text(title="Hmm") @@ -58,9 +58,9 @@ def test___provides___w_field_no_provides(self): self.assertEqual(list(providedBy(wrapped)), list(implementedBy(self._getTargetClass()))) def test___provides___w_field_w_provides(self): + from guillotina.schema import Text from zope.interface import implementedBy from zope.interface import providedBy - from guillotina.schema import Text field = Text() field_provides = list(providedBy(field)) @@ -151,8 +151,8 @@ def getter(self): self.assertEqual(getter.query(Foo()), "123") def test_set_readonly(self): - from zope.interface import Interface from guillotina.schema import Text + from zope.interface import Interface field = Text(readonly=True) @@ -241,8 +241,8 @@ def test_ctor_not_created_inside_interface(self): self.assertEqual(wrapped.__doc__, "set Hmm") def test_ctor_created_inside_interface(self): - from zope.interface import Interface from guillotina.schema import Text + from zope.interface import Interface field = Text(title="Hmm") @@ -274,8 +274,8 @@ def _callFUT(self, *args, **kw): return accessors(*args, **kw) def test_w_only_read_accessor(self): - from zope.interface import Interface from guillotina.schema import Text + from zope.interface import Interface field = Text(title="Hmm", readonly=True) @@ -294,8 +294,8 @@ class IFoo(Interface): self.assertEqual(info["kwargs"], None) def test_w_read_and_write_accessors(self): - from zope.interface import Interface from guillotina.schema import Text + from zope.interface import Interface field = Text(title="Hmm") diff --git a/guillotina/schema/tests/test_fieldproperty.py b/guillotina/schema/tests/test_fieldproperty.py index 82761fef7..b10bbaa90 100644 --- a/guillotina/schema/tests/test_fieldproperty.py +++ b/guillotina/schema/tests/test_fieldproperty.py @@ -255,8 +255,8 @@ class Foo(object): self.assertEqual(_validated, ["123"]) def test_field_event(self): - from guillotina.schema import Text from guillotina.component.event import sync_subscribers + from guillotina.schema import Text from guillotina.schema.fieldproperty import FieldUpdatedEvent log = [] @@ -277,8 +277,8 @@ def test_field_event(self): ) def test_field_event_update(self): - from guillotina.schema import Text from guillotina.component.event import sync_subscribers + from guillotina.schema import Text from guillotina.schema.fieldproperty import FieldUpdatedEvent field = Text(__name__="testing", description="DESCRIPTION", default="DEFAULT", required=True) @@ -539,8 +539,8 @@ class Foo(object): self.assertRaises(ValueError, setattr, foo, "testing", "123") def test_field_event_update(self): - from guillotina.schema import Text from guillotina.component.event import sync_subscribers + from guillotina.schema import Text from guillotina.schema.fieldproperty import FieldUpdatedEvent field = Text(__name__="testing", description="DESCRIPTION", default="DEFAULT", required=True) @@ -566,8 +566,8 @@ class Foo(object): def test_field_event(self): # fieldproperties are everywhere including in field themselfs # so event are triggered - from guillotina.schema import Text from guillotina.component.event import sync_subscribers + from guillotina.schema import Text from guillotina.schema.fieldproperty import FieldUpdatedEvent log = [] @@ -590,10 +590,10 @@ def test_field_event(self): def _getSchema(): - from zope.interface import Interface from guillotina.schema import Bytes from guillotina.schema import Float from guillotina.schema import Text + from zope.interface import Interface class Schema(Interface): title = Text(description="Short summary", default="say something") diff --git a/guillotina/schema/tests/test_interfaces.py b/guillotina/schema/tests/test_interfaces.py index 5f3b5a433..ae10c4142 100644 --- a/guillotina/schema/tests/test_interfaces.py +++ b/guillotina/schema/tests/test_interfaces.py @@ -23,11 +23,11 @@ def test_non_fields(self): self.assertEqual(self._callFUT(object()), False) def test_w_normal_fields(self): - from guillotina.schema import Text from guillotina.schema import Bytes - from guillotina.schema import Int - from guillotina.schema import Float from guillotina.schema import Decimal + from guillotina.schema import Float + from guillotina.schema import Int + from guillotina.schema import Text self.assertEqual(self._callFUT(Text()), True) self.assertEqual(self._callFUT(Bytes()), True) @@ -36,8 +36,8 @@ def test_w_normal_fields(self): self.assertEqual(self._callFUT(Decimal()), True) def test_w_explicitly_provided(self): - from zope.interface import directlyProvides from guillotina.schema.interfaces import IField + from zope.interface import directlyProvides class Foo(object): pass @@ -64,11 +64,11 @@ def test_w_non_fields(self): self.assertEqual(self._callFUT([object()]), False) def test_w_fields(self): - from guillotina.schema import Text from guillotina.schema import Bytes - from guillotina.schema import Int - from guillotina.schema import Float from guillotina.schema import Decimal + from guillotina.schema import Float + from guillotina.schema import Int + from guillotina.schema import Text self.assertEqual(self._callFUT([Text()]), True) self.assertEqual(self._callFUT([Bytes()]), True) @@ -78,11 +78,11 @@ def test_w_fields(self): self.assertEqual(self._callFUT([Text(), Bytes(), Int(), Float(), Decimal()]), True) def test_w_mixed(self): - from guillotina.schema import Text from guillotina.schema import Bytes - from guillotina.schema import Int - from guillotina.schema import Float from guillotina.schema import Decimal + from guillotina.schema import Float + from guillotina.schema import Int + from guillotina.schema import Text self.assertEqual(self._callFUT([Text(), 0]), False) self.assertEqual(self._callFUT([Text(), Bytes(), Int(), Float(), Decimal(), 0]), False) diff --git a/guillotina/schema/tests/test_schema.py b/guillotina/schema/tests/test_schema.py index b9bea2f78..524e493d0 100644 --- a/guillotina/schema/tests/test_schema.py +++ b/guillotina/schema/tests/test_schema.py @@ -18,8 +18,8 @@ def _makeSchema(): - from zope.interface import Interface from guillotina.schema import Bytes + from zope.interface import Interface return InterfaceClass( "ISchemaTest", @@ -153,9 +153,9 @@ class IEmpty(Interface): self.assertEqual(len(errors), 0) def test_schema_with_field_errors(self): - from zope.interface import Interface from guillotina.schema import Text from guillotina.schema.exceptions import SchemaNotFullyImplemented + from zope.interface import Interface class IWithRequired(Interface): must = Text(required=True) @@ -200,8 +200,8 @@ def _callFUT(self, schema, object): return getSchemaValidationErrors(schema, object) def test_schema_wo_fields(self): - from zope.interface import Interface from zope.interface import Attribute + from zope.interface import Interface class INoFields(Interface): def method(): @@ -213,8 +213,8 @@ def method(): self.assertEqual(len(errors), 0) def test_schema_with_fields_ok(self): - from zope.interface import Interface from guillotina.schema import Text + from zope.interface import Interface class IWithFields(Interface): foo = Text() @@ -228,9 +228,9 @@ class Obj(object): self.assertEqual(len(errors), 0) def test_schema_with_missing_field(self): - from zope.interface import Interface from guillotina.schema import Text from guillotina.schema.exceptions import SchemaNotFullyImplemented + from zope.interface import Interface class IWithRequired(Interface): must = Text(required=True) @@ -241,9 +241,9 @@ class IWithRequired(Interface): self.assertEqual(errors[0][1].__class__, SchemaNotFullyImplemented) def test_schema_with_invalid_field(self): - from zope.interface import Interface from guillotina.schema import Int from guillotina.schema.exceptions import TooSmall + from zope.interface import Interface class IWithMinium(Interface): value = Int(required=True, min=0) diff --git a/guillotina/schema/tests/test_states.py b/guillotina/schema/tests/test_states.py index 322d776b3..27c271a2b 100644 --- a/guillotina/schema/tests/test_states.py +++ b/guillotina/schema/tests/test_states.py @@ -17,9 +17,9 @@ class StateSelectionTest(unittest.TestCase): def setUp(self): + from guillotina.schema.tests.states import StateVocabulary from guillotina.schema.vocabulary import _clear from guillotina.schema.vocabulary import getVocabularyRegistry - from guillotina.schema.tests.states import StateVocabulary _clear() vr = getVocabularyRegistry() @@ -31,9 +31,9 @@ def tearDown(self): _clear() def _makeSchema(self): - from zope.interface import Interface from guillotina.schema import Choice from guillotina.schema.tests.states import StateVocabulary + from zope.interface import Interface class IBirthInfo(Interface): state1 = Choice( @@ -58,8 +58,8 @@ class IBirthInfo(Interface): return IBirthInfo def test_default_presentation(self): - from zope.interface.verify import verifyObject from guillotina.schema.interfaces import IVocabulary + from zope.interface.verify import verifyObject schema = self._makeSchema() field = schema.getDescriptionFor("state1") @@ -68,9 +68,9 @@ def test_default_presentation(self): self.assertEqual(bound.vocabulary.getTerm("VA").title, "Virginia") def test_contains(self): - from zope.interface.verify import verifyObject from guillotina.schema.interfaces import IVocabulary from guillotina.schema.tests.states import StateVocabulary + from zope.interface.verify import verifyObject vocab = StateVocabulary() self.assertTrue(verifyObject(IVocabulary, vocab)) @@ -88,8 +88,8 @@ def test_contains(self): self.assertEqual(L, L2) def test_prebound_vocabulary(self): - from zope.interface.verify import verifyObject from guillotina.schema.interfaces import IVocabulary + from zope.interface.verify import verifyObject schema = self._makeSchema() field = schema.getDescriptionFor("state3") # type: ignore diff --git a/guillotina/schema/tests/test_vocabulary.py b/guillotina/schema/tests/test_vocabulary.py index 00b9b1ce1..1e6168254 100644 --- a/guillotina/schema/tests/test_vocabulary.py +++ b/guillotina/schema/tests/test_vocabulary.py @@ -25,14 +25,14 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_ITokenizedTerm(self): - from zope.interface.verify import verifyClass from guillotina.schema.interfaces import ITokenizedTerm + from zope.interface.verify import verifyClass verifyClass(ITokenizedTerm, self._getTargetClass()) def test_instance_conforms_to_ITokenizedTerm(self): - from zope.interface.verify import verifyObject from guillotina.schema.interfaces import ITokenizedTerm + from zope.interface.verify import verifyObject verifyObject(ITokenizedTerm, self._makeOne("VALUE")) @@ -73,20 +73,20 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IVocabularyTokenized(self): - from zope.interface.verify import verifyClass from guillotina.schema.interfaces import IVocabularyTokenized + from zope.interface.verify import verifyClass verifyClass(IVocabularyTokenized, self._getTargetClass()) def test_instance_conforms_to_IVocabularyTokenized(self): - from zope.interface.verify import verifyObject from guillotina.schema.interfaces import IVocabularyTokenized + from zope.interface.verify import verifyObject verifyObject(IVocabularyTokenized, self._makeOne(())) def test_ctor_additional_interfaces(self): - from zope.interface import Interface from guillotina.schema.vocabulary import SimpleTerm + from zope.interface import Interface class IStupid(Interface): pass @@ -105,8 +105,8 @@ class IStupid(Interface): self.assertTrue(vocabulary.getTermByToken(term.token) is term) def test_fromValues(self): - from zope.interface import Interface from guillotina.schema.interfaces import ITokenizedTerm + from zope.interface import Interface class IStupid(Interface): pass @@ -121,8 +121,8 @@ class IStupid(Interface): self.assertTrue(value in vocabulary) def test_fromItems(self): - from zope.interface import Interface from guillotina.schema.interfaces import ITokenizedTerm + from zope.interface import Interface class IStupid(Interface): pass @@ -240,11 +240,11 @@ def tree_vocab_3(self): return self._getTargetClass().fromDict(self.business_tree()) def test_implementation(self): - from zope.interface.verify import verifyObject - from zope.interface.common.mapping import IEnumerableMapping from guillotina.schema.interfaces import ITreeVocabulary from guillotina.schema.interfaces import IVocabulary from guillotina.schema.interfaces import IVocabularyTokenized + from zope.interface.common.mapping import IEnumerableMapping + from zope.interface.verify import verifyObject for v in [self.tree_vocab_2(), self.tree_vocab_3()]: self.assertTrue(verifyObject(IEnumerableMapping, v)) @@ -508,8 +508,8 @@ def tearDown(self): _clear() def test_setVocabularyRegistry(self): - from guillotina.schema.vocabulary import setVocabularyRegistry from guillotina.schema.vocabulary import getVocabularyRegistry + from guillotina.schema.vocabulary import setVocabularyRegistry r = _makeDummyRegistry() setVocabularyRegistry(r) @@ -526,8 +526,8 @@ def test_getVocabularyRegistry(self): def _makeSampleVocabulary(): - from zope.interface import implementer from guillotina.schema.interfaces import IVocabulary + from zope.interface import implementer class SampleTerm(object): pass diff --git a/guillotina/schema/vocabulary.py b/guillotina/schema/vocabulary.py index 3efc38b4b..456d52bba 100644 --- a/guillotina/schema/vocabulary.py +++ b/guillotina/schema/vocabulary.py @@ -23,7 +23,6 @@ from zope.interface import directlyProvides from zope.interface import implementer - # simple vocabularies performing enumerated-like tasks _marker = object() diff --git a/guillotina/security/__init__.py b/guillotina/security/__init__.py index 244a554e7..fdc3108ae 100644 --- a/guillotina/security/__init__.py +++ b/guillotina/security/__init__.py @@ -1,4 +1,3 @@ from . import cache - security_map_cache = cache.SecurityMapCacheManager() diff --git a/guillotina/security/policy.py b/guillotina/security/policy.py index ad275b03d..9f9546dc4 100644 --- a/guillotina/security/policy.py +++ b/guillotina/security/policy.py @@ -26,7 +26,6 @@ from typing import Optional from typing import Union - code_principal_permission_setting = principal_permission_manager.get_setting code_roles_for_permission = role_permission_manager.get_roles_for_permission code_roles_for_principal = principal_role_manager.get_roles_for_principal diff --git a/guillotina/security/utils.py b/guillotina/security/utils.py index 57dd4535e..8585d1428 100644 --- a/guillotina/security/utils.py +++ b/guillotina/security/utils.py @@ -85,7 +85,7 @@ def settings_for_object(ob): if inherit_permissions is not None: settings = inherit_permissions.get_locked_permissions() data["perminhe"] = [] - for (p, s) in settings: + for p, s in settings: if s is Deny: locked_permissions.append(p) data["perminhe"].append({"permission": p, "setting": s}) diff --git a/guillotina/subscribers.py b/guillotina/subscribers.py index 969bf4a4f..dbbd59463 100644 --- a/guillotina/subscribers.py +++ b/guillotina/subscribers.py @@ -7,7 +7,6 @@ from guillotina.interfaces import IObjectModifiedEvent from guillotina.interfaces import IResource - _zone = tzutc() diff --git a/guillotina/task_vars.py b/guillotina/task_vars.py index 50ead784c..70f4e2731 100644 --- a/guillotina/task_vars.py +++ b/guillotina/task_vars.py @@ -10,7 +10,6 @@ from typing import Dict from typing import Optional - request: ContextVar[Optional[IRequest]] = ContextVar("g_request", default=None) txn: ContextVar[Optional[ITransaction]] = ContextVar("g_txn", default=None) tm: ContextVar[Optional[ITransactionManager]] = ContextVar("g_tm", default=None) diff --git a/guillotina/test_package.py b/guillotina/test_package.py index 98ddfb204..8dbfe7409 100644 --- a/guillotina/test_package.py +++ b/guillotina/test_package.py @@ -46,7 +46,6 @@ import tempfile import typing - app_settings = {"applications": ["guillotina"]} diff --git a/guillotina/testing.py b/guillotina/testing.py index 6aecbfb64..ce59d4426 100644 --- a/guillotina/testing.py +++ b/guillotina/testing.py @@ -7,7 +7,6 @@ import base64 import os - TESTING_PORT = 55001 TESTING_SETTINGS: Dict[str, Any] = { diff --git a/guillotina/tests/__init__.py b/guillotina/tests/__init__.py index 3bdbda876..796b58a07 100644 --- a/guillotina/tests/__init__.py +++ b/guillotina/tests/__init__.py @@ -1,5 +1,4 @@ import os - TEST_DIR = os.path.dirname(os.path.realpath(__file__)) TEST_RESOURCES_DIR = os.path.join(TEST_DIR, "resources") diff --git a/guillotina/tests/cache/test_cache_memory.py b/guillotina/tests/cache/test_cache_memory.py index 7bbaf408c..eeae81ff3 100644 --- a/guillotina/tests/cache/test_cache_memory.py +++ b/guillotina/tests/cache/test_cache_memory.py @@ -11,7 +11,6 @@ import asyncio import pytest - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/cache/test_cache_pubsub.py b/guillotina/tests/cache/test_cache_pubsub.py index 58c7f8e67..87c49afea 100644 --- a/guillotina/tests/cache/test_cache_pubsub.py +++ b/guillotina/tests/cache/test_cache_pubsub.py @@ -12,7 +12,6 @@ import pickle import pytest - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/cache/test_cache_store.py b/guillotina/tests/cache/test_cache_store.py index f4e8cb38f..9aa966f62 100644 --- a/guillotina/tests/cache/test_cache_store.py +++ b/guillotina/tests/cache/test_cache_store.py @@ -8,7 +8,6 @@ import pytest - pytestmark = pytest.mark.asyncio DEFAULT_SETTINGS = { diff --git a/guillotina/tests/cache/test_cache_txn.py b/guillotina/tests/cache/test_cache_txn.py index 7ce9831db..e58337bee 100644 --- a/guillotina/tests/cache/test_cache_txn.py +++ b/guillotina/tests/cache/test_cache_txn.py @@ -9,7 +9,6 @@ import pytest - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/cache/test_utils.py b/guillotina/tests/cache/test_utils.py index 1e5ca1409..a1292e1e8 100644 --- a/guillotina/tests/cache/test_utils.py +++ b/guillotina/tests/cache/test_utils.py @@ -7,6 +7,7 @@ async def test_get_size_of_item(): rcache = CacheUtility() from guillotina.contrib.cache.utility import _default_size + import sys assert rcache.get_size(dict(a=1)) == _default_size diff --git a/guillotina/tests/conftest.py b/guillotina/tests/conftest.py index 1166a425f..2719bba22 100644 --- a/guillotina/tests/conftest.py +++ b/guillotina/tests/conftest.py @@ -1,6 +1,5 @@ from pytest_docker_fixtures import images - images.configure( name="cockroach", image="cockroachdb/cockroach", diff --git a/guillotina/tests/dbusers/test_api.py b/guillotina/tests/dbusers/test_api.py index 096bec057..4d504894a 100644 --- a/guillotina/tests/dbusers/test_api.py +++ b/guillotina/tests/dbusers/test_api.py @@ -5,7 +5,6 @@ import json import pytest - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/dbusers/test_manage_groups.py b/guillotina/tests/dbusers/test_manage_groups.py index 712c34c0c..21843bb5c 100644 --- a/guillotina/tests/dbusers/test_manage_groups.py +++ b/guillotina/tests/dbusers/test_manage_groups.py @@ -6,7 +6,6 @@ import pytest import pytest_asyncio - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/dbusers/test_manage_users.py b/guillotina/tests/dbusers/test_manage_users.py index 67b886fa1..0c6a97c2e 100644 --- a/guillotina/tests/dbusers/test_manage_users.py +++ b/guillotina/tests/dbusers/test_manage_users.py @@ -6,7 +6,6 @@ import pytest import pytest_asyncio - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/dbusers/test_registration.py b/guillotina/tests/dbusers/test_registration.py index c6807326d..e304a5a49 100644 --- a/guillotina/tests/dbusers/test_registration.py +++ b/guillotina/tests/dbusers/test_registration.py @@ -5,7 +5,6 @@ import json import pytest - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/dbusers/test_reset_password.py b/guillotina/tests/dbusers/test_reset_password.py index a2eaf98fe..b7f509aa3 100644 --- a/guillotina/tests/dbusers/test_reset_password.py +++ b/guillotina/tests/dbusers/test_reset_password.py @@ -6,7 +6,6 @@ import json import pytest - pytestmark = pytest.mark.asyncio NEW_PASSWORD = "password2" diff --git a/guillotina/tests/dbusers/test_security.py b/guillotina/tests/dbusers/test_security.py index cb5b4a688..c2f96b678 100644 --- a/guillotina/tests/dbusers/test_security.py +++ b/guillotina/tests/dbusers/test_security.py @@ -5,7 +5,6 @@ import json import pytest - pytestmark = pytest.mark.asyncio configure.permission("dbusers.SeeTopSecret", "SeeTopSecret", "Ability to see TopSecret docs") diff --git a/guillotina/tests/dbusers/test_setup.py b/guillotina/tests/dbusers/test_setup.py index 39d13804c..e5cddc9a6 100644 --- a/guillotina/tests/dbusers/test_setup.py +++ b/guillotina/tests/dbusers/test_setup.py @@ -3,7 +3,6 @@ import pytest - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/dyncontent/test_dynapi.py b/guillotina/tests/dyncontent/test_dynapi.py index 2e0662229..3dc380a9c 100644 --- a/guillotina/tests/dyncontent/test_dynapi.py +++ b/guillotina/tests/dyncontent/test_dynapi.py @@ -3,7 +3,6 @@ import json import pytest - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/fixtures.py b/guillotina/tests/fixtures.py index 54403591e..8b63b3aaa 100644 --- a/guillotina/tests/fixtures.py +++ b/guillotina/tests/fixtures.py @@ -29,7 +29,6 @@ import pytest import pytest_asyncio - _dir = os.path.dirname(os.path.realpath(__file__)) DATABASE = os.environ.get("DATABASE", "DUMMY") @@ -418,8 +417,8 @@ async def __aexit__(self, exc_type, exc, tb): @pytest.fixture(scope="function") def dummy_request(dummy_guillotina, monkeypatch): - from guillotina.interfaces import IApplication from guillotina.component import get_utility + from guillotina.interfaces import IApplication root = get_utility(IApplication, name="root") db = root["db"] @@ -498,7 +497,8 @@ async def app(event_loop, db, request): host = server_settings.get("host", "127.0.0.1") port = int(server_settings.get("port", 8000)) - from uvicorn import Config, Server + from uvicorn import Config + from uvicorn import Server config = Config(app, host=host, port=port, lifespan="on", server_header=False) server = Server(config=config) diff --git a/guillotina/tests/image/__init__.py b/guillotina/tests/image/__init__.py index 7d132ed83..d2fbd0e60 100644 --- a/guillotina/tests/image/__init__.py +++ b/guillotina/tests/image/__init__.py @@ -1,4 +1,3 @@ import os.path - TEST_DATA_LOCATION = os.path.join(os.path.dirname(__file__), "data") diff --git a/guillotina/tests/image/test_field.py b/guillotina/tests/image/test_field.py index d664aaee5..2c0387328 100644 --- a/guillotina/tests/image/test_field.py +++ b/guillotina/tests/image/test_field.py @@ -10,7 +10,6 @@ import os import pytest - pytestmark = pytest.mark.asyncio NOT_POSTGRES = os.environ.get("DATABASE", "DUMMY") in ("cockroachdb", "DUMMY") diff --git a/guillotina/tests/image/test_scale.py b/guillotina/tests/image/test_scale.py index c4245a19a..ce2a88730 100644 --- a/guillotina/tests/image/test_scale.py +++ b/guillotina/tests/image/test_scale.py @@ -10,7 +10,6 @@ import PIL.ImageDraw import warnings - with open(os.path.join(TEST_DATA_LOCATION, "logo.png"), "rb") as fio: PNG = fio.read() with open(os.path.join(TEST_DATA_LOCATION, "logo.gif"), "rb") as fio: diff --git a/guillotina/tests/mailer/test_mailer.py b/guillotina/tests/mailer/test_mailer.py index cea68c026..090372e51 100644 --- a/guillotina/tests/mailer/test_mailer.py +++ b/guillotina/tests/mailer/test_mailer.py @@ -3,7 +3,6 @@ import pytest - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/mcp/test_mcp.py b/guillotina/tests/mcp/test_mcp.py index d9e1e736b..0d6e8addb 100644 --- a/guillotina/tests/mcp/test_mcp.py +++ b/guillotina/tests/mcp/test_mcp.py @@ -5,7 +5,6 @@ import json import pytest - pytestmark = pytest.mark.asyncio MCP_SETTINGS = { diff --git a/guillotina/tests/memcached/test_cache.py b/guillotina/tests/memcached/test_cache.py index eaa6d84f3..4ff3ff8cc 100644 --- a/guillotina/tests/memcached/test_cache.py +++ b/guillotina/tests/memcached/test_cache.py @@ -13,7 +13,6 @@ import pytest - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/memcached/test_memcached_driver.py b/guillotina/tests/memcached/test_memcached_driver.py index fabc681c3..d5ef3649f 100644 --- a/guillotina/tests/memcached/test_memcached_driver.py +++ b/guillotina/tests/memcached/test_memcached_driver.py @@ -1,8 +1,9 @@ try: - import emcache from guillotina.contrib.memcached.driver import MemcachedDriver from guillotina.contrib.memcached.driver import safe_key from guillotina.contrib.memcached.driver import update_connection_pool_metrics + + import emcache except ModuleNotFoundError: emcache = None @@ -18,7 +19,6 @@ import pytest import pytest_asyncio - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/memcached/test_metrics.py b/guillotina/tests/memcached/test_metrics.py index f22878d9b..1140da22e 100644 --- a/guillotina/tests/memcached/test_metrics.py +++ b/guillotina/tests/memcached/test_metrics.py @@ -8,7 +8,6 @@ import pytest - pytestmark = pytest.mark.asyncio MEMCACHED_SETTINGS = {"applications": ["guillotina", "memcached", "guillotina.contrib.memcached"]} diff --git a/guillotina/tests/redis/test_driver.py b/guillotina/tests/redis/test_driver.py index 4b61ee55f..16ec3155d 100644 --- a/guillotina/tests/redis/test_driver.py +++ b/guillotina/tests/redis/test_driver.py @@ -3,7 +3,6 @@ import asyncio import pytest - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/redis_session/test_session.py b/guillotina/tests/redis_session/test_session.py index 2b93cc11a..ef594ecfb 100644 --- a/guillotina/tests/redis_session/test_session.py +++ b/guillotina/tests/redis_session/test_session.py @@ -6,7 +6,6 @@ import jwt import pytest - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_annotations.py b/guillotina/tests/test_annotations.py index d29f46906..05e00ec48 100644 --- a/guillotina/tests/test_annotations.py +++ b/guillotina/tests/test_annotations.py @@ -11,7 +11,6 @@ import pytest import time - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_api.py b/guillotina/tests/test_api.py index 2eb86d8b5..9c5936253 100644 --- a/guillotina/tests/test_api.py +++ b/guillotina/tests/test_api.py @@ -23,7 +23,6 @@ import json import pytest - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_attachment.py b/guillotina/tests/test_attachment.py index 8c6128264..da51721b9 100644 --- a/guillotina/tests/test_attachment.py +++ b/guillotina/tests/test_attachment.py @@ -13,7 +13,6 @@ import pytest import random - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_auth.py b/guillotina/tests/test_auth.py index 3893918b8..f95043d85 100644 --- a/guillotina/tests/test_auth.py +++ b/guillotina/tests/test_auth.py @@ -6,7 +6,6 @@ import jwt import pytest - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_blob.py b/guillotina/tests/test_blob.py index 8011c6f70..1df273598 100644 --- a/guillotina/tests/test_blob.py +++ b/guillotina/tests/test_blob.py @@ -13,7 +13,6 @@ import pytest - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_catalog.py b/guillotina/tests/test_catalog.py index f027a220e..6fbb3636e 100644 --- a/guillotina/tests/test_catalog.py +++ b/guillotina/tests/test_catalog.py @@ -24,7 +24,6 @@ import os import pytest - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_cockroach.py b/guillotina/tests/test_cockroach.py index 975596830..8ba54fb03 100644 --- a/guillotina/tests/test_cockroach.py +++ b/guillotina/tests/test_cockroach.py @@ -8,7 +8,6 @@ import os import pytest - pytestmark = [ pytest.mark.asyncio, pytest.mark.skipif( diff --git a/guillotina/tests/test_commands.py b/guillotina/tests/test_commands.py index ea3f8a81a..a661762b7 100644 --- a/guillotina/tests/test_commands.py +++ b/guillotina/tests/test_commands.py @@ -15,7 +15,6 @@ import os import pytest - DATABASE = os.environ.get("DATABASE", "DUMMY") DB_SCHEMA = os.environ.get("DB_SCHEMA", "public") diff --git a/guillotina/tests/test_configure.py b/guillotina/tests/test_configure.py index d8ebdec24..5ca819288 100644 --- a/guillotina/tests/test_configure.py +++ b/guillotina/tests/test_configure.py @@ -18,7 +18,6 @@ import pytest - pytestmark = pytest.mark.asyncio @@ -103,8 +102,8 @@ class MyType(Item): async def test_register_behavior(container_requester): cur_count = len(configure.get_configurations("guillotina.tests", "behavior")) - from guillotina.interfaces import IResource from guillotina import schema + from guillotina.interfaces import IResource class IMyBehavior(Interface): foobar = schema.Text() diff --git a/guillotina/tests/test_configure_component.py b/guillotina/tests/test_configure_component.py index 741166fd6..a4c4040e6 100644 --- a/guillotina/tests/test_configure_component.py +++ b/guillotina/tests/test_configure_component.py @@ -23,10 +23,11 @@ def _callFUT(self, *args, **kw): # noqa: N802 return handler(*args, **kw) def test_uses_configured_site_manager(self): - from zope.interface.registry import Components from guillotina.component import get_component_registry - from guillotina.component.testfiles.components import comp, IApp from guillotina.component._compat import _BLANK + from guillotina.component.testfiles.components import comp + from guillotina.component.testfiles.components import IApp + from zope.interface.registry import Components registry = Components() @@ -86,8 +87,8 @@ def _callFUT(self, *args, **kw): # noqa: N802 return adapter(*args, **kw) def test_empty_factory(self): - from zope.interface import Interface from guillotina.configure.component import ComponentConfigurationError + from zope.interface import Interface class IFoo(Interface): pass @@ -96,8 +97,8 @@ class IFoo(Interface): self.assertRaises(ComponentConfigurationError, self._callFUT, _cfg_ctx, [], [Interface], IFoo) def test_multiple_factory_multiple_for_(self): - from zope.interface import Interface from guillotina.configure.component import ComponentConfigurationError + from zope.interface import Interface class IFoo(Interface): pass @@ -135,7 +136,8 @@ class IBar(Interface): pass from guillotina.component import adapter - from zope.interface import implementer, named + from zope.interface import implementer + from zope.interface import named @adapter(IFoo) @implementer(IBar) @@ -151,8 +153,8 @@ def __init__(self, context): self.assertEqual(action["args"][4], "bar") def test_no_for__factory_adapts_no_provides_factory_not_implement(self): - from zope.interface import Interface from guillotina.component._declaration import adapter + from zope.interface import Interface @adapter(Interface) class _Factory(object): @@ -163,9 +165,9 @@ def __init__(self, context): self.assertRaises(TypeError, self._callFUT, _cfg_ctx, [_Factory]) def test_multiple_factory_single_for__w_name(self): - from zope.interface import Interface from guillotina.component.interface import provide_interface from guillotina.configure.component import handler + from zope.interface import Interface class IFoo(Interface): pass @@ -203,10 +205,10 @@ class Bar(object): self.assertEqual(action["args"], ("", Interface)) def test_no_for__no_provides_factory_adapts_factory_implement(self): - from zope.interface import Interface - from zope.interface import implementer from guillotina.component._declaration import adapter from guillotina.configure.component import handler + from zope.interface import implementer + from zope.interface import Interface class IFoo(Interface): pass @@ -286,9 +288,9 @@ class Foo(object): self.assertRaises(TypeError, self._callFUT, _cfg_ctx, factory=Foo, provides=IFoo) def test_no_factory_w_handler_no_provides(self): - from zope.interface import Interface from guillotina.component.interface import provide_interface from guillotina.configure.component import handler + from zope.interface import Interface def _handler(*args): pass @@ -313,9 +315,9 @@ def _handler(*args): self.assertEqual(action["args"], ("", Interface)) def test_w_factory_w_provides(self): - from zope.interface import Interface from guillotina.component.interface import provide_interface from guillotina.configure.component import handler + from zope.interface import Interface class IFoo(Interface): pass @@ -380,9 +382,9 @@ def test_w_component_wo_provides_component_no_provides(self): self.assertRaises(TypeError, self._callFUT, _cfg_ctx, component=_COMPONENT) def test_w_factory_w_provides(self): - from zope.interface import Interface from guillotina.component.interface import provide_interface from guillotina.configure.component import handler + from zope.interface import Interface class IFoo(Interface): pass @@ -411,10 +413,10 @@ class Foo(object): self.assertEqual(action["args"], ("", IFoo)) def test_w_factory_wo_provides_factory_implement(self): - from zope.interface import Interface - from zope.interface import implementer from guillotina.component.interface import provide_interface from guillotina.configure.component import handler + from zope.interface import implementer + from zope.interface import Interface class IFoo(Interface): pass @@ -444,9 +446,9 @@ class Foo(object): self.assertEqual(action["args"], ("", IFoo)) def test_w_component_w_provides_w_name(self): - from zope.interface import Interface from guillotina.component.interface import provide_interface from guillotina.configure.component import handler + from zope.interface import Interface class IFoo(Interface): pass @@ -472,7 +474,9 @@ class IFoo(Interface): self.assertEqual(action["args"], ("", IFoo)) def test_w_component_wo_provides_wo_name(self): - from zope.interface import Interface, implementer, named + from zope.interface import implementer + from zope.interface import Interface + from zope.interface import named class IFoo(Interface): pass @@ -491,10 +495,10 @@ class Foo(object): self.assertEqual(action["args"][3], "foo") def test_w_component_wo_provides_component_provides(self): - from zope.interface import Interface - from zope.interface import directlyProvides from guillotina.component.interface import provide_interface from guillotina.configure.component import handler + from zope.interface import directlyProvides + from zope.interface import Interface class IFoo(Interface): pass @@ -531,8 +535,8 @@ def _callFUT(self, *args, **kw): # noqa: N802 return interface(*args, **kw) def test_wo_name_wo_type(self): - from zope.interface import Interface from guillotina.component.interface import provide_interface + from zope.interface import Interface class IFoo(Interface): pass @@ -547,8 +551,8 @@ class IFoo(Interface): self.assertEqual(action["args"], ("", IFoo, None)) def test_w_name_w_type(self): - from zope.interface import Interface from guillotina.component.interface import provide_interface + from zope.interface import Interface class IFoo(Interface): pass @@ -573,8 +577,8 @@ def _callFUT(self, *args, **kw): # noqa: N802 return view(*args, **kw) def test_w_factory_as_empty(self): - from zope.interface import Interface from guillotina.configure.component import ComponentConfigurationError + from zope.interface import Interface class IViewType(Interface): pass @@ -591,8 +595,8 @@ class IViewType(Interface): ) def test_w_multiple_factory_multiple_for_(self): - from zope.interface import Interface from guillotina.configure.component import ComponentConfigurationError + from zope.interface import Interface class IViewType(Interface): pass @@ -615,8 +619,8 @@ class Bar(object): ) def test_w_for__as_empty(self): - from zope.interface import Interface from guillotina.configure.component import ComponentConfigurationError + from zope.interface import Interface class IViewType(Interface): pass @@ -631,9 +635,9 @@ def __init__(self, context): ) def test_w_single_factory_single_for__wo_permission_w_name(self): - from zope.interface import Interface - from guillotina.configure.component import handler from guillotina.component.interface import provide_interface + from guillotina.configure.component import handler + from zope.interface import Interface class IViewType(Interface): pass @@ -674,8 +678,8 @@ def __init__(self, context): self.assertEqual(action["args"], ("", IViewType)) def test_w_multiple_factory_single_for__wo_permission(self): - from zope.interface import Interface from guillotina.configure.component import handler + from zope.interface import Interface class IViewType(Interface): pass @@ -737,10 +741,13 @@ def test_suite(): def test_configuration_machine_allows_overriding(): - from guillotina.configure.config import ConfigurationMachine + from guillotina.component import adapter + from guillotina.component import get_adapter from guillotina.configure import component - from guillotina.component import adapter, get_adapter - from zope.interface import implementer, Interface, named + from guillotina.configure.config import ConfigurationMachine + from zope.interface import implementer + from zope.interface import Interface + from zope.interface import named class IFoo(Interface): pass diff --git a/guillotina/tests/test_content.py b/guillotina/tests/test_content.py index 2324d71e2..ba9b5ec1e 100644 --- a/guillotina/tests/test_content.py +++ b/guillotina/tests/test_content.py @@ -27,7 +27,6 @@ import pickle import pytest - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_contentapi.py b/guillotina/tests/test_contentapi.py index d93847e3a..5d00781fb 100644 --- a/guillotina/tests/test_contentapi.py +++ b/guillotina/tests/test_contentapi.py @@ -3,7 +3,6 @@ import pytest - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_cors.py b/guillotina/tests/test_cors.py index 8a282cec6..8d90fee78 100644 --- a/guillotina/tests/test_cors.py +++ b/guillotina/tests/test_cors.py @@ -6,7 +6,6 @@ import pytest - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_dbobjects.py b/guillotina/tests/test_dbobjects.py index 8781f19e4..58a16d49c 100644 --- a/guillotina/tests/test_dbobjects.py +++ b/guillotina/tests/test_dbobjects.py @@ -8,7 +8,6 @@ import pytest - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_dynamic_schema.py b/guillotina/tests/test_dynamic_schema.py index 2e4b8a30d..e34e59956 100644 --- a/guillotina/tests/test_dynamic_schema.py +++ b/guillotina/tests/test_dynamic_schema.py @@ -11,7 +11,6 @@ import pytest import pytest_asyncio - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_extrafieldattr.py b/guillotina/tests/test_extrafieldattr.py index 1e294575b..161192890 100644 --- a/guillotina/tests/test_extrafieldattr.py +++ b/guillotina/tests/test_extrafieldattr.py @@ -1,6 +1,5 @@ import pytest - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_guillo.py b/guillotina/tests/test_guillo.py index 5b18466eb..521e3f53a 100644 --- a/guillotina/tests/test_guillo.py +++ b/guillotina/tests/test_guillo.py @@ -8,7 +8,6 @@ import logging import pytest - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_info.py b/guillotina/tests/test_info.py index ee300fbb3..a2709c04c 100644 --- a/guillotina/tests/test_info.py +++ b/guillotina/tests/test_info.py @@ -2,7 +2,6 @@ import pytest - pytestmark = pytest.mark.asyncio FAKE_RECAPTCHA = "FAKE_RECAPTCHA" diff --git a/guillotina/tests/test_login.py b/guillotina/tests/test_login.py index eeafa3d6e..3e8513cb0 100644 --- a/guillotina/tests/test_login.py +++ b/guillotina/tests/test_login.py @@ -5,7 +5,6 @@ import jwt import pytest - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_metrics.py b/guillotina/tests/test_metrics.py index 4f442232d..2a4479341 100644 --- a/guillotina/tests/test_metrics.py +++ b/guillotina/tests/test_metrics.py @@ -15,7 +15,6 @@ import prometheus_client import pytest - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_postgres.py b/guillotina/tests/test_postgres.py index 0bb9f0fe8..92f8391c7 100644 --- a/guillotina/tests/test_postgres.py +++ b/guillotina/tests/test_postgres.py @@ -18,7 +18,6 @@ import os import pytest - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_queue.py b/guillotina/tests/test_queue.py index fa39c0feb..d745f2a2f 100644 --- a/guillotina/tests/test_queue.py +++ b/guillotina/tests/test_queue.py @@ -8,7 +8,6 @@ import asyncio import pytest - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_request.py b/guillotina/tests/test_request.py index afa6df373..8ae297dfe 100644 --- a/guillotina/tests/test_request.py +++ b/guillotina/tests/test_request.py @@ -4,7 +4,6 @@ import asyncio import pytest - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_security.py b/guillotina/tests/test_security.py index 2f0abef5b..5603d047e 100644 --- a/guillotina/tests/test_security.py +++ b/guillotina/tests/test_security.py @@ -17,7 +17,6 @@ import json import pytest - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_serialize.py b/guillotina/tests/test_serialize.py index a0647840a..576774550 100644 --- a/guillotina/tests/test_serialize.py +++ b/guillotina/tests/test_serialize.py @@ -26,7 +26,6 @@ import pytest import uuid - pytestmark = pytest.mark.asyncio @@ -78,8 +77,9 @@ async def test_serialize_omit_main_interface_field(dummy_request, mock_txn): async def test_serialize_cloud_file(dummy_request, mock_txn): - from guillotina.test_package import FileContent, IFileContent from guillotina.interfaces import IFileManager + from guillotina.test_package import FileContent + from guillotina.test_package import IFileContent obj = create_content(FileContent) obj.file = DBFile(filename="foobar.json", md5="foobar") @@ -101,7 +101,8 @@ async def _data(): async def test_deserialize_cloud_file(dummy_request, mock_txn): - from guillotina.test_package import IFileContent, FileContent + from guillotina.test_package import FileContent + from guillotina.test_package import IFileContent obj = create_content(FileContent) obj.file = None diff --git a/guillotina/tests/test_static.py b/guillotina/tests/test_static.py index 180453d9d..461f56917 100644 --- a/guillotina/tests/test_static.py +++ b/guillotina/tests/test_static.py @@ -3,7 +3,6 @@ import pytest - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_storages.py b/guillotina/tests/test_storages.py index a5bd8f941..4b8f29f3d 100644 --- a/guillotina/tests/test_storages.py +++ b/guillotina/tests/test_storages.py @@ -8,7 +8,6 @@ import os import pytest - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_swagger.py b/guillotina/tests/test_swagger.py index b020ff8fe..d332999ad 100644 --- a/guillotina/tests/test_swagger.py +++ b/guillotina/tests/test_swagger.py @@ -4,7 +4,6 @@ import os import pytest - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_transactions.py b/guillotina/tests/test_transactions.py index 103a63653..fc65f82b4 100644 --- a/guillotina/tests/test_transactions.py +++ b/guillotina/tests/test_transactions.py @@ -11,7 +11,6 @@ import pytest - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_ws.py b/guillotina/tests/test_ws.py index 83297625c..0e944e0ec 100644 --- a/guillotina/tests/test_ws.py +++ b/guillotina/tests/test_ws.py @@ -3,7 +3,6 @@ import json import pytest - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/vocabularies/test_vocabularies.py b/guillotina/tests/vocabularies/test_vocabularies.py index f750677ab..df7973b5a 100644 --- a/guillotina/tests/vocabularies/test_vocabularies.py +++ b/guillotina/tests/vocabularies/test_vocabularies.py @@ -1,6 +1,5 @@ import pytest - pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/workflows/test_workflow_basic.py b/guillotina/tests/workflows/test_workflow_basic.py index efbb6da0b..ae8748994 100644 --- a/guillotina/tests/workflows/test_workflow_basic.py +++ b/guillotina/tests/workflows/test_workflow_basic.py @@ -1,6 +1,5 @@ import pytest - pytestmark = pytest.mark.asyncio guillotina_basic_with_translations = { diff --git a/guillotina/transactions.py b/guillotina/transactions.py index 21574123b..cd622c5a0 100644 --- a/guillotina/transactions.py +++ b/guillotina/transactions.py @@ -5,7 +5,6 @@ import logging import typing - logger = logging.getLogger("guillotina") diff --git a/guillotina/traversal.py b/guillotina/traversal.py index 23942ee28..331a28a0a 100644 --- a/guillotina/traversal.py +++ b/guillotina/traversal.py @@ -1,4 +1,5 @@ """Main routing traversal class.""" + from contextlib import contextmanager from guillotina import __version__ from guillotina import logger @@ -195,9 +196,9 @@ def debug(self, request, resp): for idx, query in enumerate(txn._queries.keys()): counts = txn._queries[query] duration = "{0:.5f}".format(counts[1] * 1000) - resp.headers[ - f"XG-Query-{idx}" - ] = f"count: {counts[0]}, time: {duration}, query: {query}" # noqa + resp.headers[f"XG-Query-{idx}"] = ( + f"count: {counts[0]}, time: {duration}, query: {query}" # noqa + ) except (KeyError, AttributeError): resp.headers["XG-Error"] = "Could not get stats" diff --git a/guillotina/utils/__init__.py b/guillotina/utils/__init__.py index 08cc9306d..745f7c7af 100644 --- a/guillotina/utils/__init__.py +++ b/guillotina/utils/__init__.py @@ -17,6 +17,7 @@ from .content import valid_id # noqa from .crypto import get_jwk_key # noqa from .crypto import secure_passphrase # noqa +from .misc import apply_coroutine # noqa; noqa from .misc import dump_task_vars # noqa from .misc import find_container # noqa from .misc import get_current_container # noqa @@ -50,8 +51,4 @@ from .modules import resolve_path # noqa from .navigator import Navigator # noqa - -from .misc import apply_coroutine # noqa; noqa - - get_object_by_oid = get_object_by_uid diff --git a/guillotina/utils/content.py b/guillotina/utils/content.py index cf2a2cd47..2c89a0ba3 100644 --- a/guillotina/utils/content.py +++ b/guillotina/utils/content.py @@ -21,7 +21,6 @@ import typing - logger = glogging.getLogger("guillotina") diff --git a/guillotina/utils/crypto.py b/guillotina/utils/crypto.py index 593d09ae0..4e0867946 100644 --- a/guillotina/utils/crypto.py +++ b/guillotina/utils/crypto.py @@ -4,7 +4,6 @@ import logging import string - logger = logging.getLogger("guillotina") diff --git a/guillotina/utils/misc.py b/guillotina/utils/misc.py index 08b13ce81..5a8b7989a 100644 --- a/guillotina/utils/misc.py +++ b/guillotina/utils/misc.py @@ -30,7 +30,6 @@ import typing import urllib.parse - try: random = random.SystemRandom() # type: ignore using_sys_random = True diff --git a/setup.cfg b/setup.cfg index c8fc479dd..aa7f7359a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -10,10 +10,9 @@ create-wheel = yes [isort] +profile=black lines_after_imports=2 -force_single_line=true line_length=110 -not_skip=__init__.py skip_glob=*.pyi From 6b007f0f4de7a07cfb78caf8994a655d7eeb8b4a Mon Sep 17 00:00:00 2001 From: nilbacardit26 Date: Mon, 30 Mar 2026 12:41:40 +0200 Subject: [PATCH 24/31] flake8 checkers --- guillotina/db/cache/dummy.py | 15 ++++++++++----- guillotina/db/orm/interfaces.py | 3 ++- guillotina/db/transaction.py | 3 ++- guillotina/interfaces/misc.py | 6 ++++-- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/guillotina/db/cache/dummy.py b/guillotina/db/cache/dummy.py index 5c99d313e..a0f50f4b7 100644 --- a/guillotina/db/cache/dummy.py +++ b/guillotina/db/cache/dummy.py @@ -14,12 +14,17 @@ async def get(self, **kwargs): async def set( self, value, keyset: List[Dict[str, Any]] = None, oid=None, container=None, id=None, variant=None - ): ... + ): + pass - async def clear(self): ... + async def clear(self): + pass - async def invalidate(self, ob): ... + async def invalidate(self, ob): + pass - async def delete(self, key): ... + async def delete(self, key): + pass - async def delete_all(self, keys): ... + async def delete_all(self, keys): + pass diff --git a/guillotina/db/orm/interfaces.py b/guillotina/db/orm/interfaces.py index 1b82230e5..e480124a5 100644 --- a/guillotina/db/orm/interfaces.py +++ b/guillotina/db/orm/interfaces.py @@ -60,4 +60,5 @@ def __getstate__(): # type: ignore The result must be picklable. """ - def register(): ... + def register(): + """ """ diff --git a/guillotina/db/transaction.py b/guillotina/db/transaction.py index 2bd3cde9d..ea4b8b692 100644 --- a/guillotina/db/transaction.py +++ b/guillotina/db/transaction.py @@ -72,7 +72,8 @@ def record_cache_metric( def record_cache_metric( name: str, result_type: str, value: Union[ObjectResultType, str], key_args: Dict[str, Any] - ) -> None: ... + ) -> None: + pass logger = logging.getLogger(__name__) diff --git a/guillotina/interfaces/misc.py b/guillotina/interfaces/misc.py index 4dbf0bb73..caf3a4374 100644 --- a/guillotina/interfaces/misc.py +++ b/guillotina/interfaces/misc.py @@ -56,6 +56,8 @@ def uninstall(container, request): # noqa: N805 class IIDChecker(Interface): - def __init__(context): ... + def __init__(context): + """ """ - async def __call__(id_: str, type_: str) -> bool: ... + async def __call__(id_: str, type_: str) -> bool: + """ """ From d95d322b988c68e7aa1ee66c60fa3387c2f7a98a Mon Sep 17 00:00:00 2001 From: nilbacardit26 Date: Mon, 30 Mar 2026 12:49:20 +0200 Subject: [PATCH 25/31] isort reformatting --- .isort.cfg | 8 +- guillotina/__init__.py | 11 +- guillotina/_settings.py | 10 +- guillotina/addons.py | 9 +- guillotina/annotations.py | 11 +- guillotina/api/__init__.py | 3 +- guillotina/api/addons.py | 8 +- guillotina/api/aggregation.py | 4 +- guillotina/api/app.py | 6 +- guillotina/api/behaviors.py | 8 +- guillotina/api/container.py | 35 +-- guillotina/api/content.py | 114 +++---- guillotina/api/files.py | 16 +- guillotina/api/login.py | 30 +- guillotina/api/metadata.py | 7 +- guillotina/api/registry.py | 12 +- guillotina/api/search.py | 6 +- guillotina/api/service.py | 23 +- guillotina/api/storage.py | 4 +- guillotina/api/types.py | 8 +- guillotina/api/user.py | 4 +- guillotina/api/vocabularies.py | 9 +- guillotina/api/ws.py | 29 +- guillotina/asgi.py | 21 +- guillotina/async_util.py | 20 +- guillotina/auth/extractors.py | 8 +- guillotina/auth/groups.py | 10 +- guillotina/auth/recaptcha.py | 3 +- guillotina/auth/role.py | 3 +- guillotina/auth/users.py | 6 +- guillotina/auth/utils.py | 16 +- guillotina/auth/validators.py | 24 +- guillotina/behaviors/__init__.py | 13 +- guillotina/behaviors/attachment.py | 6 +- guillotina/behaviors/dublincore.py | 8 +- guillotina/behaviors/dynamic.py | 10 +- guillotina/behaviors/instance.py | 12 +- guillotina/behaviors/properties.py | 1 + guillotina/blob.py | 6 +- guillotina/browser.py | 15 +- guillotina/catalog/__init__.py | 7 +- guillotina/catalog/catalog.py | 37 +-- guillotina/catalog/index.py | 29 +- guillotina/catalog/parser.py | 7 +- guillotina/catalog/types.py | 4 +- guillotina/catalog/utils.py | 21 +- guillotina/commands/__init__.py | 16 +- guillotina/commands/create.py | 6 +- guillotina/commands/crypto.py | 6 +- guillotina/commands/initialize_db.py | 3 +- guillotina/commands/migrate.py | 3 +- guillotina/commands/run.py | 9 +- guillotina/commands/serve_reload.py | 8 +- guillotina/commands/shell.py | 12 +- guillotina/commands/testdata.py | 14 +- guillotina/commands/vacuum.py | 3 +- guillotina/component/__init__.py | 60 ++-- guillotina/component/_api.py | 16 +- guillotina/component/_compat.py | 1 + guillotina/component/_declaration.py | 5 +- guillotina/component/event.py | 1 + guillotina/component/factory.py | 6 +- guillotina/component/globalregistry.py | 24 +- guillotina/component/interface.py | 3 +- guillotina/component/interfaces.py | 48 +-- guillotina/component/registry.py | 12 +- guillotina/component/testfiles/adapter.py | 4 +- guillotina/component/testfiles/components.py | 6 +- guillotina/component/testfiles/views.py | 4 +- guillotina/component/tests/examples.py | 10 +- guillotina/component/tests/test___init__.py | 15 +- guillotina/component/tests/test__api.py | 175 ++++++----- .../component/tests/test__declaration.py | 16 +- guillotina/component/tests/test_event.py | 3 +- guillotina/component/tests/test_factory.py | 12 +- .../component/tests/test_globalregistry.py | 43 ++- guillotina/component/tests/test_registry.py | 12 +- guillotina/configure/__init__.py | 72 +++-- guillotina/configure/behaviors.py | 8 +- guillotina/configure/component.py | 9 +- guillotina/configure/config.py | 9 +- guillotina/constraintypes.py | 14 +- guillotina/content.py | 117 +++---- guillotina/contentapi.py | 17 +- guillotina/contrib/cache/__init__.py | 1 + guillotina/contrib/cache/api.py | 3 +- guillotina/contrib/cache/memcache.py | 4 +- guillotina/contrib/cache/serialize.py | 7 +- guillotina/contrib/cache/strategy.py | 18 +- guillotina/contrib/cache/utility.py | 21 +- guillotina/contrib/catalog/pg/__init__.py | 3 +- guillotina/contrib/catalog/pg/indexes.py | 4 +- guillotina/contrib/catalog/pg/parser.py | 12 +- guillotina/contrib/catalog/pg/utility.py | 47 ++- guillotina/contrib/dbusers/__init__.py | 1 + guillotina/contrib/dbusers/adapters.py | 9 +- guillotina/contrib/dbusers/content/groups.py | 9 +- guillotina/contrib/dbusers/content/users.py | 14 +- guillotina/contrib/dbusers/install.py | 4 +- guillotina/contrib/dbusers/permissions.py | 1 + guillotina/contrib/dbusers/register_user.py | 3 +- guillotina/contrib/dbusers/serializers.py | 7 +- .../contrib/dbusers/services/__init__.py | 8 +- guillotina/contrib/dbusers/services/groups.py | 21 +- guillotina/contrib/dbusers/services/roles.py | 3 +- guillotina/contrib/dbusers/services/users.py | 26 +- guillotina/contrib/dbusers/services/utils.py | 10 +- guillotina/contrib/dbusers/subscribers.py | 22 +- guillotina/contrib/dbusers/users.py | 11 +- guillotina/contrib/dyncontent/__init__.py | 5 +- guillotina/contrib/dyncontent/subscriber.py | 40 +-- guillotina/contrib/dyncontent/vocabularies.py | 6 +- .../contrib/email_validation/__init__.py | 1 + .../contrib/email_validation/interfaces.py | 3 +- .../contrib/email_validation/utility.py | 24 +- guillotina/contrib/email_validation/utils.py | 14 +- guillotina/contrib/image/api.py | 13 +- guillotina/contrib/image/behaviors.py | 8 +- guillotina/contrib/image/image.py | 3 +- guillotina/contrib/image/interfaces.py | 3 +- guillotina/contrib/image/preview.py | 6 +- guillotina/contrib/image/scale.py | 6 +- guillotina/contrib/mailer/__init__.py | 3 +- guillotina/contrib/mailer/encoding.py | 4 +- guillotina/contrib/mailer/utility.py | 26 +- guillotina/contrib/mcp/__init__.py | 1 + guillotina/contrib/mcp/backend.py | 14 +- guillotina/contrib/mcp/interfaces.py | 3 +- guillotina/contrib/mcp/permissions.py | 1 + guillotina/contrib/mcp/resources.py | 11 +- guillotina/contrib/mcp/server.py | 6 +- guillotina/contrib/mcp/services.py | 7 +- guillotina/contrib/mcp/subscribers.py | 14 +- guillotina/contrib/mcp/tools.py | 20 +- guillotina/contrib/memcached/__init__.py | 5 +- guillotina/contrib/memcached/driver.py | 19 +- guillotina/contrib/pubsub/utility.py | 13 +- guillotina/contrib/redis/__init__.py | 2 + guillotina/contrib/redis/dm.py | 12 +- guillotina/contrib/redis/driver.py | 17 +- guillotina/contrib/redis_session/utility.py | 5 +- guillotina/contrib/swagger/__init__.py | 1 + guillotina/contrib/swagger/services.py | 27 +- guillotina/contrib/templates/__init__.py | 1 + guillotina/contrib/templates/interfaces.py | 3 +- guillotina/contrib/templates/permissions.py | 1 + guillotina/contrib/templates/utility.py | 16 +- guillotina/contrib/vocabularies/countries.py | 1 + guillotina/contrib/vocabularies/languages.py | 1 + guillotina/contrib/workflows/__init__.py | 6 +- guillotina/contrib/workflows/api.py | 6 +- guillotina/contrib/workflows/events.py | 3 +- guillotina/contrib/workflows/interfaces.py | 14 +- guillotina/contrib/workflows/permissions.py | 1 + .../contrib/workflows/post_serialize.py | 3 +- guillotina/contrib/workflows/subscriber.py | 10 +- guillotina/contrib/workflows/utility.py | 21 +- guillotina/contrib/workflows/vocabularies.py | 6 +- .../{{cookiecutter.package_name}}/setup.py | 4 +- .../{{cookiecutter.package_name}}/__init__.py | 1 + .../tests/fixtures.py | 7 +- .../tests/test_install.py | 1 + guillotina/cors.py | 6 +- guillotina/db/cache/base.py | 7 +- guillotina/db/cache/dummy.py | 8 +- guillotina/db/db.py | 3 +- guillotina/db/events.py | 3 +- guillotina/db/factory.py | 19 +- guillotina/db/interfaces.py | 12 +- guillotina/db/orm/base.py | 12 +- guillotina/db/orm/interfaces.py | 4 +- guillotina/db/reader.py | 4 +- guillotina/db/storages/cockroach.py | 20 +- guillotina/db/storages/dummy.py | 12 +- guillotina/db/storages/pg.py | 31 +- guillotina/db/storages/vacuum.py | 12 +- guillotina/db/transaction.py | 42 ++- guillotina/db/transaction_manager.py | 24 +- guillotina/db/uid.py | 1 + guillotina/db/writer.py | 10 +- guillotina/directives.py | 5 +- guillotina/documentation/__init__.py | 6 +- guillotina/documentation/sphinx/__init__.py | 20 +- guillotina/entrypoint.py | 3 +- guillotina/event.py | 3 +- guillotina/events.py | 79 ++--- guillotina/exc_resp.py | 37 ++- guillotina/exceptions.py | 8 +- guillotina/factory/app.py | 55 ++-- guillotina/factory/content.py | 30 +- guillotina/factory/security.py | 28 +- guillotina/factory/serialize.py | 14 +- guillotina/fields/annotation.py | 44 ++- guillotina/fields/dynamic.py | 20 +- guillotina/fields/files.py | 6 +- guillotina/fields/interfaces.py | 3 +- guillotina/fields/patch.py | 20 +- guillotina/files/__init__.py | 4 +- guillotina/files/adapter.py | 38 ++- guillotina/files/dbfile.py | 9 +- guillotina/files/field.py | 20 +- guillotina/files/manager.py | 55 ++-- guillotina/files/utils.py | 12 +- guillotina/glogging.py | 7 +- guillotina/gtypes.py | 8 +- guillotina/interfaces/__init__.py | 1 + guillotina/interfaces/async_util.py | 4 +- guillotina/interfaces/behaviors.py | 6 +- guillotina/interfaces/catalog.py | 8 +- guillotina/interfaces/content.py | 11 +- guillotina/interfaces/events.py | 4 +- guillotina/interfaces/files.py | 6 +- guillotina/interfaces/json.py | 4 +- guillotina/interfaces/mail.py | 7 +- guillotina/interfaces/misc.py | 9 +- guillotina/interfaces/registry.py | 4 +- guillotina/interfaces/response.py | 6 +- guillotina/interfaces/security.py | 18 +- guillotina/interfaces/types.py | 3 +- guillotina/json/definitions.py | 1 + guillotina/json/deserialize_content.py | 48 ++- guillotina/json/deserialize_value.py | 38 +-- guillotina/json/serialize_content.py | 39 ++- guillotina/json/serialize_schema.py | 18 +- guillotina/json/serialize_schema_field.py | 47 +-- guillotina/json/serialize_value.py | 7 +- guillotina/json/utils.py | 14 +- guillotina/metrics.py | 9 +- guillotina/middlewares/errors.py | 15 +- guillotina/permissions.py | 1 + guillotina/registry.py | 5 +- guillotina/renderers.py | 12 +- guillotina/request.py | 31 +- guillotina/response.py | 13 +- guillotina/routes.py | 3 +- guillotina/schema/__init__.py | 93 +++--- guillotina/schema/_bootstrapfields.py | 36 ++- guillotina/schema/_field.py | 153 +++++---- guillotina/schema/_messageid.py | 1 + guillotina/schema/_schema.py | 3 +- guillotina/schema/accessors.py | 4 +- guillotina/schema/exceptions.py | 4 +- guillotina/schema/fieldproperty.py | 8 +- guillotina/schema/interfaces.py | 16 +- guillotina/schema/tests/states.py | 5 +- .../schema/tests/test__bootstrapfields.py | 12 +- guillotina/schema/tests/test__field.py | 291 ++++++++++-------- guillotina/schema/tests/test_accessors.py | 22 +- guillotina/schema/tests/test_fieldproperty.py | 8 +- guillotina/schema/tests/test_interfaces.py | 21 +- guillotina/schema/tests/test_schema.py | 28 +- guillotina/schema/tests/test_states.py | 15 +- guillotina/schema/tests/test_vocabulary.py | 32 +- guillotina/schema/utils.py | 1 + guillotina/schema/vocabulary.py | 18 +- guillotina/security/__init__.py | 1 + guillotina/security/permission.py | 3 +- guillotina/security/policy.py | 49 +-- guillotina/security/security_code.py | 21 +- guillotina/security/security_local.py | 22 +- guillotina/security/utils.py | 31 +- guillotina/subscribers.py | 9 +- guillotina/task_vars.py | 15 +- guillotina/test_package.py | 66 ++-- guillotina/testing.py | 8 +- guillotina/tests/__init__.py | 1 + guillotina/tests/cache/test_cache_memory.py | 11 +- guillotina/tests/cache/test_cache_pubsub.py | 11 +- guillotina/tests/cache/test_cache_store.py | 6 +- guillotina/tests/cache/test_cache_txn.py | 6 +- guillotina/tests/cache/test_utils.py | 8 +- guillotina/tests/conftest.py | 1 + guillotina/tests/dbusers/test_api.py | 9 +- .../tests/dbusers/test_manage_groups.py | 9 +- guillotina/tests/dbusers/test_manage_users.py | 9 +- guillotina/tests/dbusers/test_registration.py | 9 +- .../tests/dbusers/test_reset_password.py | 11 +- guillotina/tests/dbusers/test_security.py | 9 +- guillotina/tests/dbusers/test_setup.py | 6 +- guillotina/tests/dyncontent/test_dynapi.py | 6 +- guillotina/tests/fixtures.py | 49 ++- guillotina/tests/image/__init__.py | 1 + guillotina/tests/image/test_field.py | 16 +- guillotina/tests/image/test_scale.py | 11 +- guillotina/tests/mailer/test_mailer.py | 3 +- guillotina/tests/mcp/test_mcp.py | 8 +- guillotina/tests/memcached/test_cache.py | 3 +- .../tests/memcached/test_memcached_driver.py | 12 +- guillotina/tests/memcached/test_metrics.py | 3 +- guillotina/tests/mocks.py | 14 +- guillotina/tests/pubsub/test_pubsub.py | 7 +- guillotina/tests/redis/test_driver.py | 6 +- .../tests/redis_session/test_session.py | 11 +- guillotina/tests/test_adapters.py | 50 ++- guillotina/tests/test_annotations.py | 10 +- guillotina/tests/test_api.py | 20 +- guillotina/tests/test_attachment.py | 17 +- guillotina/tests/test_auth.py | 9 +- guillotina/tests/test_blob.py | 9 +- guillotina/tests/test_catalog.py | 27 +- guillotina/tests/test_cockroach.py | 8 +- guillotina/tests/test_commands.py | 15 +- guillotina/tests/test_configure.py | 17 +- guillotina/tests/test_configure_component.py | 79 +++-- guillotina/tests/test_content.py | 35 +-- guillotina/tests/test_contentapi.py | 3 +- guillotina/tests/test_cors.py | 4 +- guillotina/tests/test_dbobjects.py | 8 +- guillotina/tests/test_dynamic_schema.py | 16 +- guillotina/tests/test_errors.py | 8 +- guillotina/tests/test_extrafieldattr.py | 1 + guillotina/tests/test_files.py | 7 +- guillotina/tests/test_guillo.py | 11 +- guillotina/tests/test_info.py | 3 +- guillotina/tests/test_jsonschema.py | 9 +- guillotina/tests/test_login.py | 8 +- guillotina/tests/test_metrics.py | 12 +- guillotina/tests/test_middlewares.py | 7 +- guillotina/tests/test_postgres.py | 18 +- guillotina/tests/test_queue.py | 9 +- guillotina/tests/test_renderers.py | 3 +- guillotina/tests/test_request.py | 8 +- guillotina/tests/test_routes.py | 4 +- guillotina/tests/test_security.py | 21 +- guillotina/tests/test_serialize.py | 33 +- guillotina/tests/test_server.py | 9 +- guillotina/tests/test_static.py | 3 +- guillotina/tests/test_storages.py | 8 +- guillotina/tests/test_swagger.py | 5 +- guillotina/tests/test_transactions.py | 14 +- guillotina/tests/test_traversal.py | 4 +- guillotina/tests/test_urls.py | 7 +- guillotina/tests/test_utils.py | 15 +- guillotina/tests/test_vocabulary.py | 4 +- guillotina/tests/test_ws.py | 6 +- guillotina/tests/utils.py | 16 +- .../tests/vocabularies/test_vocabularies.py | 1 + .../tests/workflows/test_workflow_basic.py | 1 + guillotina/transactions.py | 8 +- guillotina/traversal.py | 81 ++--- guillotina/utils/__init__.py | 3 +- guillotina/utils/auth.py | 6 +- guillotina/utils/content.py | 32 +- guillotina/utils/crypto.py | 8 +- guillotina/utils/execute.py | 14 +- guillotina/utils/misc.py | 37 +-- guillotina/utils/modules.py | 10 +- guillotina/utils/navigator.py | 7 +- 348 files changed, 2647 insertions(+), 2731 deletions(-) diff --git a/.isort.cfg b/.isort.cfg index e4aa14023..ba909f814 100644 --- a/.isort.cfg +++ b/.isort.cfg @@ -1,5 +1,5 @@ [settings] -force_single_line=True -sections=THIRDPARTY,FIRSTPARTY,LOCALFOLDER,STDLIB -no_lines_before=LOCALFOLDER,THIRDPARTY,FIRSTPARTY,STDLIB -force_alphabetical_sort=True +profile=black +lines_after_imports=2 +line_length=110 +skip_glob=*.pyi diff --git a/guillotina/__init__.py b/guillotina/__init__.py index 1845a5967..ac27d0392 100644 --- a/guillotina/__init__.py +++ b/guillotina/__init__.py @@ -1,4 +1,9 @@ # load the patch before anything else. +import os + +import pkg_resources +from zope.interface import Interface # noqa + from guillotina import glogging from guillotina._cache import BEHAVIOR_CACHE # noqa from guillotina._cache import FACTORY_CACHE # noqa @@ -6,10 +11,7 @@ from guillotina._cache import SCHEMA_CACHE # noqa from guillotina._settings import app_settings # noqa from guillotina.i18n import default_message_factory as _ # noqa -from zope.interface import Interface # noqa -import os -import pkg_resources __version__ = pkg_resources.get_distribution("guillotina").version @@ -20,9 +22,10 @@ if os.environ.get("GDEBUG", "").lower() in ("true", "t", "1"): # pragma: no cover # patches for extra debugging.... - import asyncpg import time + import asyncpg + original_execute = asyncpg.connection.Connection._do_execute logger.warning("RUNNING IN DEBUG MODE") diff --git a/guillotina/_settings.py b/guillotina/_settings.py index d63179bf3..5949d6297 100644 --- a/guillotina/_settings.py +++ b/guillotina/_settings.py @@ -1,11 +1,11 @@ -from guillotina import interfaces -from guillotina.db.uid import generate_uid -from typing import Any -from typing import Dict - import copy import pickle import string +from typing import Any, Dict + +from guillotina import interfaces +from guillotina.db.uid import generate_uid + app_settings: Dict[str, Any] = { "debug": False, diff --git a/guillotina/addons.py b/guillotina/addons.py index 7a81d439d..c651c5d0d 100644 --- a/guillotina/addons.py +++ b/guillotina/addons.py @@ -1,10 +1,9 @@ +from zope.interface import implementer + from guillotina import task_vars from guillotina._settings import app_settings -from guillotina.interfaces import IAddOn -from guillotina.interfaces import IAddons -from guillotina.utils import apply_coroutine -from guillotina.utils import get_current_request -from zope.interface import implementer +from guillotina.interfaces import IAddOn, IAddons +from guillotina.utils import apply_coroutine, get_current_request @implementer(IAddOn) diff --git a/guillotina/annotations.py b/guillotina/annotations.py index 5d9091e87..3753b628f 100644 --- a/guillotina/annotations.py +++ b/guillotina/annotations.py @@ -1,16 +1,15 @@ +import logging from collections import UserDict + +from zope.interface import implementer + from guillotina import configure from guillotina.db.interfaces import ITransaction from guillotina.db.orm.base import BaseObject from guillotina.exceptions import TransactionNotFound -from guillotina.interfaces import IAnnotationData -from guillotina.interfaces import IAnnotations -from guillotina.interfaces import IRegistry -from guillotina.interfaces import IResource +from guillotina.interfaces import IAnnotationData, IAnnotations, IRegistry, IResource from guillotina.transactions import get_transaction -from zope.interface import implementer -import logging logger = logging.getLogger("guillotina") _marker = object() diff --git a/guillotina/api/__init__.py b/guillotina/api/__init__.py index a3a990c70..749d3bb08 100644 --- a/guillotina/api/__init__.py +++ b/guillotina/api/__init__.py @@ -1,4 +1,6 @@ # these imports are done to force loading services +from guillotina.json import definitions # noqa + from . import addons # noqa from . import aggregation # noqa from . import app # noqa @@ -16,4 +18,3 @@ from . import user # noqa from . import vocabularies # noqa from . import ws # noqa -from guillotina.json import definitions # noqa diff --git a/guillotina/api/addons.py b/guillotina/api/addons.py index b2ad81631..871f5e419 100644 --- a/guillotina/api/addons.py +++ b/guillotina/api/addons.py @@ -1,13 +1,11 @@ -from guillotina import addons -from guillotina import configure -from guillotina import error_reasons +from guillotina import addons, configure, error_reasons from guillotina._settings import app_settings from guillotina.i18n import MessageFactory -from guillotina.interfaces import IAddons -from guillotina.interfaces import IContainer +from guillotina.interfaces import IAddons, IContainer from guillotina.response import ErrorResponse from guillotina.utils import get_registry + _ = MessageFactory("guillotina") diff --git a/guillotina/api/aggregation.py b/guillotina/api/aggregation.py index 21e4544b8..590d82fbe 100644 --- a/guillotina/api/aggregation.py +++ b/guillotina/api/aggregation.py @@ -1,8 +1,8 @@ from collections import Counter + from guillotina import configure from guillotina.component import query_utility -from guillotina.interfaces import ICatalogUtility -from guillotina.interfaces import IResource +from guillotina.interfaces import ICatalogUtility, IResource from guillotina.response import HTTPServiceUnavailable diff --git a/guillotina/api/app.py b/guillotina/api/app.py index af3cddd70..d05855aa8 100644 --- a/guillotina/api/app.py +++ b/guillotina/api/app.py @@ -1,9 +1,7 @@ -from guillotina import component -from guillotina import configure +from guillotina import component, configure from guillotina._settings import app_settings from guillotina.component import get_multi_adapter -from guillotina.interfaces import IApplication -from guillotina.interfaces import IResourceSerializeToJson +from guillotina.interfaces import IApplication, IResourceSerializeToJson from guillotina.utils import get_dotted_name diff --git a/guillotina/api/behaviors.py b/guillotina/api/behaviors.py index 15e25b80c..29da66631 100644 --- a/guillotina/api/behaviors.py +++ b/guillotina/api/behaviors.py @@ -1,11 +1,7 @@ from guillotina import configure -from guillotina.component import get_multi_adapter -from guillotina.component import get_utilities_for -from guillotina.component import query_adapter +from guillotina.component import get_multi_adapter, get_utilities_for, query_adapter from guillotina.content import get_cached_factory -from guillotina.interfaces import IBehavior -from guillotina.interfaces import IResource -from guillotina.interfaces import ISchemaSerializeToJson +from guillotina.interfaces import IBehavior, IResource, ISchemaSerializeToJson from guillotina.response import Response from guillotina.utils import resolve_dotted_name diff --git a/guillotina/api/container.py b/guillotina/api/container.py index d9a32675e..5009b0657 100644 --- a/guillotina/api/container.py +++ b/guillotina/api/container.py @@ -1,32 +1,25 @@ -from guillotina import addons -from guillotina import app_settings -from guillotina import configure -from guillotina import error_reasons -from guillotina import task_vars +import posixpath +from typing import Optional + +from guillotina import addons, app_settings, configure, error_reasons, task_vars from guillotina.api import content from guillotina.api.service import Service -from guillotina.component import get_adapter -from guillotina.component import get_multi_adapter +from guillotina.component import get_adapter, get_multi_adapter from guillotina.content import create_content from guillotina.event import notify from guillotina.events import ObjectAddedEvent -from guillotina.interfaces import IAnnotations -from guillotina.interfaces import IApplication -from guillotina.interfaces import IContainer -from guillotina.interfaces import IDatabase -from guillotina.interfaces import IPrincipalRoleManager -from guillotina.interfaces import IResourceSerializeToJson +from guillotina.interfaces import ( + IAnnotations, + IApplication, + IContainer, + IDatabase, + IPrincipalRoleManager, + IResourceSerializeToJson, +) from guillotina.interfaces.content import IGetOwner from guillotina.registry import REGISTRY_DATA_KEY -from guillotina.response import ErrorResponse -from guillotina.response import HTTPConflict -from guillotina.response import HTTPNotFound -from guillotina.response import HTTPNotImplemented -from guillotina.response import Response +from guillotina.response import ErrorResponse, HTTPConflict, HTTPNotFound, HTTPNotImplemented, Response from guillotina.utils import get_authenticated_user_id -from typing import Optional - -import posixpath @configure.service( diff --git a/guillotina/api/content.py b/guillotina/api/content.py index 9a23c2960..7dcc83396 100644 --- a/guillotina/api/content.py +++ b/guillotina/api/content.py @@ -1,68 +1,70 @@ -from guillotina import configure -from guillotina import content -from guillotina import error_reasons -from guillotina import security +from guillotina import configure, content, error_reasons, security from guillotina._cache import FACTORY_CACHE from guillotina._settings import app_settings from guillotina.api.service import Service -from guillotina.component import get_adapter -from guillotina.component import get_multi_adapter -from guillotina.component import query_adapter -from guillotina.component import query_multi_adapter -from guillotina.content import create_content_in_container -from guillotina.content import get_all_behavior_interfaces -from guillotina.content import get_all_behaviors -from guillotina.content import get_cached_factory -from guillotina.directives import merged_tagged_value_dict -from guillotina.directives import read_permission +from guillotina.component import get_adapter, get_multi_adapter, query_adapter, query_multi_adapter +from guillotina.content import ( + create_content_in_container, + get_all_behavior_interfaces, + get_all_behaviors, + get_cached_factory, +) +from guillotina.directives import merged_tagged_value_dict, read_permission from guillotina.event import notify -from guillotina.events import BeforeObjectModifiedEvent -from guillotina.events import BeforeObjectRemovedEvent -from guillotina.events import ObjectAddedEvent -from guillotina.events import ObjectModifiedEvent -from guillotina.events import ObjectPermissionsViewEvent -from guillotina.events import ObjectRemovedEvent -from guillotina.events import ObjectVisitedEvent -from guillotina.exceptions import ComponentLookupError -from guillotina.exceptions import PreconditionFailed +from guillotina.events import ( + BeforeObjectModifiedEvent, + BeforeObjectRemovedEvent, + ObjectAddedEvent, + ObjectModifiedEvent, + ObjectPermissionsViewEvent, + ObjectRemovedEvent, + ObjectVisitedEvent, +) +from guillotina.exceptions import ComponentLookupError, PreconditionFailed from guillotina.i18n import default_message_factory as _ -from guillotina.interfaces import IAnnotations -from guillotina.interfaces import IAsyncContainer -from guillotina.interfaces import IConstrainTypes -from guillotina.interfaces import IContainer -from guillotina.interfaces import IFieldValueRenderer -from guillotina.interfaces import IFolder -from guillotina.interfaces import IGetOwner -from guillotina.interfaces import IIDChecker -from guillotina.interfaces import IIDGenerator -from guillotina.interfaces import IPrincipalPermissionMap -from guillotina.interfaces import IPrincipalRoleManager -from guillotina.interfaces import IPrincipalRoleMap -from guillotina.interfaces import IResource -from guillotina.interfaces import IResourceDeserializeFromJson -from guillotina.interfaces import IResourceSerializeToJson -from guillotina.interfaces import IResourceSerializeToJsonSummary -from guillotina.interfaces import IResponse -from guillotina.interfaces import IRolePermissionMap +from guillotina.interfaces import ( + IAnnotations, + IAsyncContainer, + IConstrainTypes, + IContainer, + IFieldValueRenderer, + IFolder, + IGetOwner, + IIDChecker, + IIDGenerator, + IPrincipalPermissionMap, + IPrincipalRoleManager, + IPrincipalRoleMap, + IResource, + IResourceDeserializeFromJson, + IResourceSerializeToJson, + IResourceSerializeToJsonSummary, + IResponse, + IRolePermissionMap, +) from guillotina.json.utils import convert_interfaces_to_schema from guillotina.profile import profilable -from guillotina.response import ErrorResponse -from guillotina.response import HTTPMethodNotAllowed -from guillotina.response import HTTPMovedPermanently -from guillotina.response import HTTPNotFound -from guillotina.response import HTTPPreconditionFailed -from guillotina.response import HTTPUnauthorized -from guillotina.response import Response +from guillotina.response import ( + ErrorResponse, + HTTPMethodNotAllowed, + HTTPMovedPermanently, + HTTPNotFound, + HTTPPreconditionFailed, + HTTPUnauthorized, + Response, +) from guillotina.security.utils import apply_sharing from guillotina.transactions import get_transaction -from guillotina.utils import apply_coroutine -from guillotina.utils import get_authenticated_user_id -from guillotina.utils import get_behavior -from guillotina.utils import get_object_by_uid -from guillotina.utils import get_object_url -from guillotina.utils import get_security_policy -from guillotina.utils import iter_parents -from guillotina.utils import resolve_dotted_name +from guillotina.utils import ( + apply_coroutine, + get_authenticated_user_id, + get_behavior, + get_object_by_uid, + get_object_url, + get_security_policy, + iter_parents, + resolve_dotted_name, +) def get_content_json_schema_responses(content): diff --git a/guillotina/api/files.py b/guillotina/api/files.py index 80af7159f..327d89033 100644 --- a/guillotina/api/files.py +++ b/guillotina/api/files.py @@ -1,21 +1,15 @@ +import mimetypes + from guillotina import configure from guillotina._settings import app_settings from guillotina.api.content import DefaultOPTIONS -from guillotina.api.service import DownloadService -from guillotina.api.service import TraversableFieldService +from guillotina.api.service import DownloadService, TraversableFieldService from guillotina.component import get_multi_adapter from guillotina.event import notify from guillotina.events import ObjectModifiedEvent from guillotina.exceptions import FileNotFoundException -from guillotina.interfaces import IAsyncBehavior -from guillotina.interfaces import IFileManager -from guillotina.interfaces import IResource -from guillotina.interfaces import IStaticDirectory -from guillotina.interfaces import IStaticFile -from guillotina.response import HTTPNotFound -from guillotina.response import Response - -import mimetypes +from guillotina.interfaces import IAsyncBehavior, IFileManager, IResource, IStaticDirectory, IStaticFile +from guillotina.response import HTTPNotFound, Response def _traversed_file_doc(summary, parameters=None, responses=None): diff --git a/guillotina/api/login.py b/guillotina/api/login.py index f0dbebdef..7fbc1319e 100644 --- a/guillotina/api/login.py +++ b/guillotina/api/login.py @@ -1,29 +1,21 @@ # -*- encoding: utf-8 -*- -from datetime import datetime -from datetime import timedelta -from guillotina import app_settings -from guillotina import configure +import json +from datetime import datetime, timedelta +from json.decoder import JSONDecodeError + +import jwt + +from guillotina import app_settings, configure from guillotina.api.service import Service from guillotina.auth import authenticate_user from guillotina.auth.recaptcha import RecaptchaValidator from guillotina.auth.utils import find_user -from guillotina.component import get_utility -from guillotina.component import query_utility +from guillotina.component import get_utility, query_utility from guillotina.event import notify -from guillotina.events import UserLogin -from guillotina.events import UserRefreshToken -from guillotina.interfaces import IApplication -from guillotina.interfaces import IAuthValidationUtility -from guillotina.interfaces import IContainer -from guillotina.interfaces import ISessionManagerUtility -from guillotina.response import HTTPNotAcceptable -from guillotina.response import HTTPPreconditionFailed -from guillotina.response import HTTPUnauthorized +from guillotina.events import UserLogin, UserRefreshToken +from guillotina.interfaces import IApplication, IAuthValidationUtility, IContainer, ISessionManagerUtility +from guillotina.response import HTTPNotAcceptable, HTTPPreconditionFailed, HTTPUnauthorized from guillotina.utils import get_authenticated_user -from json.decoder import JSONDecodeError - -import json -import jwt @configure.service( diff --git a/guillotina/api/metadata.py b/guillotina/api/metadata.py index 7de693791..a723954b0 100644 --- a/guillotina/api/metadata.py +++ b/guillotina/api/metadata.py @@ -1,11 +1,8 @@ from guillotina import configure from guillotina._cache import FACTORY_CACHE from guillotina.component import get_utilities_for -from guillotina.directives import index -from guillotina.directives import merged_tagged_value_dict -from guillotina.interfaces import IAbsoluteURL -from guillotina.interfaces import IBehavior -from guillotina.interfaces import IContainer +from guillotina.directives import index, merged_tagged_value_dict +from guillotina.interfaces import IAbsoluteURL, IBehavior, IContainer @configure.service( diff --git a/guillotina/api/registry.py b/guillotina/api/registry.py index d728e6bf6..407ba5dcb 100644 --- a/guillotina/api/registry.py +++ b/guillotina/api/registry.py @@ -5,17 +5,13 @@ from guillotina.events import RegistryEditedEvent from guillotina.exceptions import ComponentLookupError from guillotina.i18n import MessageFactory -from guillotina.interfaces import IContainer -from guillotina.interfaces import IJSONToValue +from guillotina.interfaces import IContainer, IJSONToValue from guillotina.json.serialize_value import json_compatible -from guillotina.response import ErrorResponse -from guillotina.response import HTTPNotFound -from guillotina.response import Response +from guillotina.response import ErrorResponse, HTTPNotFound, Response from guillotina.schema import get_fields from guillotina.schema.exceptions import ValidationError -from guillotina.utils import get_registry -from guillotina.utils import import_class -from guillotina.utils import resolve_dotted_name +from guillotina.utils import get_registry, import_class, resolve_dotted_name + _ = MessageFactory("guillotina") diff --git a/guillotina/api/search.py b/guillotina/api/search.py index a5e8a7376..3748ed45e 100644 --- a/guillotina/api/search.py +++ b/guillotina/api/search.py @@ -1,12 +1,12 @@ +import logging + from guillotina import configure from guillotina.api.service import Service from guillotina.catalog.utils import reindex_in_future from guillotina.component import query_utility -from guillotina.interfaces import ICatalogUtility -from guillotina.interfaces import IResource +from guillotina.interfaces import ICatalogUtility, IResource from guillotina.response import HTTPServiceUnavailable -import logging logger = logging.getLogger("guillotina") diff --git a/guillotina/api/service.py b/guillotina/api/service.py index 13ac485b1..8d32042d4 100644 --- a/guillotina/api/service.py +++ b/guillotina/api/service.py @@ -1,3 +1,11 @@ +import json +from typing import Any +from typing import Dict as TDict +from typing import List as TList +from typing import Union + +import jsonschema + from guillotina import glogging from guillotina._cache import BEHAVIOR_CACHE from guillotina._settings import app_settings @@ -5,20 +13,11 @@ from guillotina.component import query_utility from guillotina.component.interfaces import IFactory from guillotina.fields import CloudFileField -from guillotina.interfaces import IAsyncBehavior -from guillotina.interfaces import ICloudFileField -from guillotina.response import HTTPNotFound -from guillotina.response import HTTPPreconditionFailed +from guillotina.interfaces import IAsyncBehavior, ICloudFileField +from guillotina.response import HTTPNotFound, HTTPPreconditionFailed from guillotina.schema.interfaces import IDict -from guillotina.utils import get_schema_validator -from guillotina.utils import JSONSchemaRefResolver -from typing import Any -from typing import Dict as TDict -from typing import List as TList -from typing import Union +from guillotina.utils import JSONSchemaRefResolver, get_schema_validator -import json -import jsonschema logger = glogging.getLogger("guillotina") diff --git a/guillotina/api/storage.py b/guillotina/api/storage.py index 035269ba7..6052fa4ee 100644 --- a/guillotina/api/storage.py +++ b/guillotina/api/storage.py @@ -1,3 +1,5 @@ +import re + from guillotina import configure from guillotina._settings import app_settings from guillotina.component import get_adapter @@ -6,8 +8,6 @@ from guillotina.response import HTTPNotFound from guillotina.utils import list_or_dict_items -import re - @configure.service(context=IApplication, method="GET", permission="guillotina.GetDatabases", name="@storages") async def storages_get(context, request): diff --git a/guillotina/api/types.py b/guillotina/api/types.py index 161b70b4c..d17c0f1a4 100644 --- a/guillotina/api/types.py +++ b/guillotina/api/types.py @@ -1,11 +1,7 @@ from guillotina import configure from guillotina.api.service import Service -from guillotina.component import get_multi_adapter -from guillotina.component import get_utilities_for -from guillotina.component import query_utility -from guillotina.interfaces import IContainer -from guillotina.interfaces import IFactorySerializeToJson -from guillotina.interfaces import IResourceFactory +from guillotina.component import get_multi_adapter, get_utilities_for, query_utility +from guillotina.interfaces import IContainer, IFactorySerializeToJson, IResourceFactory from guillotina.response import HTTPNotFound diff --git a/guillotina/api/user.py b/guillotina/api/user.py index 842b633e7..d19edfa52 100644 --- a/guillotina/api/user.py +++ b/guillotina/api/user.py @@ -1,8 +1,6 @@ from guillotina import configure from guillotina.component import get_utility -from guillotina.interfaces import IApplication -from guillotina.interfaces import IContainer -from guillotina.interfaces import IGroups +from guillotina.interfaces import IApplication, IContainer, IGroups from guillotina.utils.auth import get_authenticated_user diff --git a/guillotina/api/vocabularies.py b/guillotina/api/vocabularies.py index b5405e390..627abffb5 100644 --- a/guillotina/api/vocabularies.py +++ b/guillotina/api/vocabularies.py @@ -1,10 +1,9 @@ +from os.path import join + from guillotina import configure -from guillotina.interfaces import IAbsoluteURL -from guillotina.interfaces import IResource +from guillotina.interfaces import IAbsoluteURL, IResource from guillotina.response import HTTPNotFound -from guillotina.schema.vocabulary import getVocabularyRegistry -from guillotina.schema.vocabulary import VocabularyRegistryError -from os.path import join +from guillotina.schema.vocabulary import VocabularyRegistryError, getVocabularyRegistry @configure.service( diff --git a/guillotina/api/ws.py b/guillotina/api/ws.py index c8f4c8747..771385710 100644 --- a/guillotina/api/ws.py +++ b/guillotina/api/ws.py @@ -1,27 +1,20 @@ -from guillotina import configure -from guillotina import logger -from guillotina import routes -from guillotina import task_vars +import time +from urllib import parse + +import orjson +from jwcrypto import jwe +from jwcrypto.common import json_encode + +from guillotina import configure, logger, routes, task_vars from guillotina._settings import app_settings from guillotina.api.service import Service from guillotina.auth.extractors import BasicAuthPolicy -from guillotina.component import get_utility -from guillotina.component import query_multi_adapter -from guillotina.interfaces import IApplication -from guillotina.interfaces import IContainer -from guillotina.interfaces import IPermission -from guillotina.interfaces import IResponse +from guillotina.component import get_utility, query_multi_adapter +from guillotina.interfaces import IApplication, IContainer, IPermission, IResponse from guillotina.request import WebSocketJsonDecodeError from guillotina.security.utils import get_view_permission from guillotina.transactions import get_tm -from guillotina.utils import get_jwk_key -from guillotina.utils import get_security_policy -from jwcrypto import jwe -from jwcrypto.common import json_encode -from urllib import parse - -import orjson -import time +from guillotina.utils import get_jwk_key, get_security_policy @configure.service( diff --git a/guillotina/asgi.py b/guillotina/asgi.py index e9b14382d..88924cf85 100644 --- a/guillotina/asgi.py +++ b/guillotina/asgi.py @@ -1,23 +1,20 @@ -from guillotina import glogging -from guillotina import task_vars +import asyncio +import enum +import traceback +import uuid + +from guillotina import glogging, task_vars from guillotina.browser import View from guillotina.component import query_adapter from guillotina.exc_resp import HTTPConflict -from guillotina.exceptions import ConflictError -from guillotina.exceptions import TIDConflictError +from guillotina.exceptions import ConflictError, TIDConflictError from guillotina.interfaces import IErrorResponseException from guillotina.middlewares import ErrorsMiddleware from guillotina.request import Request from guillotina.response import Response -from guillotina.traversal import apply_cors -from guillotina.traversal import apply_rendering -from guillotina.utils import get_dotted_name -from guillotina.utils import resolve_dotted_name +from guillotina.traversal import apply_cors, apply_rendering +from guillotina.utils import get_dotted_name, resolve_dotted_name -import asyncio -import enum -import traceback -import uuid logger = glogging.getLogger("guillotina") diff --git a/guillotina/async_util.py b/guillotina/async_util.py index a1dc6878b..1bf6d7b4c 100644 --- a/guillotina/async_util.py +++ b/guillotina/async_util.py @@ -1,20 +1,14 @@ -from guillotina import logger -from guillotina import task_vars +import asyncio +import typing + +from guillotina import logger, task_vars from guillotina.db.transaction import Status -from guillotina.exceptions import ServerClosingException -from guillotina.exceptions import TransactionNotFound +from guillotina.exceptions import ServerClosingException, TransactionNotFound from guillotina.interfaces import IAsyncJobPool # noqa from guillotina.interfaces import IAsyncUtility # noqa from guillotina.interfaces import IQueueUtility # noqa -from guillotina.transactions import get_tm -from guillotina.transactions import get_transaction -from guillotina.transactions import transaction -from guillotina.utils import dump_task_vars -from guillotina.utils import execute -from guillotina.utils import load_task_vars - -import asyncio -import typing +from guillotina.transactions import get_tm, get_transaction, transaction +from guillotina.utils import dump_task_vars, execute, load_task_vars class QueueUtility(object): diff --git a/guillotina/auth/extractors.py b/guillotina/auth/extractors.py index e545de274..35880433c 100644 --- a/guillotina/auth/extractors.py +++ b/guillotina/auth/extractors.py @@ -1,11 +1,13 @@ -from guillotina.utils import get_jwk_key -from jwcrypto import jwe - import base64 import json import logging import time +from jwcrypto import jwe + +from guillotina.utils import get_jwk_key + + logger = logging.getLogger("guillotina") diff --git a/guillotina/auth/groups.py b/guillotina/auth/groups.py index e01707455..0fe3c972f 100644 --- a/guillotina/auth/groups.py +++ b/guillotina/auth/groups.py @@ -1,11 +1,9 @@ -from guillotina import app_settings -from guillotina import configure -from guillotina.auth.users import GuillotinaUser -from guillotina.interfaces import IGroups -from guillotina.interfaces import IPrincipal - import typing +from guillotina import app_settings, configure +from guillotina.auth.users import GuillotinaUser +from guillotina.interfaces import IGroups, IPrincipal + class GuillotinaGroup(GuillotinaUser): def __init__(self, ident): diff --git a/guillotina/auth/recaptcha.py b/guillotina/auth/recaptcha.py index d18a7b775..8b1f180ae 100644 --- a/guillotina/auth/recaptcha.py +++ b/guillotina/auth/recaptcha.py @@ -1,7 +1,8 @@ +import logging + from guillotina import app_settings from guillotina.utils import get_current_request -import logging logger = logging.getLogger("guillotina") diff --git a/guillotina/auth/role.py b/guillotina/auth/role.py index 1c877b91f..01911f877 100644 --- a/guillotina/auth/role.py +++ b/guillotina/auth/role.py @@ -1,7 +1,8 @@ +from zope.interface import implementer + from guillotina._settings import app_settings from guillotina.component import get_utilities_for from guillotina.interfaces import IRole -from zope.interface import implementer @implementer(IRole) diff --git a/guillotina/auth/users.py b/guillotina/auth/users.py index ef3519c1e..ad571a325 100644 --- a/guillotina/auth/users.py +++ b/guillotina/auth/users.py @@ -1,8 +1,10 @@ -from guillotina.interfaces import Allow -from guillotina.interfaces import IPrincipal from typing import Optional + from zope.interface import implementer +from guillotina.interfaces import Allow, IPrincipal + + ROOT_USER_ID = "root" ANONYMOUS_USER_ID = "Anonymous User" diff --git a/guillotina/auth/utils.py b/guillotina/auth/utils.py index 13f82a5d0..9f5da1135 100644 --- a/guillotina/auth/utils.py +++ b/guillotina/auth/utils.py @@ -1,17 +1,15 @@ -from datetime import datetime -from datetime import timedelta +from datetime import datetime, timedelta +from typing import Optional + +import jwt + from guillotina import task_vars from guillotina._settings import app_settings -from guillotina.auth.users import AnonymousUser -from guillotina.auth.users import ROOT_USER_ID +from guillotina.auth.users import ROOT_USER_ID, AnonymousUser from guillotina.component import get_utility -from guillotina.interfaces import IApplication -from guillotina.interfaces import IPrincipal +from guillotina.interfaces import IApplication, IPrincipal from guillotina.profile import profilable from guillotina.utils import get_security_policy -from typing import Optional - -import jwt @profilable diff --git a/guillotina/auth/validators.py b/guillotina/auth/validators.py index fef4131b9..508c4dec6 100644 --- a/guillotina/auth/validators.py +++ b/guillotina/auth/validators.py @@ -1,23 +1,21 @@ +import asyncio +import hashlib +import logging +import uuid from concurrent.futures import ThreadPoolExecutor from functools import partial + +import argon2 +import jwt +from lru import LRU + from guillotina import configure from guillotina._settings import app_settings from guillotina.auth import find_user -from guillotina.component import get_utility -from guillotina.component import query_utility -from guillotina.interfaces import IApplication -from guillotina.interfaces import IPasswordChecker -from guillotina.interfaces import IPasswordHasher -from guillotina.interfaces import ISessionManagerUtility +from guillotina.component import get_utility, query_utility +from guillotina.interfaces import IApplication, IPasswordChecker, IPasswordHasher, ISessionManagerUtility from guillotina.utils import strings_differ -from lru import LRU -import argon2 -import asyncio -import hashlib -import jwt -import logging -import uuid ph = argon2.PasswordHasher() _pw_auth_validator = LRU(100) diff --git a/guillotina/behaviors/__init__.py b/guillotina/behaviors/__init__.py index 2de1c0d15..02d3498fb 100644 --- a/guillotina/behaviors/__init__.py +++ b/guillotina/behaviors/__init__.py @@ -1,14 +1,13 @@ # so we can scan guillotina.behaviors and load behavior configuration +from zope.interface import alsoProvides, classImplements + +from guillotina.component import get_utilities_for, get_utility +from guillotina.interfaces import IBehavior, IResourceFactory +from guillotina.profile import profilable + from . import attachment # noqa from . import dublincore # noqa from . import dynamic # noqa -from guillotina.component import get_utilities_for -from guillotina.component import get_utility -from guillotina.interfaces import IBehavior -from guillotina.interfaces import IResourceFactory -from guillotina.profile import profilable -from zope.interface import alsoProvides -from zope.interface import classImplements def apply_concrete_behaviors(): diff --git a/guillotina/behaviors/attachment.py b/guillotina/behaviors/attachment.py index 25bff71db..b74086bb8 100644 --- a/guillotina/behaviors/attachment.py +++ b/guillotina/behaviors/attachment.py @@ -1,8 +1,8 @@ +from zope.interface import Interface + from guillotina import configure from guillotina.fields import CloudFileField -from guillotina.schema import Dict -from guillotina.schema import TextLine -from zope.interface import Interface +from guillotina.schema import Dict, TextLine class IAttachmentMarker(Interface): diff --git a/guillotina/behaviors/dublincore.py b/guillotina/behaviors/dublincore.py index 0a76d4fee..367c166e4 100644 --- a/guillotina/behaviors/dublincore.py +++ b/guillotina/behaviors/dublincore.py @@ -1,12 +1,14 @@ from datetime import datetime + from dateutil.tz import tzutc -from guillotina import configure -from guillotina import schema +from zope.interface import Interface + +from guillotina import configure, schema from guillotina.behaviors.instance import AnnotationBehavior from guillotina.behaviors.properties import ContextProperty from guillotina.directives import index_field from guillotina.fields.patch import PatchField -from zope.interface import Interface + _utc = tzutc() diff --git a/guillotina/behaviors/dynamic.py b/guillotina/behaviors/dynamic.py index 40f509142..21b20ee84 100644 --- a/guillotina/behaviors/dynamic.py +++ b/guillotina/behaviors/dynamic.py @@ -1,11 +1,9 @@ -from guillotina import configure -from guillotina import fields -from guillotina import schema -from guillotina.behaviors.instance import AnnotationBehavior -from guillotina.behaviors.instance import ContextBehavior -from guillotina.interfaces import IContentBehavior from zope.interface import Interface +from guillotina import configure, fields, schema +from guillotina.behaviors.instance import AnnotationBehavior, ContextBehavior +from guillotina.interfaces import IContentBehavior + def get_all_fields(content): _fields = {} diff --git a/guillotina/behaviors/instance.py b/guillotina/behaviors/instance.py index 052727ccf..1e04b279a 100644 --- a/guillotina/behaviors/instance.py +++ b/guillotina/behaviors/instance.py @@ -1,12 +1,12 @@ -from guillotina.annotations import AnnotationData -from guillotina.interfaces import IAnnotationData -from guillotina.interfaces import IAnnotations -from guillotina.interfaces import IAsyncBehavior -from guillotina.interfaces import IContentBehavior -from guillotina.schema.utils import get_default_from_schema from typing import Tuple + from zope.interface import implementer +from guillotina.annotations import AnnotationData +from guillotina.interfaces import IAnnotationData, IAnnotations, IAsyncBehavior, IContentBehavior +from guillotina.schema.utils import get_default_from_schema + + _default = object() diff --git a/guillotina/behaviors/properties.py b/guillotina/behaviors/properties.py index 9b274290a..883ec3154 100644 --- a/guillotina/behaviors/properties.py +++ b/guillotina/behaviors/properties.py @@ -1,5 +1,6 @@ from guillotina.schema.utils import get_default_from_schema + _EMPTY = object() diff --git a/guillotina/blob.py b/guillotina/blob.py index 4b51d1b3d..95a7652da 100644 --- a/guillotina/blob.py +++ b/guillotina/blob.py @@ -1,9 +1,9 @@ +from io import BytesIO +from typing import AsyncIterator, Union + from guillotina._settings import app_settings from guillotina.exceptions import BlobChunkNotFound from guillotina.transactions import get_transaction -from io import BytesIO -from typing import AsyncIterator -from typing import Union class Blob: diff --git a/guillotina/browser.py b/guillotina/browser.py index 30ed9d797..785be6b85 100644 --- a/guillotina/browser.py +++ b/guillotina/browser.py @@ -1,15 +1,10 @@ -from guillotina import configure -from guillotina import task_vars -from guillotina.component import adapter -from guillotina.interfaces import IAbsoluteURL -from guillotina.interfaces import ILocation -from guillotina.interfaces import IRequest -from guillotina.interfaces import IResource -from guillotina.interfaces import IView -from guillotina.utils import get_current_request -from guillotina.utils import get_url from zope.interface import implementer +from guillotina import configure, task_vars +from guillotina.component import adapter +from guillotina.interfaces import IAbsoluteURL, ILocation, IRequest, IResource, IView +from guillotina.utils import get_current_request, get_url + def get_physical_path(context): parts = [context.__name__] diff --git a/guillotina/catalog/__init__.py b/guillotina/catalog/__init__.py index f35b18b94..a3d98a47c 100644 --- a/guillotina/catalog/__init__.py +++ b/guillotina/catalog/__init__.py @@ -2,10 +2,9 @@ from guillotina import directives from guillotina.interfaces import IResource from guillotina.security.security_code import role_permission_manager -from guillotina.security.utils import get_principals_with_access_content -from guillotina.security.utils import get_roles_with_access_content -from guillotina.utils import get_content_depth -from guillotina.utils import get_content_path +from guillotina.security.utils import get_principals_with_access_content, get_roles_with_access_content +from guillotina.utils import get_content_depth, get_content_path + global_roles_for_permission = role_permission_manager.get_roles_for_permission diff --git a/guillotina/catalog/catalog.py b/guillotina/catalog/catalog.py index b7abc5560..f97d3175d 100644 --- a/guillotina/catalog/catalog.py +++ b/guillotina/catalog/catalog.py @@ -1,31 +1,28 @@ +import logging +import typing + +from zope.interface import implementer + from guillotina import configure from guillotina.catalog.utils import parse_query from guillotina.component import query_adapter from guillotina.content import iter_schemata from guillotina.db.orm.interfaces import IBaseObject -from guillotina.directives import index -from guillotina.directives import merged_tagged_value_dict -from guillotina.directives import merged_tagged_value_list -from guillotina.directives import metadata +from guillotina.directives import index, merged_tagged_value_dict, merged_tagged_value_list, metadata from guillotina.exceptions import NoIndexField -from guillotina.interfaces import IAsyncBehavior -from guillotina.interfaces import ICatalogDataAdapter -from guillotina.interfaces import ICatalogUtility -from guillotina.interfaces import IContainer -from guillotina.interfaces import IResource -from guillotina.interfaces import ISecurityInfo +from guillotina.interfaces import ( + IAsyncBehavior, + ICatalogDataAdapter, + ICatalogUtility, + IContainer, + IResource, + ISecurityInfo, +) from guillotina.json.serialize_value import json_compatible -from guillotina.security.security_code import principal_permission_manager -from guillotina.security.security_code import role_permission_manager -from guillotina.security.utils import get_principals_with_access_content -from guillotina.security.utils import get_roles_with_access_content -from guillotina.utils import apply_coroutine -from guillotina.utils import get_content_depth -from guillotina.utils import get_content_path -from zope.interface import implementer +from guillotina.security.security_code import principal_permission_manager, role_permission_manager +from guillotina.security.utils import get_principals_with_access_content, get_roles_with_access_content +from guillotina.utils import apply_coroutine, get_content_depth, get_content_path -import logging -import typing logger = logging.getLogger("guillotina") diff --git a/guillotina/catalog/index.py b/guillotina/catalog/index.py index f21e0bdd8..f0597caf6 100644 --- a/guillotina/catalog/index.py +++ b/guillotina/catalog/index.py @@ -1,21 +1,20 @@ from guillotina import configure from guillotina._settings import app_settings from guillotina.catalog.utils import reindex_in_future -from guillotina.component import query_adapter -from guillotina.component import query_utility -from guillotina.interfaces import ICatalogUtility -from guillotina.interfaces import IContainer -from guillotina.interfaces import IGroupFolder -from guillotina.interfaces import IObjectAddedEvent -from guillotina.interfaces import IObjectModifiedEvent -from guillotina.interfaces import IObjectMovedEvent -from guillotina.interfaces import IObjectPermissionsModifiedEvent -from guillotina.interfaces import IObjectRemovedEvent -from guillotina.interfaces import IResource -from guillotina.interfaces import ISecurityInfo -from guillotina.utils import apply_coroutine -from guillotina.utils import execute -from guillotina.utils import find_container +from guillotina.component import query_adapter, query_utility +from guillotina.interfaces import ( + ICatalogUtility, + IContainer, + IGroupFolder, + IObjectAddedEvent, + IObjectModifiedEvent, + IObjectMovedEvent, + IObjectPermissionsModifiedEvent, + IObjectRemovedEvent, + IResource, + ISecurityInfo, +) +from guillotina.utils import apply_coroutine, execute, find_container class Indexer: diff --git a/guillotina/catalog/parser.py b/guillotina/catalog/parser.py index 6462c72ca..a348435b7 100644 --- a/guillotina/catalog/parser.py +++ b/guillotina/catalog/parser.py @@ -1,10 +1,9 @@ +import typing + from guillotina import app_settings from guillotina.catalog.types import BasicParsedQueryInfo from guillotina.catalog.utils import iter_indexes -from guillotina.utils import get_content_depth -from guillotina.utils import get_content_path - -import typing +from guillotina.utils import get_content_depth, get_content_path def to_list(value: typing.Union[str, list]) -> typing.List[str]: diff --git a/guillotina/catalog/types.py b/guillotina/catalog/types.py index a2ffad62e..9a924beb8 100644 --- a/guillotina/catalog/types.py +++ b/guillotina/catalog/types.py @@ -1,7 +1,7 @@ -from mypy_extensions import TypedDict - import typing +from mypy_extensions import TypedDict + class BasicParsedQueryInfo(TypedDict): sort_on: typing.Optional[str] diff --git a/guillotina/catalog/utils.py b/guillotina/catalog/utils.py index 5198e76f0..31e6e35f1 100644 --- a/guillotina/catalog/utils.py +++ b/guillotina/catalog/utils.py @@ -1,22 +1,15 @@ +import logging +import typing + from guillotina import app_settings from guillotina.catalog.types import BasicParsedQueryInfo -from guillotina.component import get_utilities_for -from guillotina.component import get_utility -from guillotina.component import query_multi_adapter -from guillotina.component import query_utility -from guillotina.content import get_all_possible_schemas_for_type -from guillotina.content import IResourceFactory -from guillotina.directives import index_field -from guillotina.directives import merged_tagged_value_dict -from guillotina.directives import merged_tagged_value_list -from guillotina.directives import metadata -from guillotina.interfaces import ICatalogUtility -from guillotina.interfaces import ISearchParser +from guillotina.component import get_utilities_for, get_utility, query_multi_adapter, query_utility +from guillotina.content import IResourceFactory, get_all_possible_schemas_for_type +from guillotina.directives import index_field, merged_tagged_value_dict, merged_tagged_value_list, metadata +from guillotina.interfaces import ICatalogUtility, ISearchParser from guillotina.transactions import transaction from guillotina.utils import execute -import logging -import typing logger = logging.getLogger("guillotina") diff --git a/guillotina/commands/__init__.py b/guillotina/commands/__init__.py index 96c6b22f5..313860327 100644 --- a/guillotina/commands/__init__.py +++ b/guillotina/commands/__init__.py @@ -1,19 +1,19 @@ -from fnmatch import fnmatch -from guillotina import logger -from guillotina import profile -from guillotina._settings import app_settings -from guillotina.factory import make_app -from guillotina.utils import get_dotted_name -from guillotina.utils import resolve_dotted_name - import argparse import asyncio import cProfile import json import os import sys +from fnmatch import fnmatch + import yaml +from guillotina import logger, profile +from guillotina._settings import app_settings +from guillotina.factory import make_app +from guillotina.utils import get_dotted_name, resolve_dotted_name + + try: import uvloop # type: ignore diff --git a/guillotina/commands/create.py b/guillotina/commands/create.py index 60cffe14a..180778370 100644 --- a/guillotina/commands/create.py +++ b/guillotina/commands/create.py @@ -1,9 +1,9 @@ -from guillotina.commands import Command - -import guillotina import os import sys +import guillotina +from guillotina.commands import Command + class CreateCommand(Command): description = "Guillotina server runner" diff --git a/guillotina/commands/crypto.py b/guillotina/commands/crypto.py index b82b63979..2f77e71f8 100644 --- a/guillotina/commands/crypto.py +++ b/guillotina/commands/crypto.py @@ -1,7 +1,9 @@ -from guillotina.commands import Command +import logging + from jwcrypto import jwk -import logging +from guillotina.commands import Command + logger = logging.getLogger("guillotina") diff --git a/guillotina/commands/initialize_db.py b/guillotina/commands/initialize_db.py index b7080fa91..58e8dc761 100644 --- a/guillotina/commands/initialize_db.py +++ b/guillotina/commands/initialize_db.py @@ -1,7 +1,6 @@ from guillotina.commands import Command from guillotina.component import get_utility -from guillotina.interfaces import IApplication -from guillotina.interfaces import IDatabase +from guillotina.interfaces import IApplication, IDatabase class DatabaseInitializationCommand(Command): diff --git a/guillotina/commands/migrate.py b/guillotina/commands/migrate.py index b84576063..0d0285347 100644 --- a/guillotina/commands/migrate.py +++ b/guillotina/commands/migrate.py @@ -1,11 +1,12 @@ +import logging from distutils.version import StrictVersion + from guillotina.commands import Command from guillotina.component import get_utilities_for from guillotina.interfaces import IMigration from guillotina.transactions import transaction from guillotina.utils import iter_databases -import logging logger = logging.getLogger("guillotina") diff --git a/guillotina/commands/run.py b/guillotina/commands/run.py index d53de91e8..7976abdc0 100644 --- a/guillotina/commands/run.py +++ b/guillotina/commands/run.py @@ -1,13 +1,12 @@ -from guillotina.commands import Command -from guillotina.utils import get_containers -from guillotina.utils import iter_databases -from guillotina.utils import lazy_apply - import importlib.util import inspect import logging import os +from guillotina.commands import Command +from guillotina.utils import get_containers, iter_databases, lazy_apply + + logger = logging.getLogger("guillotina") diff --git a/guillotina/commands/serve_reload.py b/guillotina/commands/serve_reload.py index fa5760e28..cc32428e3 100644 --- a/guillotina/commands/serve_reload.py +++ b/guillotina/commands/serve_reload.py @@ -1,11 +1,11 @@ -from guillotina.asgi import AsgiApp -from guillotina.commands import Command -from guillotina.traversal import TraversalRouter - import os import subprocess import sys +from guillotina.asgi import AsgiApp +from guillotina.commands import Command +from guillotina.traversal import TraversalRouter + def create_app(): config_file = os.getenv("GUILLOTINA_CONFIG_FILE", "config.yaml") diff --git a/guillotina/commands/shell.py b/guillotina/commands/shell.py index 9d91adf8d..1bc27f5a6 100644 --- a/guillotina/commands/shell.py +++ b/guillotina/commands/shell.py @@ -1,14 +1,12 @@ +import asyncio # noqa +import sys + from guillotina import app_settings # noqa -from guillotina import task_vars -from guillotina import utils +from guillotina import task_vars, utils from guillotina.commands import Command from guillotina.component import get_utility from guillotina.interfaces import IApplication -from guillotina.tests.utils import get_mocked_request -from guillotina.tests.utils import login - -import asyncio # noqa -import sys +from guillotina.tests.utils import get_mocked_request, login class ShellHelpers: diff --git a/guillotina/commands/testdata.py b/guillotina/commands/testdata.py index a19acfd86..8594e037c 100644 --- a/guillotina/commands/testdata.py +++ b/guillotina/commands/testdata.py @@ -1,20 +1,18 @@ +import asyncio + +import aiohttp + from guillotina import task_vars from guillotina.behaviors.dublincore import IDublinCore from guillotina.commands import Command from guillotina.component import get_utility -from guillotina.content import create_content -from guillotina.content import create_content_in_container +from guillotina.content import create_content, create_content_in_container from guillotina.event import notify from guillotina.events import ObjectAddedEvent from guillotina.exceptions import ConflictIdOnContainer -from guillotina.interfaces import IApplication -from guillotina.interfaces import IDatabase -from guillotina.interfaces import IPrincipalRoleManager +from guillotina.interfaces import IApplication, IDatabase, IPrincipalRoleManager from guillotina.tests.utils import login -import aiohttp -import asyncio - class AsyncUrlRetriever: """ diff --git a/guillotina/commands/vacuum.py b/guillotina/commands/vacuum.py index 80dd52a1a..9c4c9eec3 100644 --- a/guillotina/commands/vacuum.py +++ b/guillotina/commands/vacuum.py @@ -1,9 +1,10 @@ +import logging + from guillotina.commands import Command from guillotina.component import query_adapter from guillotina.db.interfaces import IVacuumProvider from guillotina.utils import iter_databases -import logging logger = logging.getLogger("guillotina") diff --git a/guillotina/component/__init__.py b/guillotina/component/__init__.py index 0cc99c5e0..79dcc9048 100644 --- a/guillotina/component/__init__.py +++ b/guillotina/component/__init__.py @@ -12,35 +12,41 @@ # ############################################################################## # flake8: noqa -from guillotina.component._api import get_adapter -from guillotina.component._api import get_adapters -from guillotina.component._api import get_all_utilities_registered_for -from guillotina.component._api import get_component_registry -from guillotina.component._api import get_factories_for -from guillotina.component._api import get_factory_interfaces -from guillotina.component._api import get_multi_adapter -from guillotina.component._api import get_utilities_for -from guillotina.component._api import get_utility -from guillotina.component._api import handle -from guillotina.component._api import query_adapter -from guillotina.component._api import query_multi_adapter -from guillotina.component._api import query_utility -from guillotina.component._api import subscribers -from guillotina.component._declaration import adaptedBy -from guillotina.component._declaration import adapter -from guillotina.component._declaration import adapts -from guillotina.component.globalregistry import get_global_components -from guillotina.component.globalregistry import provide_adapter -from guillotina.component.globalregistry import provide_handler -from guillotina.component.globalregistry import provide_subscription_adapter -from guillotina.component.globalregistry import provide_utility -from guillotina.component.interfaces import ComponentLookupError -from guillotina.component.interfaces import IComponentArchitecture -from guillotina.component.interfaces import IComponentLookup -from guillotina.component.interfaces import IComponentRegistrationConvenience -from guillotina.component.interfaces import IFactory from zope.interface import moduleProvides +from guillotina.component._api import ( + get_adapter, + get_adapters, + get_all_utilities_registered_for, + get_component_registry, + get_factories_for, + get_factory_interfaces, + get_multi_adapter, + get_utilities_for, + get_utility, + handle, + query_adapter, + query_multi_adapter, + query_utility, + subscribers, +) +from guillotina.component._declaration import adaptedBy, adapter, adapts +from guillotina.component.globalregistry import ( + get_global_components, + provide_adapter, + provide_handler, + provide_subscription_adapter, + provide_utility, +) +from guillotina.component.interfaces import ( + ComponentLookupError, + IComponentArchitecture, + IComponentLookup, + IComponentRegistrationConvenience, + IFactory, +) + + # b/w compat imports. Will be removed in 3.0 getMultiAdapter = get_multi_adapter queryMultiAdapter = query_multi_adapter diff --git a/guillotina/component/_api.py b/guillotina/component/_api.py index 05aab9970..84165a458 100644 --- a/guillotina/component/_api.py +++ b/guillotina/component/_api.py @@ -11,21 +11,17 @@ # FOR A PARTICULAR PURPOSE. # ############################################################################## +from typing import Any, Dict, List, Optional + +import zope.interface.interface +from zope.interface import Interface, providedBy + from guillotina.component import globalregistry from guillotina.component._compat import _BLANK from guillotina.component._declaration import adapter # noqa from guillotina.component.hookable import hookable -from guillotina.component.interfaces import ComponentLookupError -from guillotina.component.interfaces import IComponentLookup -from guillotina.component.interfaces import IFactory -from typing import Any -from typing import Dict -from typing import List -from typing import Optional -from zope.interface import Interface -from zope.interface import providedBy +from guillotina.component.interfaces import ComponentLookupError, IComponentLookup, IFactory -import zope.interface.interface _MISSING = object() diff --git a/guillotina/component/_compat.py b/guillotina/component/_compat.py index 4e4587451..dab81ece7 100644 --- a/guillotina/component/_compat.py +++ b/guillotina/component/_compat.py @@ -17,6 +17,7 @@ import sys import types + CLASS_TYPES = (type,) PYTHON3 = True diff --git a/guillotina/component/_declaration.py b/guillotina/component/_declaration.py index 3e5d26e2c..d59fe1db9 100644 --- a/guillotina/component/_declaration.py +++ b/guillotina/component/_declaration.py @@ -12,11 +12,10 @@ # ############################################################################## # flake8: noqa -from guillotina.component._compat import _BLANK -from guillotina.component._compat import CLASS_TYPES - import sys +from guillotina.component._compat import _BLANK, CLASS_TYPES + class adapter(object): def __init__(self, *interfaces): diff --git a/guillotina/component/event.py b/guillotina/component/event.py index 7839c7dff..3bea23064 100644 --- a/guillotina/component/event.py +++ b/guillotina/component/event.py @@ -16,6 +16,7 @@ from guillotina.component._api import subscribers as component_subscribers from guillotina.component.interfaces import ComponentLookupError + async_subscribers = [] sync_subscribers = [] diff --git a/guillotina/component/factory.py b/guillotina/component/factory.py index bccdc0357..c9c287d6f 100644 --- a/guillotina/component/factory.py +++ b/guillotina/component/factory.py @@ -12,11 +12,11 @@ # ############################################################################## # flake8: noqa -from guillotina.component.interfaces import IFactory -from zope.interface import implementedBy -from zope.interface import implementer +from zope.interface import implementedBy, implementer from zope.interface.declarations import Implements +from guillotina.component.interfaces import IFactory + @implementer(IFactory) class Factory(object): diff --git a/guillotina/component/globalregistry.py b/guillotina/component/globalregistry.py index c58f603b5..9d28d8cc5 100644 --- a/guillotina/component/globalregistry.py +++ b/guillotina/component/globalregistry.py @@ -11,20 +11,20 @@ # FOR A PARTICULAR PURPOSE. # ############################################################################## -from guillotina.component._compat import _BLANK -from guillotina.component.interfaces import IComponentLookup -from guillotina.profile import profilable -from typing import Type -from zope.interface import implementer -from zope.interface import providedBy -from zope.interface.adapter import AdapterLookup -from zope.interface.adapter import AdapterRegistry -from zope.interface.registry import Components - import asyncio import logging import os import time +from typing import Type + +from zope.interface import implementer, providedBy +from zope.interface.adapter import AdapterLookup, AdapterRegistry +from zope.interface.registry import Components + +from guillotina.component._compat import _BLANK +from guillotina.component.interfaces import IComponentLookup +from guillotina.profile import profilable + profile_logger = logging.getLogger("guillotina.profile") @@ -56,9 +56,7 @@ class DebugGuillotinaAdapterLookup(GuillotinaAdapterLookup): # pragma: no cover async def asubscribers(self, objects, provided): from guillotina import task_vars from guillotina.exceptions import RequestNotFound - from guillotina.utils import get_authenticated_user_id - from guillotina.utils import get_current_request - from guillotina.utils import get_dotted_name + from guillotina.utils import get_authenticated_user_id, get_current_request, get_dotted_name if len(objects) > 1: event = get_dotted_name(objects[1]) diff --git a/guillotina/component/interface.py b/guillotina/component/interface.py index f1545efef..e55cd9f7d 100644 --- a/guillotina/component/interface.py +++ b/guillotina/component/interface.py @@ -1,7 +1,8 @@ -from guillotina.component._api import get_component_registry from zope.interface import alsoProvides from zope.interface.interfaces import IInterface +from guillotina.component._api import get_component_registry + def provide_interface(id, interface, iface_type=None, info=""): """Mark 'interface' as a named utilty providing 'iface_type'.""" diff --git a/guillotina/component/interfaces.py b/guillotina/component/interfaces.py index bcdb4aaf8..93abaceb1 100644 --- a/guillotina/component/interfaces.py +++ b/guillotina/component/interfaces.py @@ -13,30 +13,34 @@ ############################################################################ # flake8: noqa +from zope.interface import Attribute, Interface + +# BBB 2011-09-09, import interfaces from zope.interface +from zope.interface.interfaces import ( + ComponentLookupError, + IAdapterRegistration, + IComponentLookup, + IComponentRegistry, + IComponents, + IHandlerRegistration, + Invalid, + IObjectEvent, + IRegistered, + IRegistration, + IRegistrationEvent, + ISubscriptionAdapterRegistration, + IUnregistered, + IUtilityRegistration, + ObjectEvent, + Registered, + RegistrationEvent, + Unregistered, + _IBaseAdapterRegistration, +) + # fmt: off from guillotina.component._compat import _BLANK -from zope.interface import Attribute -from zope.interface import Interface -# BBB 2011-09-09, import interfaces from zope.interface -from zope.interface.interfaces import _IBaseAdapterRegistration -from zope.interface.interfaces import ComponentLookupError -from zope.interface.interfaces import IAdapterRegistration -from zope.interface.interfaces import IComponentLookup -from zope.interface.interfaces import IComponentRegistry -from zope.interface.interfaces import IComponents -from zope.interface.interfaces import IHandlerRegistration -from zope.interface.interfaces import Invalid -from zope.interface.interfaces import IObjectEvent -from zope.interface.interfaces import IRegistered -from zope.interface.interfaces import IRegistration -from zope.interface.interfaces import IRegistrationEvent -from zope.interface.interfaces import ISubscriptionAdapterRegistration -from zope.interface.interfaces import IUnregistered -from zope.interface.interfaces import IUtilityRegistration -from zope.interface.interfaces import ObjectEvent -from zope.interface.interfaces import Registered -from zope.interface.interfaces import RegistrationEvent -from zope.interface.interfaces import Unregistered + # fmt: on diff --git a/guillotina/component/registry.py b/guillotina/component/registry.py index a024d1211..d9b48c10a 100644 --- a/guillotina/component/registry.py +++ b/guillotina/component/registry.py @@ -13,11 +13,13 @@ ############################################################################## from guillotina.component._api import handle from guillotina.component._declaration import adapter -from guillotina.component.interfaces import IAdapterRegistration -from guillotina.component.interfaces import IHandlerRegistration -from guillotina.component.interfaces import IRegistrationEvent -from guillotina.component.interfaces import ISubscriptionAdapterRegistration -from guillotina.component.interfaces import IUtilityRegistration +from guillotina.component.interfaces import ( + IAdapterRegistration, + IHandlerRegistration, + IRegistrationEvent, + ISubscriptionAdapterRegistration, + IUtilityRegistration, +) @adapter(IUtilityRegistration, IRegistrationEvent) diff --git a/guillotina/component/testfiles/adapter.py b/guillotina/component/testfiles/adapter.py index 1b0663923..acb66f5c7 100644 --- a/guillotina/component/testfiles/adapter.py +++ b/guillotina/component/testfiles/adapter.py @@ -13,10 +13,10 @@ ############################################################################## # flake8: noqa +from zope.interface import Interface, implementer + from guillotina.component import adapter from guillotina.component.testfiles import components -from zope.interface import implementer -from zope.interface import Interface class I1(Interface): diff --git a/guillotina/component/testfiles/components.py b/guillotina/component/testfiles/components.py index 2bcb94d07..6378a2859 100644 --- a/guillotina/component/testfiles/components.py +++ b/guillotina/component/testfiles/components.py @@ -12,11 +12,9 @@ # ############################################################################## # flake8: noqa +from zope.interface import Attribute, Interface, implementer, named + from guillotina.component import adapter -from zope.interface import Attribute -from zope.interface import implementer -from zope.interface import Interface -from zope.interface import named class IAppb(Interface): diff --git a/guillotina/component/testfiles/views.py b/guillotina/component/testfiles/views.py index b93f26b0b..cf75a322d 100644 --- a/guillotina/component/testfiles/views.py +++ b/guillotina/component/testfiles/views.py @@ -13,9 +13,7 @@ ############################################################################## # flake8: noqa -from zope.interface import directlyProvides -from zope.interface import implementer -from zope.interface import Interface +from zope.interface import Interface, directlyProvides, implementer class Request(object): diff --git a/guillotina/component/tests/examples.py b/guillotina/component/tests/examples.py index 87458ef15..5b804d45c 100644 --- a/guillotina/component/tests/examples.py +++ b/guillotina/component/tests/examples.py @@ -12,14 +12,14 @@ # ############################################################################## # flake8: noqa +import sys + +from zope.interface import Interface, implementer +from zope.interface.interfaces import IInterface + from guillotina.component._declaration import adapter from guillotina.component.globalregistry import GuillotinaAdapterRegistry from guillotina.component.testfiles.views import IC -from zope.interface import implementer -from zope.interface import Interface -from zope.interface.interfaces import IInterface - -import sys def write(x): diff --git a/guillotina/component/tests/test___init__.py b/guillotina/component/tests/test___init__.py index 72cd81819..499f8863d 100644 --- a/guillotina/component/tests/test___init__.py +++ b/guillotina/component/tests/test___init__.py @@ -17,26 +17,25 @@ class Test_package(unittest.TestCase): def test_module_conforms_to_IComponentArchitecture(self): - from guillotina.component.interfaces import IComponentArchitecture from zope.interface.verify import verifyObject import guillotina.component as zc + from guillotina.component.interfaces import IComponentArchitecture verifyObject(IComponentArchitecture, zc) def test_module_conforms_to_IComponentRegistrationConvenience(self): - from guillotina.component.interfaces import IComponentRegistrationConvenience from zope.interface.verify import verifyObject import guillotina.component as zc + from guillotina.component.interfaces import IComponentRegistrationConvenience verifyObject(IComponentRegistrationConvenience, zc) class Test_Interface_call(unittest.TestCase): - from guillotina.component.testing import setUp - from guillotina.component.testing import tearDown + from guillotina.component.testing import setUp, tearDown def test_miss(self): from zope.interface import Interface @@ -56,9 +55,9 @@ class IFoo(Interface): self.assertTrue(IFoo(object(), marker) is marker) def test_hit(self): + from zope.interface import Interface, implementer + from guillotina.component import get_global_components - from zope.interface import implementer - from zope.interface import Interface class IFoo(Interface): pass @@ -82,9 +81,9 @@ def __init__(self, context): self.assertTrue(adapted.context is bar) def test_hit_registered_for_None(self): + from zope.interface import Interface, implementer + from guillotina.component import get_global_components - from zope.interface import implementer - from zope.interface import Interface class IFoo(Interface): pass diff --git a/guillotina/component/tests/test__api.py b/guillotina/component/tests/test__api.py index 13dd39a79..2957b5c86 100644 --- a/guillotina/component/tests/test__api.py +++ b/guillotina/component/tests/test__api.py @@ -17,8 +17,7 @@ class Test_get_component_registry(unittest.TestCase): - from guillotina.component.testing import setUp - from guillotina.component.testing import tearDown + from guillotina.component.testing import setUp, tearDown def _callFUT(self, *args, **kw): from guillotina.component._api import get_component_registry @@ -54,9 +53,10 @@ def test_get_component_registry_w_invalid_context_no_adapter(self): self.assertRaises(ComponentLookupError, self._callFUT, object()) def test_get_component_registry_w_invalid_context_w_adapter(self): + from zope.interface import Interface + from guillotina.component.globalregistry import get_global_components from guillotina.component.interfaces import IComponentLookup - from zope.interface import Interface gsm = get_global_components() sm = object() @@ -70,8 +70,7 @@ def _adapt(x): class Test_get_adapter(unittest.TestCase): - from guillotina.component.testing import setUp - from guillotina.component.testing import tearDown + from guillotina.component.testing import setUp, tearDown def _callFUT(self, *args, **kw): from guillotina.component import get_adapter @@ -79,27 +78,29 @@ def _callFUT(self, *args, **kw): return get_adapter(*args, **kw) def test_anonymous_nonesuch(self): - from guillotina.component.interfaces import ComponentLookupError from zope.interface import Interface + from guillotina.component.interfaces import ComponentLookupError + class IFoo(Interface): pass self.assertRaises(ComponentLookupError, self._callFUT, object(), IFoo, "") def test_named_nonesuch(self): - from guillotina.component.interfaces import ComponentLookupError from zope.interface import Interface + from guillotina.component.interfaces import ComponentLookupError + class IFoo(Interface): pass self.assertRaises(ComponentLookupError, self._callFUT, object(), IFoo, "bar") def test_anonymous_hit(self): + from zope.interface import Interface, implementer + from guillotina.component import get_global_components - from zope.interface import implementer - from zope.interface import Interface class IFoo(Interface): pass @@ -123,9 +124,9 @@ def __init__(self, context): self.assertTrue(adapted.context is bar) def test_anonymous_hit_registered_for_None(self): + from zope.interface import Interface, implementer + from guillotina.component import get_global_components - from zope.interface import implementer - from zope.interface import Interface class IFoo(Interface): pass @@ -142,9 +143,9 @@ def __init__(self, context): self.assertTrue(adapted.context is ctx) def test_named_hit(self): + from zope.interface import Interface, implementer + from guillotina.component import get_global_components - from zope.interface import implementer - from zope.interface import Interface class IFoo(Interface): pass @@ -170,8 +171,7 @@ def __init__(self, context): class Test_query_adapter(unittest.TestCase): - from guillotina.component.testing import setUp - from guillotina.component.testing import tearDown + from guillotina.component.testing import setUp, tearDown def _callFUT(self, *args, **kw): from guillotina.component import query_adapter @@ -195,9 +195,9 @@ class IFoo(Interface): self.assertEqual(self._callFUT(object(), IFoo, "bar"), None) def test_anonymous_hit(self): + from zope.interface import Interface, implementer + from guillotina.component import get_global_components - from zope.interface import implementer - from zope.interface import Interface class IFoo(Interface): pass @@ -221,9 +221,9 @@ def __init__(self, context): self.assertTrue(adapted.context is bar) def test_named_hit(self): + from zope.interface import Interface, implementer + from guillotina.component import get_global_components - from zope.interface import implementer - from zope.interface import Interface class IFoo(Interface): pass @@ -247,11 +247,11 @@ def __init__(self, context): self.assertTrue(adapted.context is bar) def test_nested(self): + from zope.interface import Interface, implementer + from zope.interface.registry import Components + from guillotina.component import get_global_components from guillotina.component.tests.examples import ConformsToIComponentLookup - from zope.interface import implementer - from zope.interface import Interface - from zope.interface.registry import Components class IFoo(Interface): pass @@ -289,8 +289,7 @@ def __init__(self, sm): class Test_get_multi_adapter(unittest.TestCase): - from guillotina.component.testing import setUp - from guillotina.component.testing import tearDown + from guillotina.component.testing import setUp, tearDown def _callFUT(self, *args, **kw): from guillotina.component import get_multi_adapter @@ -298,27 +297,29 @@ def _callFUT(self, *args, **kw): return get_multi_adapter(*args, **kw) def test_anonymous_nonesuch(self): - from guillotina.component.interfaces import ComponentLookupError from zope.interface import Interface + from guillotina.component.interfaces import ComponentLookupError + class IFoo(Interface): pass self.assertRaises(ComponentLookupError, self._callFUT, (object(), object()), IFoo, "") def test_named_nonesuch(self): - from guillotina.component.interfaces import ComponentLookupError from zope.interface import Interface + from guillotina.component.interfaces import ComponentLookupError + class IFoo(Interface): pass self.assertRaises(ComponentLookupError, self._callFUT, (object(), object()), IFoo, "bar") def test_anonymous_hit(self): + from zope.interface import Interface, implementer + from guillotina.component import get_global_components - from zope.interface import implementer - from zope.interface import Interface class IFoo(Interface): pass @@ -351,9 +352,9 @@ def __init__(self, first, second): self.assertTrue(adapted.second is baz) def test_anonymous_hit_registered_for_None(self): + from zope.interface import Interface, implementer + from guillotina.component import get_global_components - from zope.interface import implementer - from zope.interface import Interface class IFoo(Interface): pass @@ -382,9 +383,9 @@ def __init__(self, first, second): self.assertTrue(adapted.second is baz) def test_named_hit(self): + from zope.interface import Interface, implementer + from guillotina.component import get_global_components - from zope.interface import implementer - from zope.interface import Interface class IFoo(Interface): pass @@ -419,8 +420,7 @@ def __init__(self, first, second): class Test_query_multi_adapter(unittest.TestCase): - from guillotina.component.testing import setUp - from guillotina.component.testing import tearDown + from guillotina.component.testing import setUp, tearDown def _callFUT(self, *args, **kw): from guillotina.component import query_multi_adapter @@ -444,9 +444,9 @@ class IFoo(Interface): self.assertEqual(self._callFUT((object(), object()), IFoo, "bar"), None) def test_anonymous_hit(self): + from zope.interface import Interface, implementer + from guillotina.component import get_global_components - from zope.interface import implementer - from zope.interface import Interface class IFoo(Interface): pass @@ -479,9 +479,9 @@ def __init__(self, first, second): self.assertTrue(adapted.second is baz) def test_named_hit(self): + from zope.interface import Interface, implementer + from guillotina.component import get_global_components - from zope.interface import implementer - from zope.interface import Interface class IFoo(Interface): pass @@ -514,11 +514,11 @@ def __init__(self, first, second): self.assertTrue(adapted.second is baz) def test_nested(self): + from zope.interface import Interface, implementer + from zope.interface.registry import Components + from guillotina.component import get_global_components from guillotina.component.tests.examples import ConformsToIComponentLookup - from zope.interface import implementer - from zope.interface import Interface - from zope.interface.registry import Components class IFoo(Interface): pass @@ -563,9 +563,9 @@ def __init__(self, sm): self.assertTrue(adapted.second is baz) def test_wo_sitemanager(self): + from zope.interface import Interface, implementer + from guillotina.component.interfaces import ComponentLookupError - from zope.interface import implementer - from zope.interface import Interface class IFoo(Interface): pass @@ -596,8 +596,7 @@ def __conform__(self, iface): class Test_get_adapters(unittest.TestCase): - from guillotina.component.testing import setUp - from guillotina.component.testing import tearDown + from guillotina.component.testing import setUp, tearDown def _callFUT(self, *args, **kw): from guillotina.component import get_adapters @@ -613,9 +612,10 @@ class IFoo(Interface): self.assertEqual(list(self._callFUT((object(),), IFoo)), []) def test_hit(self): - from guillotina.component import get_global_components from zope.interface import Interface + from guillotina.component import get_global_components + class IFoo(Interface): pass @@ -637,9 +637,9 @@ def __init__(self, context): self.assertTrue(("bar", "BazAdapter") in names) def test_wo_sitemanager(self): + from zope.interface import Interface, implementer + from guillotina.component.interfaces import ComponentLookupError - from zope.interface import implementer - from zope.interface import Interface class IFoo(Interface): pass @@ -670,8 +670,7 @@ def __conform__(self, iface): class Test_subscribers(unittest.TestCase): - from guillotina.component.testing import setUp - from guillotina.component.testing import tearDown + from guillotina.component.testing import setUp, tearDown def _callFUT(self, *args, **kw): from guillotina.component import subscribers @@ -688,9 +687,10 @@ class IFoo(Interface): self.assertEqual(subscribers, []) def test_hit(self): - from guillotina.component import get_global_components from zope.interface import Interface + from guillotina.component import get_global_components + class IFoo(Interface): pass @@ -712,9 +712,10 @@ def __init__(self, context): self.assertTrue("BazAdapter" in names) def test_wo_sitemanager(self): - from guillotina.component.interfaces import ComponentLookupError from zope.interface import Interface + from guillotina.component.interfaces import ComponentLookupError + class IFoo(Interface): pass @@ -728,8 +729,7 @@ def __conform__(self, iface): class Test_handle(unittest.TestCase): - from guillotina.component.testing import setUp - from guillotina.component.testing import tearDown + from guillotina.component.testing import setUp, tearDown def _callFUT(self, *args, **kw): from guillotina.component import handle @@ -745,9 +745,9 @@ class IFoo(Interface): subscribers = self._callFUT((object,), IFoo) # doesn't raise def test_hit(self): + from zope.interface import Interface, implementer + from guillotina.component import get_global_components - from zope.interface import implementer - from zope.interface import Interface class IFoo(Interface): pass @@ -775,8 +775,7 @@ def _baz(context): class Test_get_utility(unittest.TestCase): - from guillotina.component.testing import setUp - from guillotina.component.testing import tearDown + from guillotina.component.testing import setUp, tearDown def _callFUT(self, *args, **kw): from guillotina.component._api import get_utility @@ -784,27 +783,30 @@ def _callFUT(self, *args, **kw): return get_utility(*args, **kw) def test_anonymous_nonesuch(self): - from guillotina.component.interfaces import ComponentLookupError from zope.interface import Interface + from guillotina.component.interfaces import ComponentLookupError + class IFoo(Interface): pass self.assertRaises(ComponentLookupError, self._callFUT, IFoo) def test_named_nonesuch(self): - from guillotina.component.interfaces import ComponentLookupError from zope.interface import Interface + from guillotina.component.interfaces import ComponentLookupError + class IFoo(Interface): pass self.assertRaises(ComponentLookupError, self._callFUT, IFoo, name="bar") def test_anonymous_hit(self): - from guillotina.component import get_global_components from zope.interface import Interface + from guillotina.component import get_global_components + class IFoo(Interface): pass @@ -813,9 +815,10 @@ class IFoo(Interface): self.assertTrue(self._callFUT(IFoo) is obj) def test_named_hit(self): - from guillotina.component import get_global_components from zope.interface import Interface + from guillotina.component import get_global_components + class IFoo(Interface): pass @@ -824,9 +827,10 @@ class IFoo(Interface): self.assertTrue(self._callFUT(IFoo, name="bar") is obj) def test_w_conforming_context(self): + from zope.interface import Interface + from guillotina.component import get_global_components from guillotina.component.tests.examples import ConformsToIComponentLookup - from zope.interface import Interface class SM(object): def __init__(self, obj): @@ -848,8 +852,7 @@ class IFoo(Interface): class Test_query_utility(unittest.TestCase): - from guillotina.component.testing import setUp - from guillotina.component.testing import tearDown + from guillotina.component.testing import setUp, tearDown def _callFUT(self, *args, **kw): from guillotina.component._api import query_utility @@ -891,9 +894,10 @@ class IFoo(Interface): self.assertTrue(self._callFUT(IFoo, name="bar", default=obj) is obj) def test_anonymous_hit(self): - from guillotina.component import get_global_components from zope.interface import Interface + from guillotina.component import get_global_components + class IFoo(Interface): pass @@ -902,9 +906,10 @@ class IFoo(Interface): self.assertTrue(self._callFUT(IFoo) is obj) def test_named_hit(self): - from guillotina.component import get_global_components from zope.interface import Interface + from guillotina.component import get_global_components + class IFoo(Interface): pass @@ -913,9 +918,10 @@ class IFoo(Interface): self.assertTrue(self._callFUT(IFoo, name="bar") is obj) def test_w_conforming_context(self): + from zope.interface import Interface + from guillotina.component import get_global_components from guillotina.component.tests.examples import ConformsToIComponentLookup - from zope.interface import Interface class SM(object): def __init__(self, obj): @@ -937,8 +943,7 @@ class IFoo(Interface): class Test_get_utilities_for(unittest.TestCase): - from guillotina.component.testing import setUp - from guillotina.component.testing import tearDown + from guillotina.component.testing import setUp, tearDown def _callFUT(self, *args, **kw): from guillotina.component._api import get_utilities_for @@ -954,9 +959,10 @@ class IFoo(Interface): self.assertEqual(list(self._callFUT(IFoo)), []) def test_hit(self): - from guillotina.component import get_global_components from zope.interface import Interface + from guillotina.component import get_global_components + class IFoo(Interface): pass @@ -972,8 +978,7 @@ class IFoo(Interface): class Test_get_all_utilities_registered_for(unittest.TestCase): - from guillotina.component.testing import setUp - from guillotina.component.testing import tearDown + from guillotina.component.testing import setUp, tearDown def _callFUT(self, *args, **kw): from guillotina.component import get_all_utilities_registered_for @@ -989,9 +994,10 @@ class IFoo(Interface): self.assertEqual(list(self._callFUT(IFoo)), []) def test_hit(self): - from guillotina.component import get_global_components from zope.interface import Interface + from guillotina.component import get_global_components + class IFoo(Interface): pass @@ -1013,8 +1019,7 @@ class IBar(IFoo): class Test_get_factory_interfaces(unittest.TestCase): - from guillotina.component.testing import setUp - from guillotina.component.testing import tearDown + from guillotina.component.testing import setUp, tearDown def _callFUT(self, *args, **kw): from guillotina.component import get_factory_interfaces @@ -1027,9 +1032,10 @@ def test_miss(self): self.assertRaises(ComponentLookupError, self._callFUT, "nonesuch") def test_hit(self): - from guillotina.component.interfaces import IFactory from zope.interface import Interface + from guillotina.component.interfaces import IFactory + class IFoo(Interface): pass @@ -1052,8 +1058,7 @@ def queryUtility(self, iface, name, default): class Test_get_factories_for(unittest.TestCase): - from guillotina.component.testing import setUp - from guillotina.component.testing import tearDown + from guillotina.component.testing import setUp, tearDown def _callFUT(self, *args, **kw): from guillotina.component import get_factories_for @@ -1069,10 +1074,9 @@ class IFoo(Interface): self.assertEqual(list(self._callFUT(IFoo)), []) def test_w_factory_returning_spec(self): + from zope.interface import Interface, implementer, providedBy + from guillotina.component.interfaces import IFactory - from zope.interface import implementer - from zope.interface import Interface - from zope.interface import providedBy class IFoo(Interface): pass @@ -1099,9 +1103,10 @@ def getUtilitiesFor(self, iface): self.assertEqual(list(self._callFUT(IBar, context=Context())), [("test", _factory)]) def test_w_factory_returning_list_of_interfaces(self): - from guillotina.component.interfaces import IFactory from zope.interface import Interface + from guillotina.component.interfaces import IFactory + class IFoo(Interface): pass @@ -1131,9 +1136,9 @@ def getUtilitiesFor(self, iface): def _makeMyUtility(name, sm): global IMyUtility + from zope.interface import Interface, implementer + from guillotina.component.tests.examples import ConformsToIComponentLookup - from zope.interface import implementer - from zope.interface import Interface if IMyUtility is None: diff --git a/guillotina/component/tests/test__declaration.py b/guillotina/component/tests/test__declaration.py index f22ecb5ae..d62097ef4 100644 --- a/guillotina/component/tests/test__declaration.py +++ b/guillotina/component/tests/test__declaration.py @@ -123,10 +123,11 @@ def _try(): self.assertRaises(AttributeError, _try) def test_called_from_function(self): - from guillotina.component._declaration import adapts + import warnings + from zope.interface import Interface - import warnings + from guillotina.component._declaration import adapts class IFoo(Interface): pass @@ -142,11 +143,12 @@ class IFoo(Interface): self.assertEqual(len(log), 0) # no longer warn def test_called_twice_from_class(self): - from guillotina.component._declaration import adapts + import warnings + from zope.interface import Interface from zope.interface._compat import PYTHON3 - import warnings + from guillotina.component._declaration import adapts class IFoo(Interface): pass @@ -168,9 +170,10 @@ class IBar(Interface): self.fail("Didn't raise TypeError") def test_called_once_from_class(self): - from guillotina.component._declaration import adapts from zope.interface import Interface + from guillotina.component._declaration import adapts + class IFoo(Interface): pass @@ -207,9 +210,10 @@ class Baz(object): self.assertEqual(self._callFUT(Baz), (IFoo, IBar)) def test__call___w_inst_of_decorated_class(self): - from guillotina.component._declaration import _adapts_descr from zope.interface import Interface + from guillotina.component._declaration import _adapts_descr + class IFoo(Interface): pass diff --git a/guillotina/component/tests/test_event.py b/guillotina/component/tests/test_event.py index 507637c69..87e8be0f2 100644 --- a/guillotina/component/tests/test_event.py +++ b/guillotina/component/tests/test_event.py @@ -17,9 +17,10 @@ class Test_dispatch(unittest.TestCase): def test_it(self): + from zope.interface import Interface + from guillotina.component.event import dispatch from guillotina.component.globalregistry import get_global_components - from zope.interface import Interface _adapted = [] diff --git a/guillotina/component/tests/test_factory.py b/guillotina/component/tests/test_factory.py index 319912973..248d8a90a 100644 --- a/guillotina/component/tests/test_factory.py +++ b/guillotina/component/tests/test_factory.py @@ -27,15 +27,17 @@ def _makeOne(self, callable=None, *args, **kw): return self._getTargetClass()(callable, *args, **kw) def test_class_conforms_to_IFactory(self): - from guillotina.component.interfaces import IFactory from zope.interface.verify import verifyClass + from guillotina.component.interfaces import IFactory + verifyClass(IFactory, self._getTargetClass()) def test_instance_conforms_to_IFactory(self): - from guillotina.component.interfaces import IFactory from zope.interface.verify import verifyObject + from guillotina.component.interfaces import IFactory + verifyObject(IFactory, self._makeOne()) def test_ctor_defaults(self): @@ -81,8 +83,7 @@ def _callable(*args, **kw): self.assertEqual(_called, [((), {"foo": "bar"})]) def test_get_interfaces_explicit(self): - from zope.interface import implementer - from zope.interface import Interface + from zope.interface import Interface, implementer class IFoo(Interface): pass @@ -103,8 +104,7 @@ def _callable(): self.assertEqual(list(spec), [IFoo, IBar]) def test_get_interfaces_implicit(self): - from zope.interface import implementer - from zope.interface import Interface + from zope.interface import Interface, implementer class IBaz(Interface): pass diff --git a/guillotina/component/tests/test_globalregistry.py b/guillotina/component/tests/test_globalregistry.py index ea25f9483..f02912483 100644 --- a/guillotina/component/tests/test_globalregistry.py +++ b/guillotina/component/tests/test_globalregistry.py @@ -35,8 +35,7 @@ def test_gsm_is_singleton(self): class Test_provide_utility(unittest.TestCase): - from guillotina.component.testing import setUp - from guillotina.component.testing import tearDown + from guillotina.component.testing import setUp, tearDown def _callFUT(self, *args, **kw): from guillotina.component.globalregistry import provide_utility @@ -44,9 +43,9 @@ def _callFUT(self, *args, **kw): return provide_utility(*args, **kw) def test_anonymous_no_provides(self): + from zope.interface import Interface, implementer + from guillotina.component.globalregistry import get_global_components - from zope.interface import implementer - from zope.interface import Interface class IFoo(Interface): pass @@ -61,9 +60,10 @@ class Foo(object): self.assertTrue(gsm.getUtility(IFoo, "") is foo) def test_named_w_provides(self): - from guillotina.component.globalregistry import get_global_components from zope.interface import Interface + from guillotina.component.globalregistry import get_global_components + class IFoo(Interface): pass @@ -78,8 +78,7 @@ class Foo(object): class Test_provide_adapter(unittest.TestCase): - from guillotina.component.testing import setUp - from guillotina.component.testing import tearDown + from guillotina.component.testing import setUp, tearDown def _callFUT(self, *args, **kw): from guillotina.component.globalregistry import provide_adapter @@ -87,10 +86,10 @@ def _callFUT(self, *args, **kw): return provide_adapter(*args, **kw) def test_anonymous_no_provides_no_adapts(self): + from zope.interface import Interface, implementer + from guillotina.component._declaration import adapter from guillotina.component.globalregistry import get_global_components - from zope.interface import implementer - from zope.interface import Interface class IFoo(Interface): pass @@ -116,9 +115,9 @@ def __init__(self, context): self.assertTrue(adapted.context is foo) def test_named_w_provides_w_adapts(self): + from zope.interface import Interface, implementer + from guillotina.component.globalregistry import get_global_components - from zope.interface import implementer - from zope.interface import Interface class IFoo(Interface): pass @@ -144,8 +143,7 @@ def __init__(self, context): class Test_provide_subscription_adapter(unittest.TestCase): - from guillotina.component.testing import setUp - from guillotina.component.testing import tearDown + from guillotina.component.testing import setUp, tearDown def _callFUT(self, *args, **kw): from guillotina.component.globalregistry import provide_subscription_adapter @@ -153,10 +151,10 @@ def _callFUT(self, *args, **kw): return provide_subscription_adapter(*args, **kw) def test_no_provides_no_adapts(self): + from zope.interface import Interface, implementer + from guillotina.component._declaration import adapter from guillotina.component.globalregistry import get_global_components - from zope.interface import implementer - from zope.interface import Interface class IFoo(Interface): pass @@ -183,9 +181,9 @@ def __init__(self, context): self.assertTrue(adapted[0].context is foo) def test_w_provides_w_adapts(self): + from zope.interface import Interface, implementer + from guillotina.component.globalregistry import get_global_components - from zope.interface import implementer - from zope.interface import Interface class IFoo(Interface): pass @@ -212,8 +210,7 @@ def __init__(self, context): class Test_provide_handler(unittest.TestCase): - from guillotina.component.testing import setUp - from guillotina.component.testing import tearDown + from guillotina.component.testing import setUp, tearDown def _callFUT(self, *args, **kw): from guillotina.component.globalregistry import provide_handler @@ -221,11 +218,10 @@ def _callFUT(self, *args, **kw): return provide_handler(*args, **kw) def test_no_adapts(self): + from zope.interface import Interface, implementer, providedBy + from guillotina.component._declaration import adapter from guillotina.component.globalregistry import get_global_components - from zope.interface import implementer - from zope.interface import Interface - from zope.interface import providedBy class IFoo(Interface): pass @@ -248,9 +244,10 @@ def _handler(context): self.assertTrue(hr.factory is _handler) def test_w_adapts(self): - from guillotina.component.globalregistry import get_global_components from zope.interface import Interface + from guillotina.component.globalregistry import get_global_components + class IFoo(Interface): pass diff --git a/guillotina/component/tests/test_registry.py b/guillotina/component/tests/test_registry.py index 1541db7a9..6be3601e4 100644 --- a/guillotina/component/tests/test_registry.py +++ b/guillotina/component/tests/test_registry.py @@ -17,8 +17,7 @@ class Test_dispatch_utility_registration_event(unittest.TestCase): - from guillotina.component.testing import setUp - from guillotina.component.testing import tearDown + from guillotina.component.testing import setUp, tearDown def _callFUT(self, *args, **kw): from guillotina.component.registry import dispatch_utility_registration_event @@ -44,8 +43,7 @@ def _handle(*args): class Test_dispatch_adapter_registration_event(unittest.TestCase): - from guillotina.component.testing import setUp - from guillotina.component.testing import tearDown + from guillotina.component.testing import setUp, tearDown def _callFUT(self, *args, **kw): from guillotina.component.registry import dispatch_adapter_registration_event @@ -73,8 +71,7 @@ def _handle(*args): class Test_dispatch_subscription_adapter_registration_event(unittest.TestCase): - from guillotina.component.testing import setUp - from guillotina.component.testing import tearDown + from guillotina.component.testing import setUp, tearDown def _callFUT(self, *args, **kw): from guillotina.component.registry import dispatch_subscription_adapter_registration_event @@ -102,8 +99,7 @@ def _handle(*args): class Test_dispatch_handler_registration_event(unittest.TestCase): - from guillotina.component.testing import setUp - from guillotina.component.testing import tearDown + from guillotina.component.testing import setUp, tearDown def _callFUT(self, *args, **kw): from guillotina.component.registry import dispatch_handler_registration_event diff --git a/guillotina/configure/__init__.py b/guillotina/configure/__init__.py index 865bc7cf7..2d76ec26e 100644 --- a/guillotina/configure/__init__.py +++ b/guillotina/configure/__init__.py @@ -1,44 +1,43 @@ +import asyncio +import inspect +import logging from collections import OrderedDict +from pprint import pformat +from typing import Any, Dict, Optional, Tuple + +from zope.interface import Interface, classImplements +from zope.interface.interfaces import IInterface + from guillotina import routes from guillotina._settings import app_settings from guillotina.configure import component -from guillotina.configure.behaviors import BehaviorAdapterFactory -from guillotina.configure.behaviors import BehaviorRegistration -from guillotina.exceptions import ConfigurationError -from guillotina.exceptions import ServiceConfigurationError -from guillotina.gtypes import ConfigurationType -from guillotina.gtypes import ResolvableType -from guillotina.interfaces import DEFAULT_ADD_PERMISSION -from guillotina.interfaces import IBehavior -from guillotina.interfaces import IBehaviorSchemaAwareFactory -from guillotina.interfaces import IDefaultLayer -from guillotina.interfaces import IJSONToValue -from guillotina.interfaces import ILanguage -from guillotina.interfaces import IPermission -from guillotina.interfaces import IRenderer -from guillotina.interfaces import IRequest -from guillotina.interfaces import IResource -from guillotina.interfaces import IResourceFactory -from guillotina.interfaces import IRole -from guillotina.interfaces import IValueToJson -from guillotina.interfaces import IView +from guillotina.configure.behaviors import BehaviorAdapterFactory, BehaviorRegistration +from guillotina.exceptions import ConfigurationError, ServiceConfigurationError +from guillotina.gtypes import ConfigurationType, ResolvableType +from guillotina.interfaces import ( + DEFAULT_ADD_PERMISSION, + IBehavior, + IBehaviorSchemaAwareFactory, + IDefaultLayer, + IJSONToValue, + ILanguage, + IPermission, + IRenderer, + IRequest, + IResource, + IResourceFactory, + IRole, + IValueToJson, + IView, +) from guillotina.security.permission import Permission -from guillotina.utils import get_caller_module -from guillotina.utils import get_module_dotted_name -from guillotina.utils import resolve_dotted_name -from guillotina.utils import resolve_module_path -from pprint import pformat -from typing import Any -from typing import Dict -from typing import Optional -from typing import Tuple -from zope.interface import classImplements -from zope.interface import Interface -from zope.interface.interfaces import IInterface +from guillotina.utils import ( + get_caller_module, + get_module_dotted_name, + resolve_dotted_name, + resolve_module_path, +) -import asyncio -import inspect -import logging _registered_configurations: ConfigurationType = [] # stored as tuple of (type, configuration) so we get keep it in the order @@ -632,8 +631,7 @@ def grant_directive(_context, principal=None, role=None, permission=None, permis def grantAll_directive(_context, principal=None, role=None): # noqa: N802 """Grant all permissions to a role or principal""" - from guillotina.security.security_code import principal_permission_manager - from guillotina.security.security_code import role_permission_manager + from guillotina.security.security_code import principal_permission_manager, role_permission_manager nspecified = (principal is not None) + (role is not None) diff --git a/guillotina/configure/behaviors.py b/guillotina/configure/behaviors.py index 0ca607059..99910c855 100644 --- a/guillotina/configure/behaviors.py +++ b/guillotina/configure/behaviors.py @@ -1,7 +1,7 @@ -from guillotina.interfaces import IBehavior -from guillotina.interfaces import IBehaviorAdapterFactory -from zope.interface import implementer -from zope.interface import Interface +from zope.interface import Interface, implementer + +from guillotina.interfaces import IBehavior, IBehaviorAdapterFactory + REGISTRATION_REPR = """\ <{class} {name} at {id} diff --git a/guillotina/configure/component.py b/guillotina/configure/component.py index a05f3e404..77bb78a98 100644 --- a/guillotina/configure/component.py +++ b/guillotina/configure/component.py @@ -13,16 +13,15 @@ ############################################################################## """Component Architecture configuration handlers """ +from zope.interface import Interface, implementedBy, providedBy + from guillotina.component._api import get_component_registry from guillotina.component._compat import _BLANK -from guillotina.component._declaration import adaptedBy -from guillotina.component._declaration import getName +from guillotina.component._declaration import adaptedBy, getName from guillotina.component.interface import provide_interface from guillotina.exceptions import ComponentConfigurationError from guillotina.i18n import MessageFactory -from zope.interface import implementedBy -from zope.interface import Interface -from zope.interface import providedBy + _ = MessageFactory("guillotina") diff --git a/guillotina/configure/config.py b/guillotina/configure/config.py index 84703b829..565b3b972 100644 --- a/guillotina/configure/config.py +++ b/guillotina/configure/config.py @@ -13,13 +13,14 @@ ############################################################################## """Configuration processor """ -from guillotina.exceptions import ConfigurationError -from guillotina.interfaces.configuration import IConfigurationContext -from zope.interface import implementer - import operator import sys +from zope.interface import implementer + +from guillotina.exceptions import ConfigurationError +from guillotina.interfaces.configuration import IConfigurationContext + def reraise(tp, value, tb=None): if value is None: diff --git a/guillotina/constraintypes.py b/guillotina/constraintypes.py index f793bedd6..2abbef53b 100644 --- a/guillotina/constraintypes.py +++ b/guillotina/constraintypes.py @@ -1,13 +1,11 @@ -from guillotina import app_settings -from guillotina import configure -from guillotina.content import get_cached_factory -from guillotina.interfaces import IConstrainTypes -from guillotina.interfaces import IDatabase -from guillotina.interfaces import IResource -from typing import List -from typing import Optional +from typing import List, Optional + from zope.interface import Interface +from guillotina import app_settings, configure +from guillotina.content import get_cached_factory +from guillotina.interfaces import IConstrainTypes, IDatabase, IResource + @configure.adapter(for_=Interface, provides=IConstrainTypes) class FTIConstrainAllowedTypes: diff --git a/guillotina/content.py b/guillotina/content.py index b54793290..60506010c 100644 --- a/guillotina/content.py +++ b/guillotina/content.py @@ -1,88 +1,73 @@ +import os +import pathlib from datetime import datetime +from typing import Any, AsyncIterator, Dict, FrozenSet, Iterator, List, Optional, Tuple, Union, cast + from dateutil.tz import tzutc -from guillotina import configure -from guillotina import task_vars -from guillotina._cache import BEHAVIOR_CACHE -from guillotina._cache import FACTORY_CACHE -from guillotina._cache import PERMISSIONS_CACHE -from guillotina._cache import SCHEMA_CACHE +from zope.interface import Interface, alsoProvides, implementer, noLongerProvides +from zope.interface.interfaces import ComponentLookupError + +import guillotina.db.orm.base +from guillotina import configure, task_vars +from guillotina._cache import BEHAVIOR_CACHE, FACTORY_CACHE, PERMISSIONS_CACHE, SCHEMA_CACHE from guillotina._settings import app_settings from guillotina.annotations import AnnotationData -from guillotina.auth.users import ANONYMOUS_USER_ID -from guillotina.auth.users import ROOT_USER_ID +from guillotina.auth.users import ANONYMOUS_USER_ID, ROOT_USER_ID from guillotina.behaviors import apply_markers from guillotina.browser import get_physical_path -from guillotina.component import get_adapter -from guillotina.component import get_utilities_for -from guillotina.component import get_utility -from guillotina.component import query_utility +from guillotina.component import get_adapter, get_utilities_for, get_utility, query_utility from guillotina.component.factory import Factory from guillotina.db import uid from guillotina.db.interfaces import ITransaction from guillotina.db.orm.interfaces import IBaseObject from guillotina.event import notify -from guillotina.events import BeforeObjectAddedEvent -from guillotina.events import BeforeObjectMovedEvent -from guillotina.events import ObjectDuplicatedEvent -from guillotina.events import ObjectLoadedEvent -from guillotina.events import ObjectMovedEvent -from guillotina.exceptions import ConflictIdOnContainer -from guillotina.exceptions import InvalidContentType -from guillotina.exceptions import NoPermissionToAdd -from guillotina.exceptions import NotAllowedContentType -from guillotina.exceptions import PreconditionFailed -from guillotina.exceptions import TransactionNotFound -from guillotina.interfaces import DEFAULT_ADD_PERMISSION -from guillotina.interfaces import IAddons -from guillotina.interfaces import IAnnotations -from guillotina.interfaces import IAsyncBehavior -from guillotina.interfaces import IBehavior -from guillotina.interfaces import IConstrainTypes -from guillotina.interfaces import IContainer -from guillotina.interfaces import IFolder -from guillotina.interfaces import IGetOwner -from guillotina.interfaces import IIDChecker -from guillotina.interfaces import IItem -from guillotina.interfaces import IJavaScriptApplication -from guillotina.interfaces import ILayers -from guillotina.interfaces import IPermission -from guillotina.interfaces import IPrincipalPermissionManager -from guillotina.interfaces import IPrincipalRoleManager -from guillotina.interfaces import IRequest -from guillotina.interfaces import IResource -from guillotina.interfaces import IResourceFactory -from guillotina.interfaces import IStaticDirectory -from guillotina.interfaces import IStaticFile +from guillotina.events import ( + BeforeObjectAddedEvent, + BeforeObjectMovedEvent, + ObjectDuplicatedEvent, + ObjectLoadedEvent, + ObjectMovedEvent, +) +from guillotina.exceptions import ( + ConflictIdOnContainer, + InvalidContentType, + NoPermissionToAdd, + NotAllowedContentType, + PreconditionFailed, + TransactionNotFound, +) +from guillotina.interfaces import ( + DEFAULT_ADD_PERMISSION, + IAddons, + IAnnotations, + IAsyncBehavior, + IBehavior, + IConstrainTypes, + IContainer, + IFolder, + IGetOwner, + IIDChecker, + IItem, + IJavaScriptApplication, + ILayers, + IPermission, + IPrincipalPermissionManager, + IPrincipalRoleManager, + IRequest, + IResource, + IResourceFactory, + IStaticDirectory, + IStaticFile, +) from guillotina.profile import profilable from guillotina.registry import REGISTRY_DATA_KEY from guillotina.response import HTTPConflict from guillotina.schema.utils import get_default_from_schema from guillotina.security.security_code import PrincipalPermissionManager from guillotina.transactions import get_transaction -from guillotina.utils import get_object_by_uid -from guillotina.utils import get_security_policy -from guillotina.utils import navigate_to -from guillotina.utils import valid_id +from guillotina.utils import get_object_by_uid, get_security_policy, navigate_to, valid_id from guillotina.utils.auth import get_authenticated_user_id -from typing import Any -from typing import AsyncIterator -from typing import cast -from typing import Dict -from typing import FrozenSet -from typing import Iterator -from typing import List -from typing import Optional -from typing import Tuple -from typing import Union -from zope.interface import alsoProvides -from zope.interface import implementer -from zope.interface import Interface -from zope.interface import noLongerProvides -from zope.interface.interfaces import ComponentLookupError -import guillotina.db.orm.base -import os -import pathlib _zone = tzutc() # utz tz is much faster than local tz info _marker = object() diff --git a/guillotina/contentapi.py b/guillotina/contentapi.py index 700d4cc77..148936f58 100644 --- a/guillotina/contentapi.py +++ b/guillotina/contentapi.py @@ -1,21 +1,16 @@ +import typing + +from zope.interface import alsoProvides + from guillotina import task_vars from guillotina._settings import app_settings from guillotina.auth.users import RootUser from guillotina.auth.utils import set_authenticated_user from guillotina.component import get_multi_adapter from guillotina.db.interfaces import ITransaction -from guillotina.interfaces import ACTIVE_LAYERS_KEY -from guillotina.interfaces import IContainer -from guillotina.interfaces import IResource +from guillotina.interfaces import ACTIVE_LAYERS_KEY, IContainer, IResource from guillotina.tests.utils import get_mocked_request -from guillotina.utils import get_authenticated_user -from guillotina.utils import get_object_url -from guillotina.utils import get_registry -from guillotina.utils import import_class -from guillotina.utils import navigate_to -from zope.interface import alsoProvides - -import typing +from guillotina.utils import get_authenticated_user, get_object_url, get_registry, import_class, navigate_to class ContentAPI: diff --git a/guillotina/contrib/cache/__init__.py b/guillotina/contrib/cache/__init__.py index b55c80a8d..3516fc4bc 100644 --- a/guillotina/contrib/cache/__init__.py +++ b/guillotina/contrib/cache/__init__.py @@ -1,5 +1,6 @@ from guillotina import configure + CACHE_PREFIX = "gcache2-" app_settings = { diff --git a/guillotina/contrib/cache/api.py b/guillotina/contrib/cache/api.py index 195850d57..6b8a85294 100644 --- a/guillotina/contrib/cache/api.py +++ b/guillotina/contrib/cache/api.py @@ -1,7 +1,6 @@ from guillotina import configure from guillotina.component import get_utility -from guillotina.interfaces import ICacheUtility -from guillotina.interfaces import IContainer +from guillotina.interfaces import ICacheUtility, IContainer @configure.service(context=IContainer, name="@cache-stats", method="GET", permission="guillotina.CacheManage") diff --git a/guillotina/contrib/cache/memcache.py b/guillotina/contrib/cache/memcache.py index b610b9854..f3369a23c 100644 --- a/guillotina/contrib/cache/memcache.py +++ b/guillotina/contrib/cache/memcache.py @@ -1,6 +1,8 @@ +from typing import Optional + from guillotina import app_settings from guillotina.contrib.cache.lru import LRU -from typing import Optional + _lru: Optional[LRU] = None diff --git a/guillotina/contrib/cache/serialize.py b/guillotina/contrib/cache/serialize.py index 98bd07aa8..8e149a0e9 100644 --- a/guillotina/contrib/cache/serialize.py +++ b/guillotina/contrib/cache/serialize.py @@ -1,9 +1,10 @@ -from guillotina.profile import profilable - -import asyncpg import pickle import typing +import asyncpg + +from guillotina.profile import profilable + @profilable def dumps(value: typing.Any) -> bytes: diff --git a/guillotina/contrib/cache/strategy.py b/guillotina/contrib/cache/strategy.py index 4785881c8..aee48d467 100644 --- a/guillotina/contrib/cache/strategy.py +++ b/guillotina/contrib/cache/strategy.py @@ -1,20 +1,16 @@ -from guillotina import app_settings -from guillotina import configure +import asyncio +import logging +from typing import Any, Dict, List + +from guillotina import app_settings, configure from guillotina.component import query_utility from guillotina.db.cache.base import BaseCache -from guillotina.db.interfaces import ITransaction -from guillotina.db.interfaces import ITransactionCache -from guillotina.exceptions import NoChannelConfigured -from guillotina.exceptions import NoPubSubUtility +from guillotina.db.interfaces import ITransaction, ITransactionCache +from guillotina.exceptions import NoChannelConfigured, NoPubSubUtility from guillotina.interfaces import ICacheUtility from guillotina.profile import profilable from guillotina.utils import notice_on_error_internal -from typing import Any -from typing import Dict -from typing import List -import asyncio -import logging logger = logging.getLogger("guillotina") diff --git a/guillotina/contrib/cache/utility.py b/guillotina/contrib/cache/utility.py index 80fb95604..679635185 100644 --- a/guillotina/contrib/cache/utility.py +++ b/guillotina/contrib/cache/utility.py @@ -1,22 +1,21 @@ +import asyncio +import logging +import pickle +import uuid +from sys import getsizeof +from typing import List, Optional + +import asyncpg + from guillotina import app_settings from guillotina.component import query_utility -from guillotina.contrib.cache import CACHE_PREFIX -from guillotina.contrib.cache import memcache -from guillotina.contrib.cache import serialize +from guillotina.contrib.cache import CACHE_PREFIX, memcache, serialize from guillotina.contrib.cache.lru import LRU from guillotina.exceptions import NoPubSubUtility from guillotina.interfaces import IPubSubUtility from guillotina.profile import profilable from guillotina.utils import resolve_dotted_name -from sys import getsizeof -from typing import List -from typing import Optional -import asyncio -import asyncpg -import logging -import pickle -import uuid logger = logging.getLogger("guillotina.contrib.cache") _default_size = 1024 diff --git a/guillotina/contrib/catalog/pg/__init__.py b/guillotina/contrib/catalog/pg/__init__.py index 131a4c59d..db141876b 100644 --- a/guillotina/contrib/catalog/pg/__init__.py +++ b/guillotina/contrib/catalog/pg/__init__.py @@ -1,6 +1,7 @@ +import logging + from guillotina import configure -import logging logger = logging.getLogger("guillotina") diff --git a/guillotina/contrib/catalog/pg/indexes.py b/guillotina/contrib/catalog/pg/indexes.py index 00f60737e..20803bdb9 100644 --- a/guillotina/contrib/catalog/pg/indexes.py +++ b/guillotina/contrib/catalog/pg/indexes.py @@ -1,9 +1,9 @@ +import typing + from guillotina.catalog.utils import iter_indexes from guillotina.contrib.catalog.pg.utils import sqlq from guillotina.db.interfaces import IPostgresStorage -import typing - class BasicJsonIndex: operators: typing.List[str] = ["=", "!=", "?", "?|", "is null", "is not null"] diff --git a/guillotina/contrib/catalog/pg/parser.py b/guillotina/contrib/catalog/pg/parser.py index 0212db1a3..ecb0ee156 100644 --- a/guillotina/contrib/catalog/pg/parser.py +++ b/guillotina/contrib/catalog/pg/parser.py @@ -1,17 +1,17 @@ +import typing +import urllib + from dateutil.parser import parse + from guillotina import configure -from guillotina.catalog.parser import BaseParser -from guillotina.catalog.parser import to_list +from guillotina.catalog.parser import BaseParser, to_list from guillotina.catalog.types import BasicParsedQueryInfo from guillotina.catalog.utils import get_index_definition from guillotina.contrib.catalog.pg import logger from guillotina.contrib.catalog.pg.indexes import get_pg_index -from guillotina.interfaces import IResource -from guillotina.interfaces import ISearchParser +from guillotina.interfaces import IResource, ISearchParser from guillotina.interfaces.catalog import ICatalogUtility -import typing -import urllib _type_mapping = {"int": int, "float": float} diff --git a/guillotina/contrib/catalog/pg/utility.py b/guillotina/contrib/catalog/pg/utility.py index 011997450..493eb6c04 100644 --- a/guillotina/contrib/catalog/pg/utility.py +++ b/guillotina/contrib/catalog/pg/utility.py @@ -1,3 +1,11 @@ +import json +import os +import typing + +import asyncpg.exceptions +import orjson +from zope.interface import implementer + from guillotina.api.content import DefaultGET from guillotina.auth.users import AnonymousUser from guillotina.catalog.catalog import DefaultSearchUtility @@ -5,41 +13,28 @@ from guillotina.component import get_utility from guillotina.const import TRASHED_ID from guillotina.contrib.catalog.pg import logger -from guillotina.contrib.catalog.pg.indexes import BasicJsonIndex -from guillotina.contrib.catalog.pg.indexes import get_pg_index -from guillotina.contrib.catalog.pg.indexes import get_pg_indexes +from guillotina.contrib.catalog.pg.indexes import BasicJsonIndex, get_pg_index, get_pg_indexes from guillotina.contrib.catalog.pg.parser import ParsedQueryInfo from guillotina.contrib.catalog.pg.utils import sqlq -from guillotina.db.interfaces import IPostgresStorage -from guillotina.db.interfaces import ITransaction -from guillotina.db.interfaces import IWriter +from guillotina.db.interfaces import IPostgresStorage, ITransaction, IWriter from guillotina.db.orm.interfaces import IBaseObject from guillotina.db.storages.utils import register_sql from guillotina.db.uid import MAX_UID_LENGTH -from guillotina.exceptions import ContainerNotFound -from guillotina.exceptions import RequestNotFound -from guillotina.exceptions import TransactionNotFound -from guillotina.interfaces import IDatabase -from guillotina.interfaces import IFolder -from guillotina.interfaces import IPGCatalogUtility -from guillotina.interfaces import IResource +from guillotina.exceptions import ContainerNotFound, RequestNotFound, TransactionNotFound +from guillotina.interfaces import IDatabase, IFolder, IPGCatalogUtility, IResource from guillotina.interfaces.content import IApplication from guillotina.response import HTTPNotImplemented from guillotina.transactions import get_transaction -from guillotina.utils import find_container -from guillotina.utils import get_authenticated_user -from guillotina.utils import get_content_path -from guillotina.utils import get_current_request -from guillotina.utils import get_current_transaction -from guillotina.utils import get_object_url -from guillotina.utils import get_roles_principal -from zope.interface import implementer +from guillotina.utils import ( + find_container, + get_authenticated_user, + get_content_path, + get_current_request, + get_current_transaction, + get_object_url, + get_roles_principal, +) -import asyncpg.exceptions -import json -import orjson -import os -import typing # 2019-06-15T18:37:31.008359+00:00 PG_FUNCTIONS = [ diff --git a/guillotina/contrib/dbusers/__init__.py b/guillotina/contrib/dbusers/__init__.py index 324523d53..b3eaade0d 100644 --- a/guillotina/contrib/dbusers/__init__.py +++ b/guillotina/contrib/dbusers/__init__.py @@ -1,6 +1,7 @@ from guillotina import configure from guillotina.i18n import MessageFactory + _ = MessageFactory("guillotina.contrib.dbusers") diff --git a/guillotina/contrib/dbusers/adapters.py b/guillotina/contrib/dbusers/adapters.py index 77f75fb2f..9edf84559 100644 --- a/guillotina/contrib/dbusers/adapters.py +++ b/guillotina/contrib/dbusers/adapters.py @@ -1,9 +1,10 @@ -from .content.users import IUserManager -from guillotina import app_settings -from guillotina import configure +import re + +from guillotina import app_settings, configure from guillotina.interfaces import IIDChecker -import re +from .content.users import IUserManager + # from https://github.com/theskumar/python-usernames/blob/master/usernames/validators.py diff --git a/guillotina/contrib/dbusers/content/groups.py b/guillotina/contrib/dbusers/content/groups.py index 741eb9041..22bdaa379 100644 --- a/guillotina/contrib/dbusers/content/groups.py +++ b/guillotina/contrib/dbusers/content/groups.py @@ -1,11 +1,10 @@ -from guillotina import configure -from guillotina import schema +from zope.interface import implementer + +from guillotina import configure, schema from guillotina.content import Folder from guillotina.contrib.dbusers import _ from guillotina.directives import index_field -from guillotina.interfaces import IFolder -from guillotina.interfaces import IPrincipal -from zope.interface import implementer +from guillotina.interfaces import IFolder, IPrincipal class IGroupManager(IFolder): diff --git a/guillotina/contrib/dbusers/content/users.py b/guillotina/contrib/dbusers/content/users.py index 2eecf3c4a..b362ba47d 100644 --- a/guillotina/contrib/dbusers/content/users.py +++ b/guillotina/contrib/dbusers/content/users.py @@ -1,15 +1,9 @@ -from guillotina import configure -from guillotina import schema -from guillotina.auth.validators import check_password -from guillotina.auth.validators import hash_password +from guillotina import configure, schema +from guillotina.auth.validators import check_password, hash_password from guillotina.content import Folder from guillotina.contrib.dbusers import _ -from guillotina.directives import index_field -from guillotina.directives import read_permission -from guillotina.directives import write_permission -from guillotina.interfaces import Allow -from guillotina.interfaces import IFolder -from guillotina.interfaces import IPrincipal +from guillotina.directives import index_field, read_permission, write_permission +from guillotina.interfaces import Allow, IFolder, IPrincipal from guillotina.response import HTTPUnauthorized diff --git a/guillotina/contrib/dbusers/install.py b/guillotina/contrib/dbusers/install.py index dc44f4e20..3ea41e8cc 100644 --- a/guillotina/contrib/dbusers/install.py +++ b/guillotina/contrib/dbusers/install.py @@ -4,8 +4,8 @@ from guillotina.event import notify from guillotina.events import ObjectAddedEvent from guillotina.interfaces import ILayers -from guillotina.utils import get_authenticated_user_id -from guillotina.utils import get_registry +from guillotina.utils import get_authenticated_user_id, get_registry + USERS_LAYER = "guillotina.contrib.dbusers.interfaces.IDBUsersLayer" diff --git a/guillotina/contrib/dbusers/permissions.py b/guillotina/contrib/dbusers/permissions.py index 3949edbda..8674ae1f4 100644 --- a/guillotina/contrib/dbusers/permissions.py +++ b/guillotina/contrib/dbusers/permissions.py @@ -1,5 +1,6 @@ from guillotina import configure + configure.permission("guillotina.AddUser", title="Add plone user") configure.permission("guillotina.AddGroup", title="Add plone group") configure.permission("guillotina.Nobody", "Permission not assigned to anyone") diff --git a/guillotina/contrib/dbusers/register_user.py b/guillotina/contrib/dbusers/register_user.py index e9e4eb4ce..e3b2705c9 100644 --- a/guillotina/contrib/dbusers/register_user.py +++ b/guillotina/contrib/dbusers/register_user.py @@ -2,8 +2,7 @@ from guillotina.auth import authenticate_user from guillotina.content import create_content_in_container from guillotina.event import notify -from guillotina.events import ObjectAddedEvent -from guillotina.events import UserLogin +from guillotina.events import ObjectAddedEvent, UserLogin from guillotina.utils import get_current_container diff --git a/guillotina/contrib/dbusers/serializers.py b/guillotina/contrib/dbusers/serializers.py index 97d31a323..da72fb389 100644 --- a/guillotina/contrib/dbusers/serializers.py +++ b/guillotina/contrib/dbusers/serializers.py @@ -1,11 +1,10 @@ +from zope.interface import Interface + from guillotina import configure from guillotina.contrib.dbusers.content.groups import IGroup from guillotina.contrib.dbusers.content.users import IUser -from guillotina.interfaces import IPATCH -from guillotina.interfaces import IResourceDeserializeFromJson -from guillotina.interfaces import IResourceSerializeToJsonSummary +from guillotina.interfaces import IPATCH, IResourceDeserializeFromJson, IResourceSerializeToJsonSummary from guillotina.json.serialize_content import DefaultJSONSummarySerializer -from zope.interface import Interface @configure.adapter(for_=(IUser, Interface), provides=IResourceSerializeToJsonSummary) diff --git a/guillotina/contrib/dbusers/services/__init__.py b/guillotina/contrib/dbusers/services/__init__.py index 4df73882f..3ab5401e0 100644 --- a/guillotina/contrib/dbusers/services/__init__.py +++ b/guillotina/contrib/dbusers/services/__init__.py @@ -1,11 +1,13 @@ -from . import groups # noqa -from . import roles # noqa -from . import users # noqa from guillotina import configure from guillotina.api.content import DefaultPOST from guillotina.contrib.dbusers.content.groups import IGroupManager from guillotina.contrib.dbusers.content.users import IUserManager +from . import groups # noqa +from . import roles # noqa +from . import users # noqa + + # override some views... configure.service(context=IGroupManager, method="POST", permission="guillotina.AddGroup", allow_access=True)( DefaultPOST diff --git a/guillotina/contrib/dbusers/services/groups.py b/guillotina/contrib/dbusers/services/groups.py index b761a6aad..38e0c342b 100644 --- a/guillotina/contrib/dbusers/services/groups.py +++ b/guillotina/contrib/dbusers/services/groups.py @@ -1,6 +1,10 @@ +import logging +import typing + +from zope.interface import alsoProvides + from guillotina import configure -from guillotina.api.content import DefaultDELETE -from guillotina.api.content import DefaultPATCH +from guillotina.api.content import DefaultDELETE, DefaultPATCH from guillotina.api.service import Service from guillotina.component import get_multi_adapter from guillotina.content import create_content_in_container @@ -8,17 +12,10 @@ from guillotina.contrib.dbusers.services.utils import ListGroupsOrUsersService from guillotina.event import notify from guillotina.events import ObjectAddedEvent -from guillotina.interfaces import IContainer -from guillotina.interfaces import IPATCH -from guillotina.interfaces import IResourceSerializeToJsonSummary -from guillotina.response import HTTPNotFound -from guillotina.response import HTTPPreconditionFailed -from guillotina.utils import navigate_to -from guillotina.utils import valid_id -from zope.interface import alsoProvides +from guillotina.interfaces import IPATCH, IContainer, IResourceSerializeToJsonSummary +from guillotina.response import HTTPNotFound, HTTPPreconditionFailed +from guillotina.utils import navigate_to, valid_id -import logging -import typing logger = logging.getLogger("guillotina.contrib.dbusers") diff --git a/guillotina/contrib/dbusers/services/roles.py b/guillotina/contrib/dbusers/services/roles.py index 89508e702..1928d2280 100644 --- a/guillotina/contrib/dbusers/services/roles.py +++ b/guillotina/contrib/dbusers/services/roles.py @@ -1,7 +1,6 @@ from guillotina import configure from guillotina.api.service import Service -from guillotina.auth.role import global_roles -from guillotina.auth.role import local_roles +from guillotina.auth.role import global_roles, local_roles from guillotina.interfaces import IContainer diff --git a/guillotina/contrib/dbusers/services/users.py b/guillotina/contrib/dbusers/services/users.py index 886a7838d..49f7cdc15 100644 --- a/guillotina/contrib/dbusers/services/users.py +++ b/guillotina/contrib/dbusers/services/users.py @@ -1,21 +1,21 @@ +import typing + +from zope.interface import alsoProvides + from guillotina import configure -from guillotina.api.content import DefaultDELETE -from guillotina.api.content import DefaultPATCH +from guillotina.api.content import DefaultDELETE, DefaultPATCH from guillotina.api.service import Service -from guillotina.component import get_multi_adapter -from guillotina.component import queryMultiAdapter +from guillotina.component import get_multi_adapter, queryMultiAdapter from guillotina.contrib.dbusers.content.users import User from guillotina.contrib.dbusers.services.utils import ListGroupsOrUsersService -from guillotina.interfaces import IContainer -from guillotina.interfaces import IPATCH -from guillotina.interfaces import IResourceSerializeToJson -from guillotina.interfaces import IResourceSerializeToJsonSummary +from guillotina.interfaces import ( + IPATCH, + IContainer, + IResourceSerializeToJson, + IResourceSerializeToJsonSummary, +) from guillotina.response import HTTPNotFound -from guillotina.utils import get_authenticated_user -from guillotina.utils import navigate_to -from zope.interface import alsoProvides - -import typing +from guillotina.utils import get_authenticated_user, navigate_to @configure.service( diff --git a/guillotina/contrib/dbusers/services/utils.py b/guillotina/contrib/dbusers/services/utils.py index 4fbcd6fc4..706ac9d9e 100644 --- a/guillotina/contrib/dbusers/services/utils.py +++ b/guillotina/contrib/dbusers/services/utils.py @@ -1,15 +1,13 @@ +import typing as t + from guillotina.api.service import Service from guillotina.catalog.catalog import DefaultSearchUtility -from guillotina.component import get_multi_adapter -from guillotina.component import query_utility +from guillotina.component import get_multi_adapter, query_utility from guillotina.content import Container -from guillotina.interfaces import IAsyncContainer -from guillotina.interfaces import IResourceSerializeToJsonSummary +from guillotina.interfaces import IAsyncContainer, IResourceSerializeToJsonSummary from guillotina.interfaces.catalog import ICatalogUtility from guillotina.utils import navigate_to -import typing as t - class ListGroupsOrUsersService(Service): type_name: t.Optional[str] = None diff --git a/guillotina/contrib/dbusers/subscribers.py b/guillotina/contrib/dbusers/subscribers.py index 5886d51aa..8e894f3de 100644 --- a/guillotina/contrib/dbusers/subscribers.py +++ b/guillotina/contrib/dbusers/subscribers.py @@ -1,20 +1,20 @@ -from .content.groups import Group -from .content.users import User from guillotina import configure from guillotina.auth.validators import hash_password from guillotina.contrib.dbusers.content.groups import IGroup from guillotina.contrib.dbusers.content.users import IUser from guillotina.event import notify -from guillotina.events import BeforeObjectModifiedEvent -from guillotina.events import NewUserAdded -from guillotina.events import ObjectAddedEvent -from guillotina.interfaces import IBeforeObjectModifiedEvent -from guillotina.interfaces import IBeforeObjectRemovedEvent -from guillotina.interfaces import IObjectAddedEvent -from guillotina.interfaces import IPrincipalRoleManager +from guillotina.events import BeforeObjectModifiedEvent, NewUserAdded, ObjectAddedEvent +from guillotina.interfaces import ( + IBeforeObjectModifiedEvent, + IBeforeObjectRemovedEvent, + IObjectAddedEvent, + IPrincipalRoleManager, +) from guillotina.response import HTTPPreconditionFailed -from guillotina.utils import get_current_container -from guillotina.utils import navigate_to +from guillotina.utils import get_current_container, navigate_to + +from .content.groups import Group +from .content.users import User @configure.subscriber(for_=(IUser, IObjectAddedEvent)) diff --git a/guillotina/contrib/dbusers/users.py b/guillotina/contrib/dbusers/users.py index 5de8c8678..33c483d9a 100644 --- a/guillotina/contrib/dbusers/users.py +++ b/guillotina/contrib/dbusers/users.py @@ -1,15 +1,14 @@ -from .services.utils import NoCatalogException +import typing + from guillotina.component import query_utility from guillotina.contrib.catalog.pg.utility import PGSearchUtility -from guillotina.exceptions import ContainerNotFound -from guillotina.exceptions import TransactionNotFound +from guillotina.exceptions import ContainerNotFound, TransactionNotFound from guillotina.interfaces import IPrincipal from guillotina.interfaces.catalog import ICatalogUtility from guillotina.transactions import get_transaction -from guillotina.utils import get_current_container -from guillotina.utils import navigate_to +from guillotina.utils import get_current_container, navigate_to -import typing +from .services.utils import NoCatalogException class DBUserIdentifier: diff --git a/guillotina/contrib/dyncontent/__init__.py b/guillotina/contrib/dyncontent/__init__.py index be7f093aa..1ee75244d 100644 --- a/guillotina/contrib/dyncontent/__init__.py +++ b/guillotina/contrib/dyncontent/__init__.py @@ -1,6 +1,7 @@ +from typing import Any, Dict + from guillotina import configure -from typing import Any -from typing import Dict + app_settings: Dict[str, Any] = {} diff --git a/guillotina/contrib/dyncontent/subscriber.py b/guillotina/contrib/dyncontent/subscriber.py index 7b0ce8a7f..868aec0e4 100644 --- a/guillotina/contrib/dyncontent/subscriber.py +++ b/guillotina/contrib/dyncontent/subscriber.py @@ -1,33 +1,21 @@ -from guillotina import app_settings -from guillotina import BEHAVIOR_CACHE -from guillotina import configure -from guillotina import FACTORY_CACHE -from guillotina.component import get_global_components -from guillotina.component import get_utility -from guillotina.component import query_utility -from guillotina.content import get_cached_factory -from guillotina.content import load_cached_schema -from guillotina.contrib.dyncontent import behaviors -from guillotina.contrib.dyncontent import contents -from guillotina.contrib.dyncontent.vocabularies import AppSettingSource -from guillotina.directives import index_field -from guillotina.directives import metadata -from guillotina.directives import read_permission -from guillotina.directives import write_permission -from guillotina.interfaces import IApplication -from guillotina.interfaces import IApplicationInitializedEvent -from guillotina.interfaces import IBehavior -from guillotina.interfaces import IResourceFactory -from guillotina.schema.vocabulary import SimpleTerm -from guillotina.schema.vocabulary import SimpleVocabulary -from guillotina.utils import import_class -from zope.interface import Interface -from zope.interface.interface import InterfaceClass - import json import logging import typing +from zope.interface import Interface +from zope.interface.interface import InterfaceClass + +from guillotina import BEHAVIOR_CACHE, FACTORY_CACHE, app_settings, configure +from guillotina.component import get_global_components, get_utility, query_utility +from guillotina.content import get_cached_factory, load_cached_schema +from guillotina.contrib.dyncontent import behaviors, contents +from guillotina.contrib.dyncontent.vocabularies import AppSettingSource +from guillotina.directives import index_field, metadata, read_permission, write_permission +from guillotina.interfaces import IApplication, IApplicationInitializedEvent, IBehavior, IResourceFactory +from guillotina.schema.vocabulary import SimpleTerm, SimpleVocabulary +from guillotina.utils import import_class + + SUPPORTED_DIRECTIVES = { "index": index_field, "read_permission": read_permission, diff --git a/guillotina/contrib/dyncontent/vocabularies.py b/guillotina/contrib/dyncontent/vocabularies.py index 45a492b98..74b19c935 100644 --- a/guillotina/contrib/dyncontent/vocabularies.py +++ b/guillotina/contrib/dyncontent/vocabularies.py @@ -1,8 +1,8 @@ -from guillotina import app_settings -from guillotina import configure +from zope.interface import implementer + +from guillotina import app_settings, configure from guillotina.contrib.dyncontent.exceptions import VocabularySettingNotFound from guillotina.schema.interfaces import ISource -from zope.interface import implementer @implementer(ISource) diff --git a/guillotina/contrib/email_validation/__init__.py b/guillotina/contrib/email_validation/__init__.py index 6bf7fdcbe..7bf6f73f5 100644 --- a/guillotina/contrib/email_validation/__init__.py +++ b/guillotina/contrib/email_validation/__init__.py @@ -1,5 +1,6 @@ from guillotina import configure + app_settings = { "applications": ["guillotina.contrib.templates"], "load_utilities": { diff --git a/guillotina/contrib/email_validation/interfaces.py b/guillotina/contrib/email_validation/interfaces.py index 14764dbc5..05dee89d9 100644 --- a/guillotina/contrib/email_validation/interfaces.py +++ b/guillotina/contrib/email_validation/interfaces.py @@ -1,6 +1,7 @@ -from guillotina import schema from zope.interface import Interface +from guillotina import schema + class IValidationSettings(Interface): diff --git a/guillotina/contrib/email_validation/utility.py b/guillotina/contrib/email_validation/utility.py index 72377f387..329ccafbd 100644 --- a/guillotina/contrib/email_validation/utility.py +++ b/guillotina/contrib/email_validation/utility.py @@ -1,22 +1,24 @@ +import logging + +from jsonschema import validate as jsonvalidate +from jsonschema.exceptions import ValidationError + from guillotina import app_settings from guillotina.component import get_utility from guillotina.contrib.email_validation.interfaces import IValidationSettings -from guillotina.contrib.email_validation.utils import extract_validation_token -from guillotina.contrib.email_validation.utils import generate_validation_token +from guillotina.contrib.email_validation.utils import extract_validation_token, generate_validation_token from guillotina.contrib.templates.interfaces import IJinjaUtility from guillotina.event import notify from guillotina.events import ValidationEvent from guillotina.interfaces import IMailer -from guillotina.response import HTTPNotImplemented -from guillotina.response import HTTPPreconditionFailed -from guillotina.response import HTTPServiceUnavailable -from guillotina.response import HTTPUnauthorized -from guillotina.utils import get_registry -from guillotina.utils import resolve_dotted_name -from jsonschema import validate as jsonvalidate -from jsonschema.exceptions import ValidationError +from guillotina.response import ( + HTTPNotImplemented, + HTTPPreconditionFailed, + HTTPServiceUnavailable, + HTTPUnauthorized, +) +from guillotina.utils import get_registry, resolve_dotted_name -import logging logger = logging.getLogger("guillotina.email_validation") diff --git a/guillotina/contrib/email_validation/utils.py b/guillotina/contrib/email_validation/utils.py index 6452520b9..91ca9f868 100644 --- a/guillotina/contrib/email_validation/utils.py +++ b/guillotina/contrib/email_validation/utils.py @@ -1,13 +1,15 @@ +import logging +import time from datetime import datetime -from guillotina import app_settings -from guillotina.utils import get_jwk_key -from jwcrypto import jwe -from jwcrypto.common import json_encode -import logging import orjson import pytz -import time +from jwcrypto import jwe +from jwcrypto.common import json_encode + +from guillotina import app_settings +from guillotina.utils import get_jwk_key + logger = logging.getLogger("guillotina.email_validation") diff --git a/guillotina/contrib/image/api.py b/guillotina/contrib/image/api.py index 1dd1ac3d4..79fc3f620 100644 --- a/guillotina/contrib/image/api.py +++ b/guillotina/contrib/image/api.py @@ -1,7 +1,8 @@ from functools import partial +from io import BytesIO + from guillotina import configure -from guillotina.api.files import _traversed_file_doc -from guillotina.api.files import DownloadFile +from guillotina.api.files import DownloadFile, _traversed_file_doc from guillotina.api.service import TraversableFieldService from guillotina.component import get_multi_adapter from guillotina.contrib.image.interfaces import IImagingSettings @@ -11,12 +12,10 @@ from guillotina.events import ObjectModifiedEvent from guillotina.interfaces import IFileManager from guillotina.interfaces.content import IResource -from guillotina.response import HTTPNoContent -from guillotina.response import HTTPNotFound +from guillotina.response import HTTPNoContent, HTTPNotFound from guillotina.schema.interfaces import IOrderedDict -from guillotina.utils import get_registry -from guillotina.utils import run_async -from io import BytesIO +from guillotina.utils import get_registry, run_async + BUFFER = 262144 diff --git a/guillotina/contrib/image/behaviors.py b/guillotina/contrib/image/behaviors.py index 57efcaa67..d504eec50 100644 --- a/guillotina/contrib/image/behaviors.py +++ b/guillotina/contrib/image/behaviors.py @@ -1,10 +1,10 @@ from collections import OrderedDict as NativeOrderedDict + +from zope.interface import Interface + from guillotina import configure from guillotina.contrib.image.image import CloudImageFileField -from guillotina.schema import Dict -from guillotina.schema import OrderedDict -from guillotina.schema import TextLine -from zope.interface import Interface +from guillotina.schema import Dict, OrderedDict, TextLine class IImageAttachmentMarker(Interface): diff --git a/guillotina/contrib/image/image.py b/guillotina/contrib/image/image.py index a6f904120..c1941869a 100644 --- a/guillotina/contrib/image/image.py +++ b/guillotina/contrib/image/image.py @@ -1,7 +1,8 @@ +from zope.interface import implementer + from guillotina.contrib.image.interfaces import IImageFile from guillotina.interfaces.files import ICloudFileField from guillotina.schema import Object -from zope.interface import implementer class ICloudImageFileField(ICloudFileField): diff --git a/guillotina/contrib/image/interfaces.py b/guillotina/contrib/image/interfaces.py index 822b4dabe..848acc3aa 100644 --- a/guillotina/contrib/image/interfaces.py +++ b/guillotina/contrib/image/interfaces.py @@ -1,7 +1,8 @@ +from zope.interface import Interface + from guillotina import schema from guillotina.contrib.image.preview import CloudPreviewImageFileField from guillotina.interfaces import IFile -from zope.interface import Interface class IImageFile(IFile): diff --git a/guillotina/contrib/image/preview.py b/guillotina/contrib/image/preview.py index b73e99f17..3d429780a 100644 --- a/guillotina/contrib/image/preview.py +++ b/guillotina/contrib/image/preview.py @@ -1,8 +1,8 @@ -from guillotina.interfaces.files import ICloudFileField -from guillotina.interfaces.files import IFileField -from guillotina.schema import Object from zope.interface import implementer +from guillotina.interfaces.files import ICloudFileField, IFileField +from guillotina.schema import Object + class ICloudPreviewImageFileField(ICloudFileField): """Preview on the cloud file""" diff --git a/guillotina/contrib/image/scale.py b/guillotina/contrib/image/scale.py index 89f8a38c5..93247dfdc 100644 --- a/guillotina/contrib/image/scale.py +++ b/guillotina/contrib/image/scale.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- # Code from plone.scale +import math +import sys +import warnings from io import BytesIO -import math import PIL.Image import PIL.ImageFile -import sys -import warnings def none_as_int(the_int): diff --git a/guillotina/contrib/mailer/__init__.py b/guillotina/contrib/mailer/__init__.py index 30bb7d277..25ae4e623 100644 --- a/guillotina/contrib/mailer/__init__.py +++ b/guillotina/contrib/mailer/__init__.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- +import logging + from guillotina import configure from guillotina.component import provide_utility from guillotina.interfaces import IMailer from guillotina.utils import import_class -import logging logger = logging.getLogger("guillotina.contrib.mailer") diff --git a/guillotina/contrib/mailer/encoding.py b/guillotina/contrib/mailer/encoding.py index d4f4aeafd..215dd10b0 100644 --- a/guillotina/contrib/mailer/encoding.py +++ b/guillotina/contrib/mailer/encoding.py @@ -1,6 +1,6 @@ # pulled out of repoze.sendmail -from email import header -from email import utils +from email import header, utils + # From http://tools.ietf.org/html/rfc5322#section-3.6 ADDR_HEADERS = ( diff --git a/guillotina/contrib/mailer/utility.py b/guillotina/contrib/mailer/utility.py index 26ea4b966..46a2c6627 100644 --- a/guillotina/contrib/mailer/utility.py +++ b/guillotina/contrib/mailer/utility.py @@ -1,26 +1,22 @@ # -*- coding: utf-8 -*- +import asyncio +import logging +import socket +import time from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.utils import formatdate -from guillotina import app_settings -from guillotina import configure +from typing import Any, List, Optional, Union + +from zope.interface import implementer + +from guillotina import app_settings, configure from guillotina.component import query_utility from guillotina.contrib.mailer import encoding from guillotina.contrib.mailer.exceptions import NoEndpointDefinedException -from guillotina.interfaces import IMailEndpoint -from guillotina.interfaces import IMailer -from guillotina.utils import get_random_string -from guillotina.utils import notice_on_error -from typing import Any -from typing import List -from typing import Optional -from typing import Union -from zope.interface import implementer +from guillotina.interfaces import IMailEndpoint, IMailer +from guillotina.utils import get_random_string, notice_on_error -import asyncio -import logging -import socket -import time try: import aiosmtplib diff --git a/guillotina/contrib/mcp/__init__.py b/guillotina/contrib/mcp/__init__.py index 6b0a46bcb..26dfdfb30 100644 --- a/guillotina/contrib/mcp/__init__.py +++ b/guillotina/contrib/mcp/__init__.py @@ -1,5 +1,6 @@ from guillotina import configure + app_settings = { "mcp": { "enabled": True, diff --git a/guillotina/contrib/mcp/backend.py b/guillotina/contrib/mcp/backend.py index 77b27283f..36a1cc9b2 100644 --- a/guillotina/contrib/mcp/backend.py +++ b/guillotina/contrib/mcp/backend.py @@ -1,18 +1,14 @@ +import hashlib +import json +import logging from dataclasses import dataclass +from typing import Any, Awaitable, Callable, Dict, List, Optional + from guillotina import app_settings from guillotina.contrib.mcp import resources as mcp_resources from guillotina.contrib.mcp import tools from guillotina.contrib.redis import get_driver -from typing import Any -from typing import Awaitable -from typing import Callable -from typing import Dict -from typing import List -from typing import Optional -import hashlib -import json -import logging ToolHandler = Callable[[Any, Any, Dict[str, Any]], Awaitable[Dict[str, Any]]] ResourceHandler = Callable[[Any], Awaitable[Dict[str, Any]]] diff --git a/guillotina/contrib/mcp/interfaces.py b/guillotina/contrib/mcp/interfaces.py index 3970d93b6..957f61cbc 100644 --- a/guillotina/contrib/mcp/interfaces.py +++ b/guillotina/contrib/mcp/interfaces.py @@ -1,6 +1,7 @@ -from guillotina import schema from zope.interface import Interface +from guillotina import schema + class IMCPSettings(Interface): enabled = schema.Bool(title="Enable MCP services", default=True, required=False) diff --git a/guillotina/contrib/mcp/permissions.py b/guillotina/contrib/mcp/permissions.py index ddc9376d5..9e0d23880 100644 --- a/guillotina/contrib/mcp/permissions.py +++ b/guillotina/contrib/mcp/permissions.py @@ -1,5 +1,6 @@ from guillotina import configure + configure.permission("guillotina.MCPView", "View MCP integration services") configure.permission("guillotina.MCPExecute", "Execute MCP tools") diff --git a/guillotina/contrib/mcp/resources.py b/guillotina/contrib/mcp/resources.py index 3df23948e..059ecc970 100644 --- a/guillotina/contrib/mcp/resources.py +++ b/guillotina/contrib/mcp/resources.py @@ -1,13 +1,10 @@ -from guillotina import __version__ -from guillotina import app_settings +from typing import Any, Dict + +from guillotina import __version__, app_settings from guillotina.component import query_utility from guillotina.interfaces.catalog import ICatalogUtility from guillotina.transactions import get_transaction -from guillotina.utils import get_content_path -from guillotina.utils import get_current_container -from guillotina.utils import navigate_to -from typing import Any -from typing import Dict +from guillotina.utils import get_content_path, get_current_container, navigate_to async def mcp_info_resource(request) -> Dict[str, Any]: diff --git a/guillotina/contrib/mcp/server.py b/guillotina/contrib/mcp/server.py index 55d0dbb4a..32bc5da70 100644 --- a/guillotina/contrib/mcp/server.py +++ b/guillotina/contrib/mcp/server.py @@ -1,9 +1,7 @@ -from typing import Any -from urllib.parse import parse_qs -from urllib.parse import urlparse - import importlib import json +from typing import Any +from urllib.parse import parse_qs, urlparse class _RequestWithUriParams: diff --git a/guillotina/contrib/mcp/services.py b/guillotina/contrib/mcp/services.py index a28ad0069..40bc0c2db 100644 --- a/guillotina/contrib/mcp/services.py +++ b/guillotina/contrib/mcp/services.py @@ -3,9 +3,7 @@ from guillotina.component import query_utility from guillotina.contrib.mcp.interfaces import IMCPToolRegistry from guillotina.interfaces import IResource -from guillotina.response import HTTPNotFound -from guillotina.response import HTTPServiceUnavailable -from guillotina.response import Response +from guillotina.response import HTTPNotFound, HTTPServiceUnavailable, Response def _get_registry(): @@ -32,9 +30,8 @@ async def __call__(self): async def _handle_protocol(self): try: - from mcp.server.streamable_http import StreamableHTTPServerTransport - import anyio + from mcp.server.streamable_http import StreamableHTTPServerTransport except ImportError as exc: raise HTTPServiceUnavailable( content={"reason": 'MCP SDK missing. Install "guillotina[mcp]".'} diff --git a/guillotina/contrib/mcp/subscribers.py b/guillotina/contrib/mcp/subscribers.py index f96be8aa4..559e2df55 100644 --- a/guillotina/contrib/mcp/subscribers.py +++ b/guillotina/contrib/mcp/subscribers.py @@ -1,12 +1,14 @@ +import asyncio + from guillotina import configure from guillotina.component import query_utility from guillotina.contrib.mcp.interfaces import IMCPToolRegistry -from guillotina.interfaces import IBeforeObjectRemovedEvent -from guillotina.interfaces import IObjectAddedEvent -from guillotina.interfaces import IObjectModifiedEvent -from guillotina.interfaces import IResource - -import asyncio +from guillotina.interfaces import ( + IBeforeObjectRemovedEvent, + IObjectAddedEvent, + IObjectModifiedEvent, + IResource, +) @configure.subscriber(for_=(IResource, IObjectAddedEvent)) diff --git a/guillotina/contrib/mcp/tools.py b/guillotina/contrib/mcp/tools.py index 08fe821a4..93ec9ead4 100644 --- a/guillotina/contrib/mcp/tools.py +++ b/guillotina/contrib/mcp/tools.py @@ -1,22 +1,14 @@ +import functools +from typing import Any, Awaitable, Callable, Dict, List, Tuple + from guillotina.catalog.catalog import DefaultSearchUtility -from guillotina.component import query_multi_adapter -from guillotina.component import query_utility +from guillotina.component import query_multi_adapter, query_utility from guillotina.event import notify from guillotina.events import ObjectModifiedEvent -from guillotina.interfaces import IResourceSerializeToJson -from guillotina.interfaces import IResourceSerializeToJsonSummary +from guillotina.interfaces import IResourceSerializeToJson, IResourceSerializeToJsonSummary from guillotina.interfaces.catalog import ICatalogUtility -from guillotina.utils import get_content_path -from guillotina.utils import get_current_container -from guillotina.utils import navigate_to -from typing import Any -from typing import Awaitable -from typing import Callable -from typing import Dict -from typing import List -from typing import Tuple +from guillotina.utils import get_content_path, get_current_container, navigate_to -import functools ToolHandler = Callable[[Any, Any, Dict[str, Any]], Awaitable[Dict[str, Any]]] diff --git a/guillotina/contrib/memcached/__init__.py b/guillotina/contrib/memcached/__init__.py index 85cca227d..ffec765ac 100644 --- a/guillotina/contrib/memcached/__init__.py +++ b/guillotina/contrib/memcached/__init__.py @@ -1,7 +1,8 @@ from asyncio import get_running_loop +from typing import Any, Dict + from guillotina.contrib.memcached.driver import MemcachedDriver -from typing import Any -from typing import Dict + _driver = None diff --git a/guillotina/contrib/memcached/driver.py b/guillotina/contrib/memcached/driver.py index 92056d798..83f5d7313 100644 --- a/guillotina/contrib/memcached/driver.py +++ b/guillotina/contrib/memcached/driver.py @@ -4,23 +4,20 @@ print("If you add guillotina.contrib.memcached you need to add emcache on your requirements") raise -from guillotina import app_settings -from guillotina import metrics -from guillotina.contrib.memcached.exceptions import NoMemcachedConfigured -from typing import Any -from typing import Dict -from typing import List -from typing import Optional - import asyncio -import backoff import hashlib import logging +from typing import Any, Dict, List, Optional -try: - from prometheus_client.utils import INF +import backoff +from guillotina import app_settings, metrics +from guillotina.contrib.memcached.exceptions import NoMemcachedConfigured + + +try: import prometheus_client + from prometheus_client.utils import INF _SEND_METRICS = True diff --git a/guillotina/contrib/pubsub/utility.py b/guillotina/contrib/pubsub/utility.py index fca0a07de..76a3fa219 100644 --- a/guillotina/contrib/pubsub/utility.py +++ b/guillotina/contrib/pubsub/utility.py @@ -1,13 +1,14 @@ +import asyncio +import logging +import pickle +from typing import Any, Callable + +import backoff + from guillotina.contrib.pubsub.exceptions import NoPubSubDriver from guillotina.profile import profilable from guillotina.utils import resolve_dotted_name -from typing import Any -from typing import Callable -import asyncio -import backoff -import logging -import pickle logger = logging.getLogger("guillotina") diff --git a/guillotina/contrib/redis/__init__.py b/guillotina/contrib/redis/__init__.py index 07f5c16fe..3e94e3847 100644 --- a/guillotina/contrib/redis/__init__.py +++ b/guillotina/contrib/redis/__init__.py @@ -1,7 +1,9 @@ from asyncio import get_running_loop + from guillotina import configure from guillotina.contrib.redis.driver import RedisDriver + _driver = None app_settings = { diff --git a/guillotina/contrib/redis/dm.py b/guillotina/contrib/redis/dm.py index b3fa860d0..55c45dd55 100644 --- a/guillotina/contrib/redis/dm.py +++ b/guillotina/contrib/redis/dm.py @@ -1,14 +1,14 @@ -from guillotina import configure -from guillotina import metrics +import time + +import orjson + +from guillotina import configure, metrics from guillotina.contrib.redis import get_driver from guillotina.files.adapter import DBDataManager -from guillotina.interfaces import IExternalFileStorageManager -from guillotina.interfaces import IUploadDataManager +from guillotina.interfaces import IExternalFileStorageManager, IUploadDataManager from guillotina.renderers import guillotina_json_default from guillotina.transactions import get_transaction -import orjson -import time try: import prometheus_client diff --git a/guillotina/contrib/redis/driver.py b/guillotina/contrib/redis/driver.py index c0c5d0ebf..9f2611b63 100644 --- a/guillotina/contrib/redis/driver.py +++ b/guillotina/contrib/redis/driver.py @@ -4,18 +4,17 @@ print("If you add guillotina.contrib.redis you need to add redis>4.2.0rc1 on your requirements") raise -from guillotina import app_settings -from guillotina import metrics -from guillotina.contrib.redis.exceptions import NoRedisConfigured +import asyncio +import logging +from typing import Dict, List, Optional + +import backoff from redis.asyncio.client import PubSub from redis.exceptions import ConnectionError -from typing import Dict -from typing import List -from typing import Optional -import asyncio -import backoff -import logging +from guillotina import app_settings, metrics +from guillotina.contrib.redis.exceptions import NoRedisConfigured + try: import prometheus_client diff --git a/guillotina/contrib/redis_session/utility.py b/guillotina/contrib/redis_session/utility.py index 41fd71ada..9e3cc10c0 100644 --- a/guillotina/contrib/redis_session/utility.py +++ b/guillotina/contrib/redis_session/utility.py @@ -1,9 +1,10 @@ -from guillotina import app_settings - import asyncio import logging import uuid +from guillotina import app_settings + + logger = logging.getLogger("guillotina") diff --git a/guillotina/contrib/swagger/__init__.py b/guillotina/contrib/swagger/__init__.py index 98ff8ccca..1f67ac9d6 100644 --- a/guillotina/contrib/swagger/__init__.py +++ b/guillotina/contrib/swagger/__init__.py @@ -1,5 +1,6 @@ from guillotina import configure + configure.permission("guillotina.swagger.View", "View swagger definition") configure.grant(permission="guillotina.swagger.View", role="guillotina.Anonymous") configure.grant(permission="guillotina.swagger.View", role="guillotina.Authenticated") diff --git a/guillotina/contrib/swagger/services.py b/guillotina/contrib/swagger/services.py index 56554b981..c33e23ee8 100644 --- a/guillotina/contrib/swagger/services.py +++ b/guillotina/contrib/swagger/services.py @@ -1,19 +1,22 @@ -from guillotina import app_settings -from guillotina import configure -from guillotina.api.service import Service -from guillotina.utils import get_authenticated_user -from guillotina.utils import get_full_content_path -from guillotina.utils import get_request_scheme -from guillotina.utils import get_security_policy -from guillotina.utils import get_url -from guillotina.utils import resolve_dotted_name -from urllib.parse import urlparse -from zope.interface import Interface - import copy import json import os +from urllib.parse import urlparse + import pkg_resources +from zope.interface import Interface + +from guillotina import app_settings, configure +from guillotina.api.service import Service +from guillotina.utils import ( + get_authenticated_user, + get_full_content_path, + get_request_scheme, + get_security_policy, + get_url, + resolve_dotted_name, +) + here = os.path.dirname(os.path.realpath(__file__)) diff --git a/guillotina/contrib/templates/__init__.py b/guillotina/contrib/templates/__init__.py index e33324842..89558de34 100644 --- a/guillotina/contrib/templates/__init__.py +++ b/guillotina/contrib/templates/__init__.py @@ -1,5 +1,6 @@ from guillotina import configure + app_settings = { "load_utilities": { "template": { diff --git a/guillotina/contrib/templates/interfaces.py b/guillotina/contrib/templates/interfaces.py index 4db7dda9b..00ce46a95 100644 --- a/guillotina/contrib/templates/interfaces.py +++ b/guillotina/contrib/templates/interfaces.py @@ -1,6 +1,5 @@ from guillotina import schema -from guillotina.interfaces import IAsyncUtility -from guillotina.interfaces import IItem +from guillotina.interfaces import IAsyncUtility, IItem class IJinjaUtility(IAsyncUtility): diff --git a/guillotina/contrib/templates/permissions.py b/guillotina/contrib/templates/permissions.py index 4fa64251a..4989c4830 100644 --- a/guillotina/contrib/templates/permissions.py +++ b/guillotina/contrib/templates/permissions.py @@ -1,5 +1,6 @@ from guillotina import configure + configure.permission("guillotina.AddJinjaTemplate", title="Add Jinja template") configure.grant(permission="guillotina.AddJinjaTemplate", role="guillotina.Manager") diff --git a/guillotina/contrib/templates/utility.py b/guillotina/contrib/templates/utility.py index 54dccd1a6..cdce63eee 100644 --- a/guillotina/contrib/templates/utility.py +++ b/guillotina/contrib/templates/utility.py @@ -1,17 +1,15 @@ +import logging from concurrent.futures import ThreadPoolExecutor from functools import partial -from guillotina import app_settings -from guillotina.contrib.templates.interfaces import IJinjaTemplate -from guillotina.utils import get_current_container -from guillotina.utils import navigate_to -from jinja2 import BaseLoader -from jinja2 import Environment -from jinja2 import PackageLoader -from jinja2 import select_autoescape + +from jinja2 import BaseLoader, Environment, PackageLoader, select_autoescape from jinja2.exceptions import TemplateNotFound from lru import LRU -import logging +from guillotina import app_settings +from guillotina.contrib.templates.interfaces import IJinjaTemplate +from guillotina.utils import get_current_container, navigate_to + logger = logging.getLogger("guillotina") diff --git a/guillotina/contrib/vocabularies/countries.py b/guillotina/contrib/vocabularies/countries.py index f33b4b259..074a2bca5 100644 --- a/guillotina/contrib/vocabularies/countries.py +++ b/guillotina/contrib/vocabularies/countries.py @@ -1,5 +1,6 @@ from guillotina import configure + _countries = { "AF": "Afghanistan", "AN": "Netherlands Antilles", diff --git a/guillotina/contrib/vocabularies/languages.py b/guillotina/contrib/vocabularies/languages.py index da9c2d71d..15b86884e 100644 --- a/guillotina/contrib/vocabularies/languages.py +++ b/guillotina/contrib/vocabularies/languages.py @@ -1,5 +1,6 @@ from guillotina import configure + _languagelist = { "aa": {"native": "магIарул мацI", "name": "Afar"}, "ab": {"native": "бызшәа", "name": "Abkhazian"}, diff --git a/guillotina/contrib/workflows/__init__.py b/guillotina/contrib/workflows/__init__.py index 2d33bd15d..4aa066b85 100644 --- a/guillotina/contrib/workflows/__init__.py +++ b/guillotina/contrib/workflows/__init__.py @@ -1,10 +1,12 @@ -from guillotina import configure - import glob import logging import typing + import yaml +from guillotina import configure + + logger = logging.getLogger("guillotina.contrib.workflows") diff --git a/guillotina/contrib/workflows/api.py b/guillotina/contrib/workflows/api.py index c12e31da5..75001980f 100644 --- a/guillotina/contrib/workflows/api.py +++ b/guillotina/contrib/workflows/api.py @@ -1,10 +1,8 @@ from guillotina import configure from guillotina.api.service import Service from guillotina.component import query_adapter -from guillotina.contrib.workflows.interfaces import IWorkflow -from guillotina.contrib.workflows.interfaces import IWorkflowBehavior -from guillotina.interfaces import IAbsoluteURL -from guillotina.interfaces import IResource +from guillotina.contrib.workflows.interfaces import IWorkflow, IWorkflowBehavior +from guillotina.interfaces import IAbsoluteURL, IResource class Workflow(object): diff --git a/guillotina/contrib/workflows/events.py b/guillotina/contrib/workflows/events.py index 160140a1d..a28309c4c 100644 --- a/guillotina/contrib/workflows/events.py +++ b/guillotina/contrib/workflows/events.py @@ -1,6 +1,7 @@ +from zope.interface import implementer + from guillotina.contrib.workflows.interfaces import IWorkflowChangedEvent from guillotina.events import ObjectEvent -from zope.interface import implementer @implementer(IWorkflowChangedEvent) diff --git a/guillotina/contrib/workflows/interfaces.py b/guillotina/contrib/workflows/interfaces.py index d103f6b08..47bf536ce 100644 --- a/guillotina/contrib/workflows/interfaces.py +++ b/guillotina/contrib/workflows/interfaces.py @@ -1,16 +1,14 @@ +import json +from typing import Optional + +from zope.interface import Attribute, Interface, implementer, interfaces + from guillotina import schema from guillotina.component import query_adapter from guillotina.directives import index_field -from guillotina.interfaces import IAsyncUtility -from guillotina.interfaces import IResource +from guillotina.interfaces import IAsyncUtility, IResource from guillotina.schema.interfaces import IContextAwareDefaultFactory -from typing import Optional -from zope.interface import Attribute -from zope.interface import implementer -from zope.interface import Interface -from zope.interface import interfaces -import json HISTORY_SCHEMA = json.dumps( { diff --git a/guillotina/contrib/workflows/permissions.py b/guillotina/contrib/workflows/permissions.py index 7a211043e..71ee692b5 100644 --- a/guillotina/contrib/workflows/permissions.py +++ b/guillotina/contrib/workflows/permissions.py @@ -1,5 +1,6 @@ from guillotina import configure + configure.permission("guillotina.ReviewContent", "Review content permission") configure.permission("guillotina.RequestReview", "Request review content permission") diff --git a/guillotina/contrib/workflows/post_serialize.py b/guillotina/contrib/workflows/post_serialize.py index 2667bf850..8b3623017 100644 --- a/guillotina/contrib/workflows/post_serialize.py +++ b/guillotina/contrib/workflows/post_serialize.py @@ -1,7 +1,8 @@ +from typing import Any + from guillotina.component import query_adapter from guillotina.contrib.workflows.interfaces import IWorkflowBehavior from guillotina.interfaces import IResource -from typing import Any def apply_review(context: IResource, result: Any): diff --git a/guillotina/contrib/workflows/subscriber.py b/guillotina/contrib/workflows/subscriber.py index a9f4c680f..46323c2fb 100644 --- a/guillotina/contrib/workflows/subscriber.py +++ b/guillotina/contrib/workflows/subscriber.py @@ -1,14 +1,12 @@ +import datetime + from guillotina import configure from guillotina.component import query_adapter -from guillotina.contrib.workflows.interfaces import IWorkflow -from guillotina.contrib.workflows.interfaces import IWorkflowBehavior -from guillotina.interfaces import IObjectAddedEvent -from guillotina.interfaces import IResource +from guillotina.contrib.workflows.interfaces import IWorkflow, IWorkflowBehavior +from guillotina.interfaces import IObjectAddedEvent, IResource from guillotina.security.utils import apply_sharing from guillotina.utils import get_authenticated_user_id -import datetime - @configure.subscriber(for_=(IResource, IObjectAddedEvent), priority=1001) # after indexing async def workflow_object_added(obj, event): diff --git a/guillotina/contrib/workflows/utility.py b/guillotina/contrib/workflows/utility.py index 96c8fc483..63aea4ff2 100644 --- a/guillotina/contrib/workflows/utility.py +++ b/guillotina/contrib/workflows/utility.py @@ -1,22 +1,15 @@ -from guillotina import app_settings -from guillotina import configure -from guillotina.component import provide_adapter -from guillotina.component import query_adapter +import datetime + +from guillotina import app_settings, configure +from guillotina.component import provide_adapter, query_adapter from guillotina.contrib.workflows import logger from guillotina.contrib.workflows.events import WorkflowChangedEvent -from guillotina.contrib.workflows.interfaces import IWorkflow -from guillotina.contrib.workflows.interfaces import IWorkflowBehavior -from guillotina.contrib.workflows.interfaces import IWorkflowUtility +from guillotina.contrib.workflows.interfaces import IWorkflow, IWorkflowBehavior, IWorkflowUtility from guillotina.event import notify from guillotina.events import ObjectModifiedEvent -from guillotina.response import HTTPPreconditionFailed -from guillotina.response import HTTPUnauthorized +from guillotina.response import HTTPPreconditionFailed, HTTPUnauthorized from guillotina.security.utils import apply_sharing -from guillotina.utils import get_authenticated_user_id -from guillotina.utils import get_security_policy -from guillotina.utils import import_class - -import datetime +from guillotina.utils import get_authenticated_user_id, get_security_policy, import_class def create_workflow_factory(proto_name, proto_definition): diff --git a/guillotina/contrib/workflows/vocabularies.py b/guillotina/contrib/workflows/vocabularies.py index a8bb97e74..8a1e0003f 100644 --- a/guillotina/contrib/workflows/vocabularies.py +++ b/guillotina/contrib/workflows/vocabularies.py @@ -1,8 +1,6 @@ from guillotina import configure -from guillotina.component import get_utility -from guillotina.component import query_adapter -from guillotina.contrib.workflows.interfaces import IWorkflow -from guillotina.contrib.workflows.interfaces import IWorkflowUtility +from guillotina.component import get_utility, query_adapter +from guillotina.contrib.workflows.interfaces import IWorkflow, IWorkflowUtility from guillotina.interfaces import IResource diff --git a/guillotina/cookiecutter/application/{{cookiecutter.package_name}}/setup.py b/guillotina/cookiecutter/application/{{cookiecutter.package_name}}/setup.py index 1165a919c..37e561f49 100644 --- a/guillotina/cookiecutter/application/{{cookiecutter.package_name}}/setup.py +++ b/guillotina/cookiecutter/application/{{cookiecutter.package_name}}/setup.py @@ -1,5 +1,5 @@ -from setuptools import find_packages -from setuptools import setup +from setuptools import find_packages, setup + try: README = open('README.rst').read() diff --git a/guillotina/cookiecutter/application/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/__init__.py b/guillotina/cookiecutter/application/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/__init__.py index 68a85ab23..18d4c2bcb 100644 --- a/guillotina/cookiecutter/application/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/__init__.py +++ b/guillotina/cookiecutter/application/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/__init__.py @@ -1,5 +1,6 @@ from guillotina import configure + app_settings = { # provide custom application settings here... } diff --git a/guillotina/cookiecutter/application/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/tests/fixtures.py b/guillotina/cookiecutter/application/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/tests/fixtures.py index f5fbb45e6..8fc6edf68 100644 --- a/guillotina/cookiecutter/application/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/tests/fixtures.py +++ b/guillotina/cookiecutter/application/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/tests/fixtures.py @@ -1,9 +1,10 @@ -from guillotina import testing -from guillotina.tests.fixtures import ContainerRequesterAsyncContextManager - import json + import pytest +from guillotina import testing +from guillotina.tests.fixtures import ContainerRequesterAsyncContextManager + def base_settings_configurator(settings): if 'applications' in settings: diff --git a/guillotina/cookiecutter/application/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/tests/test_install.py b/guillotina/cookiecutter/application/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/tests/test_install.py index 9c43e2414..6c520fa06 100644 --- a/guillotina/cookiecutter/application/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/tests/test_install.py +++ b/guillotina/cookiecutter/application/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/tests/test_install.py @@ -1,5 +1,6 @@ import pytest + pytestmark = [pytest.mark.asyncio] diff --git a/guillotina/cors.py b/guillotina/cors.py index ff7b0508a..f02884b94 100644 --- a/guillotina/cors.py +++ b/guillotina/cors.py @@ -1,9 +1,9 @@ -from guillotina import app_settings -from guillotina import glogging +import fnmatch + +from guillotina import app_settings, glogging from guillotina.interfaces import IRequest from guillotina.response import HTTPUnauthorized -import fnmatch logger = glogging.getLogger("guillotina") diff --git a/guillotina/db/cache/base.py b/guillotina/db/cache/base.py index 3ac35dfe7..a91a7dad0 100644 --- a/guillotina/db/cache/base.py +++ b/guillotina/db/cache/base.py @@ -1,10 +1,9 @@ +import typing +from typing import Any, Dict, List + from guillotina import glogging from guillotina.db.orm.interfaces import IBaseObject -from typing import Any -from typing import Dict -from typing import List -import typing logger = glogging.getLogger("guillotina") diff --git a/guillotina/db/cache/dummy.py b/guillotina/db/cache/dummy.py index a0f50f4b7..02aa17147 100644 --- a/guillotina/db/cache/dummy.py +++ b/guillotina/db/cache/dummy.py @@ -1,10 +1,8 @@ +from typing import Any, Dict, List + from guillotina import configure from guillotina.db.cache.base import BaseCache -from guillotina.db.interfaces import ITransaction -from guillotina.db.interfaces import ITransactionCache -from typing import Any -from typing import Dict -from typing import List +from guillotina.db.interfaces import ITransaction, ITransactionCache @configure.adapter(for_=ITransaction, provides=ITransactionCache, name="dummy") diff --git a/guillotina/db/db.py b/guillotina/db/db.py index 99eed3351..8be39112a 100644 --- a/guillotina/db/db.py +++ b/guillotina/db/db.py @@ -1,7 +1,8 @@ +from zope.interface import implementer_only + from guillotina.content import Folder from guillotina.db.orm.interfaces import IBaseObject from guillotina.interfaces import IDatabase -from zope.interface import implementer_only @implementer_only(IDatabase, IBaseObject) diff --git a/guillotina/db/events.py b/guillotina/db/events.py index 1600180c1..6b6b81ef2 100644 --- a/guillotina/db/events.py +++ b/guillotina/db/events.py @@ -1,6 +1,7 @@ -from guillotina.db.interfaces import IStorageCreatedEvent from zope.interface import implementer +from guillotina.db.interfaces import IStorageCreatedEvent + @implementer(IStorageCreatedEvent) class StorageCreatedEvent: diff --git a/guillotina/db/factory.py b/guillotina/db/factory.py index 973da7d4c..af8650f8f 100644 --- a/guillotina/db/factory.py +++ b/guillotina/db/factory.py @@ -1,24 +1,21 @@ +import string from copy import deepcopy +from typing import List + +import asyncpg + from guillotina import configure from guillotina.component import get_utility from guillotina.db.interfaces import IDatabaseManager from guillotina.db.storages.cockroach import CockroachStorage -from guillotina.db.storages.dummy import DummyFileStorage -from guillotina.db.storages.dummy import DummyStorage +from guillotina.db.storages.dummy import DummyFileStorage, DummyStorage from guillotina.db.storages.pg import PostgresqlStorage from guillotina.db.transaction_manager import TransactionManager from guillotina.event import notify from guillotina.events import DatabaseInitializedEvent from guillotina.factory.content import Database -from guillotina.interfaces import IApplication -from guillotina.interfaces import IDatabase -from guillotina.interfaces import IDatabaseConfigurationFactory -from guillotina.utils import apply_coroutine -from guillotina.utils import resolve_dotted_name -from typing import List - -import asyncpg -import string +from guillotina.interfaces import IApplication, IDatabase, IDatabaseConfigurationFactory +from guillotina.utils import apply_coroutine, resolve_dotted_name def _get_connection_options(dbconfig): diff --git a/guillotina/db/interfaces.py b/guillotina/db/interfaces.py index 294e4d87e..ffa45e443 100644 --- a/guillotina/db/interfaces.py +++ b/guillotina/db/interfaces.py @@ -1,13 +1,11 @@ -from guillotina.db.orm.interfaces import IBaseObject -from guillotina.interfaces import ICatalogDataAdapter -from guillotina.interfaces import IDatabase -from zope.interface import Attribute -from zope.interface import Interface -from zope.interface import interfaces - import asyncio import typing +from zope.interface import Attribute, Interface, interfaces + +from guillotina.db.orm.interfaces import IBaseObject +from guillotina.interfaces import ICatalogDataAdapter, IDatabase + class IPartition(Interface): """Get the partition of the object""" diff --git a/guillotina/db/orm/base.py b/guillotina/db/orm/base.py index 841cc29b7..ed709a532 100644 --- a/guillotina/db/orm/base.py +++ b/guillotina/db/orm/base.py @@ -1,16 +1,14 @@ +import weakref +from typing import Any, Dict, Generic, Optional, TypeVar + +from zope.interface import implementer + from guillotina.db.interfaces import ITransaction from guillotina.db.orm.interfaces import IBaseObject from guillotina.exceptions import TransactionNotFound from guillotina.profile import profilable from guillotina.transactions import get_transaction -from typing import Any -from typing import Dict -from typing import Generic -from typing import Optional -from typing import TypeVar -from zope.interface import implementer -import weakref T = TypeVar("T") diff --git a/guillotina/db/orm/interfaces.py b/guillotina/db/orm/interfaces.py index e480124a5..ffa0301cb 100644 --- a/guillotina/db/orm/interfaces.py +++ b/guillotina/db/orm/interfaces.py @@ -1,6 +1,6 @@ # -*- encoding: utf-8 -*- -from zope.interface import Attribute -from zope.interface import Interface +from zope.interface import Attribute, Interface + OID_TYPE = SERIAL_TYPE = bytes diff --git a/guillotina/db/reader.py b/guillotina/db/reader.py index 0e2d55f20..0c1da3c4d 100644 --- a/guillotina/db/reader.py +++ b/guillotina/db/reader.py @@ -1,8 +1,8 @@ -from guillotina.db.orm.interfaces import IBaseObject - import pickle import typing +from guillotina.db.orm.interfaces import IBaseObject + def reader(result: dict) -> IBaseObject: obj = typing.cast(IBaseObject, pickle.loads(result["state"])) diff --git a/guillotina/db/storages/cockroach.py b/guillotina/db/storages/cockroach.py index 760f61916..7b8ad4374 100644 --- a/guillotina/db/storages/cockroach.py +++ b/guillotina/db/storages/cockroach.py @@ -1,19 +1,23 @@ +import uuid + +import asyncpg +from zope.interface import implementer + from guillotina import glogging from guillotina.const import TRASHED_ID from guillotina.db.interfaces import ICockroachStorage from guillotina.db.storages import pg from guillotina.db.storages.utils import register_sql from guillotina.db.uid import MAX_UID_LENGTH -from guillotina.exceptions import ConflictError -from guillotina.exceptions import ConflictIdOnContainer -from guillotina.exceptions import RequestNotFound -from guillotina.exceptions import RestartCommit -from guillotina.exceptions import TIDConflictError +from guillotina.exceptions import ( + ConflictError, + ConflictIdOnContainer, + RequestNotFound, + RestartCommit, + TIDConflictError, +) from guillotina.utils import get_current_request -from zope.interface import implementer -import asyncpg -import uuid logger = glogging.getLogger("guillotina") diff --git a/guillotina/db/storages/dummy.py b/guillotina/db/storages/dummy.py index 877152c51..f74c59608 100644 --- a/guillotina/db/storages/dummy.py +++ b/guillotina/db/storages/dummy.py @@ -1,13 +1,15 @@ -from guillotina.db.interfaces import IStorage -from guillotina.db.storages.base import BaseStorage -from guillotina.exceptions import ConflictIdOnContainer -from zope.interface import implementer - import asyncio import logging import os import pickle +from zope.interface import implementer + +from guillotina.db.interfaces import IStorage +from guillotina.db.storages.base import BaseStorage +from guillotina.exceptions import ConflictIdOnContainer + + logger = logging.getLogger("guillotina") diff --git a/guillotina/db/storages/pg.py b/guillotina/db/storages/pg.py index 7e156f11b..4e8dc894a 100644 --- a/guillotina/db/storages/pg.py +++ b/guillotina/db/storages/pg.py @@ -1,31 +1,26 @@ +import asyncio +import concurrent +import time from asyncio import shield -from contextlib import asynccontextmanager -from contextlib import contextmanager -from guillotina import glogging -from guillotina import metrics +from contextlib import asynccontextmanager, contextmanager + +import asyncpg +import asyncpg.connection +import orjson +from zope.interface import implementer + +from guillotina import glogging, metrics from guillotina._settings import app_settings from guillotina.const import TRASHED_ID from guillotina.db.events import StorageCreatedEvent from guillotina.db.interfaces import IPostgresStorage from guillotina.db.storages.base import BaseStorage -from guillotina.db.storages.utils import clear_table_name -from guillotina.db.storages.utils import get_table_definition -from guillotina.db.storages.utils import register_sql -from guillotina.db.storages.utils import SQLStatements +from guillotina.db.storages.utils import SQLStatements, clear_table_name, get_table_definition, register_sql from guillotina.db.uid import MAX_UID_LENGTH from guillotina.event import notify -from guillotina.exceptions import ConflictError -from guillotina.exceptions import ConflictIdOnContainer -from guillotina.exceptions import TIDConflictError +from guillotina.exceptions import ConflictError, ConflictIdOnContainer, TIDConflictError from guillotina.profile import profilable -from zope.interface import implementer -import asyncio -import asyncpg -import asyncpg.connection -import concurrent -import orjson -import time try: import prometheus_client diff --git a/guillotina/db/storages/vacuum.py b/guillotina/db/storages/vacuum.py index 7e83aaa39..1066a7a39 100644 --- a/guillotina/db/storages/vacuum.py +++ b/guillotina/db/storages/vacuum.py @@ -1,13 +1,13 @@ +import asyncio +import logging + +import asyncpg.exceptions + from guillotina import configure from guillotina.const import TRASHED_ID -from guillotina.db.interfaces import ICockroachStorage -from guillotina.db.interfaces import IPostgresStorage -from guillotina.db.interfaces import IVacuumProvider +from guillotina.db.interfaces import ICockroachStorage, IPostgresStorage, IVacuumProvider from guillotina.db.storages.utils import register_sql -import asyncio -import asyncpg.exceptions -import logging logger = logging.getLogger("guillotina") diff --git a/guillotina/db/transaction.py b/guillotina/db/transaction.py index ea4b8b692..919ffa630 100644 --- a/guillotina/db/transaction.py +++ b/guillotina/db/transaction.py @@ -1,38 +1,34 @@ +import asyncio +import logging +import sys +import time +import warnings from collections import OrderedDict +from typing import Any, AsyncIterator, Callable, Dict, List, Optional, Union + +from typing_extensions import TypedDict +from zope.interface import implementer + from guillotina import task_vars from guillotina._settings import app_settings from guillotina.component import query_adapter from guillotina.const import ROOT_ID from guillotina.content import Container from guillotina.db.db import Root -from guillotina.db.interfaces import ITransaction -from guillotina.db.interfaces import ITransactionCache -from guillotina.db.interfaces import IWriter +from guillotina.db.interfaces import ITransaction, ITransactionCache, IWriter from guillotina.db.orm.interfaces import IBaseObject -from guillotina.exceptions import ConflictError -from guillotina.exceptions import ReadOnlyError -from guillotina.exceptions import RestartCommit -from guillotina.exceptions import TIDConflictError -from guillotina.exceptions import TransactionClosedException -from guillotina.exceptions import TransactionObjectRegistrationMismatchException +from guillotina.exceptions import ( + ConflictError, + ReadOnlyError, + RestartCommit, + TIDConflictError, + TransactionClosedException, + TransactionObjectRegistrationMismatchException, +) from guillotina.profile import profilable from guillotina.registry import Registry from guillotina.utils import lazy_apply -from typing import Any -from typing import AsyncIterator -from typing import Callable -from typing import Dict -from typing import List -from typing import Optional -from typing import Union -from typing_extensions import TypedDict -from zope.interface import implementer -import asyncio -import logging -import sys -import time -import warnings _EMPTY = "____" diff --git a/guillotina/db/transaction_manager.py b/guillotina/db/transaction_manager.py index 60795c286..f3a8072f4 100644 --- a/guillotina/db/transaction_manager.py +++ b/guillotina/db/transaction_manager.py @@ -1,24 +1,20 @@ +import asyncio +import typing from asyncio import shield -from guillotina import glogging -from guillotina import task_vars + +import asyncpg +from zope.interface import implementer + +from guillotina import glogging, task_vars from guillotina.db import ROOT_ID -from guillotina.db.interfaces import ITransaction -from guillotina.db.interfaces import ITransactionManager +from guillotina.db.interfaces import ITransaction, ITransactionManager from guillotina.db.orm.interfaces import IBaseObject -from guillotina.db.transaction import Status -from guillotina.db.transaction import Transaction -from guillotina.exceptions import ConflictError -from guillotina.exceptions import RequestNotFound -from guillotina.exceptions import TIDConflictError -from guillotina.exceptions import TransactionNotFound +from guillotina.db.transaction import Status, Transaction +from guillotina.exceptions import ConflictError, RequestNotFound, TIDConflictError, TransactionNotFound from guillotina.profile import profilable from guillotina.transactions import transaction from guillotina.utils import get_authenticated_user_id -from zope.interface import implementer -import asyncio -import asyncpg -import typing logger = glogging.getLogger("guillotina") diff --git a/guillotina/db/uid.py b/guillotina/db/uid.py index 594da2b76..db2e6a0b4 100644 --- a/guillotina/db/uid.py +++ b/guillotina/db/uid.py @@ -1,5 +1,6 @@ import uuid + MAX_UID_LENGTH = 64 UID_SPLIT_LENGTH = 3 UUID_LENGTH = 32 diff --git a/guillotina/db/writer.py b/guillotina/db/writer.py index 607184cba..718dd06b3 100644 --- a/guillotina/db/writer.py +++ b/guillotina/db/writer.py @@ -1,15 +1,13 @@ +import pickle + from guillotina import configure from guillotina._settings import app_settings from guillotina.catalog.catalog import DefaultCatalogDataAdapter from guillotina.component import query_adapter -from guillotina.db.interfaces import IJSONDBSerializer -from guillotina.db.interfaces import IWriter +from guillotina.db.interfaces import IJSONDBSerializer, IWriter from guillotina.db.orm.interfaces import IBaseObject from guillotina.interfaces import IResource -from guillotina.utils import find_container -from guillotina.utils import get_dotted_name - -import pickle +from guillotina.utils import find_container, get_dotted_name @configure.adapter(for_=IResource, provides=IJSONDBSerializer) diff --git a/guillotina/directives.py b/guillotina/directives.py index 56d60860f..5fe7f213e 100644 --- a/guillotina/directives.py +++ b/guillotina/directives.py @@ -1,8 +1,7 @@ +import sys from typing import Any -from zope.interface.interface import Element -from zope.interface.interface import TAGGED_DATA -import sys +from zope.interface.interface import TAGGED_DATA, Element class DirectiveClass(type): diff --git a/guillotina/documentation/__init__.py b/guillotina/documentation/__init__.py index 1d3470e23..60f1a643b 100644 --- a/guillotina/documentation/__init__.py +++ b/guillotina/documentation/__init__.py @@ -1,8 +1,8 @@ -from guillotina import configure -from guillotina import schema -from guillotina.addons import Addon from zope.interface import Interface +from guillotina import configure, schema +from guillotina.addons import Addon + class IRegistryData(Interface): foobar = schema.TextLine() diff --git a/guillotina/documentation/sphinx/__init__.py b/guillotina/documentation/sphinx/__init__.py index cdeef9745..69f8e73e6 100644 --- a/guillotina/documentation/sphinx/__init__.py +++ b/guillotina/documentation/sphinx/__init__.py @@ -1,9 +1,18 @@ # -*- coding: utf-8 -*- -from async_asgi_testclient import TestClient +import asyncio +import json +import logging from base64 import b64encode +from typing import Any, Dict, Optional + +import docutils.statemachine +import pkg_resources +from async_asgi_testclient import TestClient from docutils import nodes from docutils.parsers.rst import Directive # type: ignore from docutils.parsers.rst import directives # type: ignore +from zope.interface import Interface + from guillotina import routes from guillotina._settings import app_settings from guillotina.component import query_multi_adapter @@ -13,16 +22,7 @@ from guillotina.transactions import abort from guillotina.traversal import traverse from guillotina.utils import get_dotted_name -from typing import Any -from typing import Dict -from typing import Optional -from zope.interface import Interface -import asyncio -import docutils.statemachine -import json -import logging -import pkg_resources logger = logging.getLogger("guillotina.docs") diff --git a/guillotina/entrypoint.py b/guillotina/entrypoint.py index 8d79b64b8..5257982b4 100644 --- a/guillotina/entrypoint.py +++ b/guillotina/entrypoint.py @@ -1,6 +1,7 @@ +import os + from guillotina.factory import make_app -import os if "G_CONFIG_FILE" not in os.environ: raise Exception("You must provide the envar G_CONFIG_FILE") diff --git a/guillotina/event.py b/guillotina/event.py index 091aa5b05..afbc5118a 100644 --- a/guillotina/event.py +++ b/guillotina/event.py @@ -1,5 +1,4 @@ -from guillotina.component.event import async_subscribers -from guillotina.component.event import sync_subscribers +from guillotina.component.event import async_subscribers, sync_subscribers async def notify(event): diff --git a/guillotina/events.py b/guillotina/events.py index cc2f4934c..7738e5182 100644 --- a/guillotina/events.py +++ b/guillotina/events.py @@ -1,43 +1,46 @@ -from guillotina.component.interfaces import IObjectEvent -from guillotina.db.orm.interfaces import IBaseObject -from guillotina.interfaces import IAfterAsyncUtilityLoadedEvent -from guillotina.interfaces import IApplicationCleanupEvent -from guillotina.interfaces import IApplicationConfiguredEvent -from guillotina.interfaces import IApplicationEvent -from guillotina.interfaces import IApplicationInitializedEvent -from guillotina.interfaces import IBeforeAsyncUtilityLoadedEvent -from guillotina.interfaces import IBeforeObjectAddedEvent -from guillotina.interfaces import IBeforeObjectModifiedEvent -from guillotina.interfaces import IBeforeObjectMovedEvent -from guillotina.interfaces import IBeforeObjectRemovedEvent -from guillotina.interfaces import IBeforeRenderViewEvent -from guillotina.interfaces import IDatabaseInitializedEvent -from guillotina.interfaces import IFileBeforeFinishUploaded -from guillotina.interfaces import IFileFinishUploaded -from guillotina.interfaces import IFileStartedUpload -from guillotina.interfaces import INewUserAdded -from guillotina.interfaces import IObjectAddedEvent -from guillotina.interfaces import IObjectDuplicatedEvent -from guillotina.interfaces import IObjectLoadedEvent -from guillotina.interfaces import IObjectLocationEvent -from guillotina.interfaces import IObjectModifiedEvent -from guillotina.interfaces import IObjectMovedEvent -from guillotina.interfaces import IObjectPermissionsModifiedEvent -from guillotina.interfaces import IObjectPermissionsViewEvent -from guillotina.interfaces import IObjectRemovedEvent -from guillotina.interfaces import IObjectVisitedEvent -from guillotina.interfaces import IRegistry -from guillotina.interfaces import IRegistryEditedEvent -from guillotina.interfaces import ITraversalMissEvent -from guillotina.interfaces import ITraversalResourceMissEvent -from guillotina.interfaces import ITraversalRouteMissEvent -from guillotina.interfaces import ITraversalViewMissEvent -from guillotina.interfaces import IUserLogin -from guillotina.interfaces import IUserRefreshToken -from guillotina.interfaces import IValidationEvent +import typing + from zope.interface import implementer -import typing +from guillotina.component.interfaces import IObjectEvent +from guillotina.db.orm.interfaces import IBaseObject +from guillotina.interfaces import ( + IAfterAsyncUtilityLoadedEvent, + IApplicationCleanupEvent, + IApplicationConfiguredEvent, + IApplicationEvent, + IApplicationInitializedEvent, + IBeforeAsyncUtilityLoadedEvent, + IBeforeObjectAddedEvent, + IBeforeObjectModifiedEvent, + IBeforeObjectMovedEvent, + IBeforeObjectRemovedEvent, + IBeforeRenderViewEvent, + IDatabaseInitializedEvent, + IFileBeforeFinishUploaded, + IFileFinishUploaded, + IFileStartedUpload, + INewUserAdded, + IObjectAddedEvent, + IObjectDuplicatedEvent, + IObjectLoadedEvent, + IObjectLocationEvent, + IObjectModifiedEvent, + IObjectMovedEvent, + IObjectPermissionsModifiedEvent, + IObjectPermissionsViewEvent, + IObjectRemovedEvent, + IObjectVisitedEvent, + IRegistry, + IRegistryEditedEvent, + ITraversalMissEvent, + ITraversalResourceMissEvent, + ITraversalRouteMissEvent, + ITraversalViewMissEvent, + IUserLogin, + IUserRefreshToken, + IValidationEvent, +) @implementer(IObjectEvent) diff --git a/guillotina/exc_resp.py b/guillotina/exc_resp.py index 4ac3059fd..376f455be 100644 --- a/guillotina/exc_resp.py +++ b/guillotina/exc_resp.py @@ -1,22 +1,25 @@ -from guillotina import configure -from guillotina import error_reasons -from guillotina.exceptions import ConflictIdOnContainer -from guillotina.exceptions import DeserializationError -from guillotina.exceptions import InvalidContentType -from guillotina.exceptions import NoPermissionToAdd -from guillotina.exceptions import NotAllowedContentType -from guillotina.exceptions import PreconditionFailed -from guillotina.exceptions import Unauthorized -from guillotina.exceptions import UnRetryableRequestError -from guillotina.interfaces import IErrorResponseException -from guillotina.response import HTTPConflict -from guillotina.response import HTTPExpectationFailed -from guillotina.response import HTTPPreconditionFailed -from guillotina.response import HTTPUnauthorized -from guillotina.response import Response - import json +from guillotina import configure, error_reasons +from guillotina.exceptions import ( + ConflictIdOnContainer, + DeserializationError, + InvalidContentType, + NoPermissionToAdd, + NotAllowedContentType, + PreconditionFailed, + Unauthorized, + UnRetryableRequestError, +) +from guillotina.interfaces import IErrorResponseException +from guillotina.response import ( + HTTPConflict, + HTTPExpectationFailed, + HTTPPreconditionFailed, + HTTPUnauthorized, + Response, +) + def render_error_response(error, reason, eid=None): data = {"reason": reason.name, "details": reason.details, "type": error} diff --git a/guillotina/exceptions.py b/guillotina/exceptions.py index 26c5a4628..c72614eb2 100644 --- a/guillotina/exceptions.py +++ b/guillotina/exceptions.py @@ -1,11 +1,11 @@ -from guillotina._settings import app_settings -from guillotina.interfaces import IUnauthorized +import jsonschema +import orjson from zope.interface import implementer from zope.interface.exceptions import Invalid # noqa pylint: disable=W0611 from zope.interface.interfaces import ComponentLookupError # noqa pylint: disable=W0611 -import jsonschema -import orjson +from guillotina._settings import app_settings +from guillotina.interfaces import IUnauthorized class NoPermissionToAdd(Exception): diff --git a/guillotina/factory/app.py b/guillotina/factory/app.py index ccc300dd5..fd3002a84 100644 --- a/guillotina/factory/app.py +++ b/guillotina/factory/app.py @@ -1,38 +1,35 @@ +import json +import logging +import logging.config from copy import deepcopy -from guillotina import configure -from guillotina import glogging -from guillotina import traversal -from guillotina._settings import app_settings -from guillotina._settings import default_settings + +from jwcrypto import jwk + +from guillotina import configure, glogging, traversal +from guillotina._settings import app_settings, default_settings from guillotina.behaviors import apply_concrete_behaviors -from guillotina.component import get_utility -from guillotina.component import provide_utility +from guillotina.component import get_utility, provide_utility from guillotina.configure.config import ConfigurationMachine -from guillotina.content import JavaScriptApplication -from guillotina.content import load_cached_schema -from guillotina.content import StaticDirectory -from guillotina.content import StaticFile +from guillotina.content import JavaScriptApplication, StaticDirectory, StaticFile, load_cached_schema from guillotina.event import notify -from guillotina.events import AfterAsyncUtilityLoadedEvent -from guillotina.events import ApplicationCleanupEvent -from guillotina.events import ApplicationConfiguredEvent -from guillotina.events import ApplicationInitializedEvent -from guillotina.events import BeforeAsyncUtilityLoadedEvent -from guillotina.events import DatabaseInitializedEvent +from guillotina.events import ( + AfterAsyncUtilityLoadedEvent, + ApplicationCleanupEvent, + ApplicationConfiguredEvent, + ApplicationInitializedEvent, + BeforeAsyncUtilityLoadedEvent, + DatabaseInitializedEvent, +) from guillotina.factory.content import ApplicationRoot -from guillotina.interfaces import IApplication -from guillotina.interfaces import IDatabase -from guillotina.interfaces import IDatabaseConfigurationFactory -from guillotina.utils import lazy_apply -from guillotina.utils import list_or_dict_items -from guillotina.utils import resolve_dotted_name -from guillotina.utils import resolve_path -from guillotina.utils import secure_passphrase -from jwcrypto import jwk +from guillotina.interfaces import IApplication, IDatabase, IDatabaseConfigurationFactory +from guillotina.utils import ( + lazy_apply, + list_or_dict_items, + resolve_dotted_name, + resolve_path, + secure_passphrase, +) -import json -import logging -import logging.config app_logger = logging.getLogger("guillotina") logger = glogging.getLogger("guillotina") diff --git a/guillotina/factory/content.py b/guillotina/factory/content.py index b323475bb..d191e4722 100644 --- a/guillotina/factory/content.py +++ b/guillotina/factory/content.py @@ -1,31 +1,21 @@ +import asyncio +import logging +import typing from concurrent.futures import ThreadPoolExecutor + +from zope.interface import alsoProvides, implementer + from guillotina._settings import app_settings from guillotina.auth.users import RootUser from guillotina.auth.validators import hash_password -from guillotina.component import get_adapter -from guillotina.component import get_global_components -from guillotina.component import get_utility -from guillotina.component import provide_utility +from guillotina.component import get_adapter, get_global_components, get_utility, provide_utility from guillotina.const import ROOT_ID -from guillotina.db.interfaces import IDatabaseManager -from guillotina.db.interfaces import ITransaction -from guillotina.db.interfaces import ITransactionManager -from guillotina.db.interfaces import IWriter +from guillotina.db.interfaces import IDatabaseManager, ITransaction, ITransactionManager, IWriter from guillotina.db.transaction_manager import TransactionManager -from guillotina.interfaces import IApplication -from guillotina.interfaces import IDatabase +from guillotina.interfaces import IApplication, IDatabase from guillotina.transactions import get_transaction -from guillotina.utils import apply_coroutine -from guillotina.utils import import_class -from guillotina.utils import lazy_apply -from guillotina.utils import list_or_dict_items -from guillotina.utils import notice_on_error -from zope.interface import alsoProvides -from zope.interface import implementer +from guillotina.utils import apply_coroutine, import_class, lazy_apply, list_or_dict_items, notice_on_error -import asyncio -import logging -import typing logger = logging.getLogger("guillotina") diff --git a/guillotina/factory/security.py b/guillotina/factory/security.py index f638147cc..2d8bd526a 100644 --- a/guillotina/factory/security.py +++ b/guillotina/factory/security.py @@ -1,16 +1,18 @@ -from guillotina import configure -from guillotina import security -from guillotina.auth.users import ANONYMOUS_USER_ID -from guillotina.auth.users import ROOT_USER_ID -from guillotina.interfaces import AllowSingle -from guillotina.interfaces import IApplication -from guillotina.interfaces import IDatabase -from guillotina.interfaces import IInheritPermissionManager -from guillotina.interfaces import IPrincipalPermissionManager -from guillotina.interfaces import IRolePermissionManager -from guillotina.security.security_code import InheritPermissionManager -from guillotina.security.security_code import PrincipalPermissionManager -from guillotina.security.security_code import RolePermissionManager +from guillotina import configure, security +from guillotina.auth.users import ANONYMOUS_USER_ID, ROOT_USER_ID +from guillotina.interfaces import ( + AllowSingle, + IApplication, + IDatabase, + IInheritPermissionManager, + IPrincipalPermissionManager, + IRolePermissionManager, +) +from guillotina.security.security_code import ( + InheritPermissionManager, + PrincipalPermissionManager, + RolePermissionManager, +) @configure.adapter(for_=IDatabase, provides=IInheritPermissionManager) diff --git a/guillotina/factory/serialize.py b/guillotina/factory/serialize.py index d1df83abc..b7291894c 100644 --- a/guillotina/factory/serialize.py +++ b/guillotina/factory/serialize.py @@ -1,10 +1,12 @@ from guillotina import configure -from guillotina.interfaces import IApplication -from guillotina.interfaces import IDatabase -from guillotina.interfaces import IRequest -from guillotina.interfaces import IResourceSerializeToJson -from guillotina.interfaces import IStaticDirectory -from guillotina.interfaces import IStaticFile +from guillotina.interfaces import ( + IApplication, + IDatabase, + IRequest, + IResourceSerializeToJson, + IStaticDirectory, + IStaticFile, +) from guillotina.utils import get_security_policy diff --git a/guillotina/fields/annotation.py b/guillotina/fields/annotation.py index 881e8fe72..5e6d5707f 100644 --- a/guillotina/fields/annotation.py +++ b/guillotina/fields/annotation.py @@ -1,34 +1,28 @@ -from guillotina import configure -from guillotina import schema +import bisect +import logging +import time +import typing +import uuid +from typing import Any, AsyncIterator, List, Optional, Tuple, cast + +from zope.interface import Interface, implementer + +from guillotina import configure, schema from guillotina.annotations import AnnotationData from guillotina.component import query_adapter from guillotina.db.orm.interfaces import IBaseObject from guillotina.exceptions import ValueDeserializationError from guillotina.fields import patch -from guillotina.fields.interfaces import IBucketDictField -from guillotina.fields.interfaces import IBucketListField -from guillotina.fields.interfaces import IPatchFieldOperation -from guillotina.interfaces import IAnnotationData -from guillotina.interfaces import IAnnotations -from guillotina.interfaces import IContentBehavior -from guillotina.interfaces import IFieldValueRenderer -from guillotina.interfaces import IRequest -from guillotina.response import HTTPGone -from guillotina.response import HTTPPreconditionFailed -from typing import Any -from typing import AsyncIterator -from typing import cast -from typing import List -from typing import Optional -from typing import Tuple -from zope.interface import implementer -from zope.interface import Interface +from guillotina.fields.interfaces import IBucketDictField, IBucketListField, IPatchFieldOperation +from guillotina.interfaces import ( + IAnnotationData, + IAnnotations, + IContentBehavior, + IFieldValueRenderer, + IRequest, +) +from guillotina.response import HTTPGone, HTTPPreconditionFailed -import bisect -import logging -import time -import typing -import uuid logger = logging.getLogger("guillotina") diff --git a/guillotina/fields/dynamic.py b/guillotina/fields/dynamic.py index f72243030..9bb75a461 100644 --- a/guillotina/fields/dynamic.py +++ b/guillotina/fields/dynamic.py @@ -1,20 +1,14 @@ from collections import namedtuple -from guillotina import configure -from guillotina import schema + +from zope.interface import Interface, implementer + +from guillotina import configure, schema from guillotina.component import get_adapter -from guillotina.exceptions import ComponentLookupError -from guillotina.exceptions import ValueDeserializationError -from guillotina.fields.interfaces import IDynamicField -from guillotina.fields.interfaces import IDynamicFieldOperation -from guillotina.fields.patch import field_converter -from guillotina.fields.patch import PatchDictDel -from guillotina.fields.patch import PatchDictSet -from guillotina.fields.patch import PatchDictUpdate -from guillotina.fields.patch import PatchField +from guillotina.exceptions import ComponentLookupError, ValueDeserializationError +from guillotina.fields.interfaces import IDynamicField, IDynamicFieldOperation +from guillotina.fields.patch import PatchDictDel, PatchDictSet, PatchDictUpdate, PatchField, field_converter from guillotina.interfaces import IJSONToValue from guillotina.schema.interfaces import IDict -from zope.interface import implementer -from zope.interface import Interface @implementer(IDynamicField) diff --git a/guillotina/fields/files.py b/guillotina/fields/files.py index aa8d105de..22542995f 100644 --- a/guillotina/fields/files.py +++ b/guillotina/fields/files.py @@ -1,8 +1,8 @@ -from guillotina.interfaces import ICloudFileField -from guillotina.interfaces import IFile -from guillotina.schema import Object from zope.interface import implementer +from guillotina.interfaces import ICloudFileField, IFile +from guillotina.schema import Object + @implementer(ICloudFileField) class CloudFileField(Object): diff --git a/guillotina/fields/interfaces.py b/guillotina/fields/interfaces.py index 244cbc613..676716db3 100644 --- a/guillotina/fields/interfaces.py +++ b/guillotina/fields/interfaces.py @@ -1,6 +1,7 @@ -from guillotina.schema.interfaces import IField from zope.interface import Interface +from guillotina.schema.interfaces import IField + class IPatchField(IField): pass diff --git a/guillotina/fields/patch.py b/guillotina/fields/patch.py index c730c9294..159164cf1 100644 --- a/guillotina/fields/patch.py +++ b/guillotina/fields/patch.py @@ -1,20 +1,14 @@ from collections import namedtuple -from guillotina import configure -from guillotina import schema -from guillotina.component import get_adapter -from guillotina.component import query_adapter + +from zope.interface import implementer + +from guillotina import configure, schema +from guillotina.component import get_adapter, query_adapter from guillotina.exceptions import ValueDeserializationError -from guillotina.fields.interfaces import IPatchField -from guillotina.fields.interfaces import IPatchFieldOperation +from guillotina.fields.interfaces import IPatchField, IPatchFieldOperation from guillotina.interfaces import IJSONToValue -from guillotina.schema.interfaces import IArrayJSONField -from guillotina.schema.interfaces import IDict -from guillotina.schema.interfaces import IInt -from guillotina.schema.interfaces import IList -from guillotina.schema.interfaces import IObjectJSONField -from guillotina.schema.interfaces import ITuple +from guillotina.schema.interfaces import IArrayJSONField, IDict, IInt, IList, IObjectJSONField, ITuple from guillotina.utils import apply_coroutine -from zope.interface import implementer @implementer(IPatchField) diff --git a/guillotina/files/__init__.py b/guillotina/files/__init__.py index 771c437ef..cafc25a4c 100644 --- a/guillotina/files/__init__.py +++ b/guillotina/files/__init__.py @@ -1,3 +1,5 @@ +from guillotina.exceptions import UnRetryableRequestError # noqa + from .adapter import DBFileStorageManagerAdapter # noqa from .const import CHUNK_SIZE # noqa from .const import MAX_REQUEST_CACHE_SIZE # noqa @@ -7,6 +9,6 @@ from .utils import convert_base64_to_binary # noqa from .utils import get_contenttype # noqa from .utils import read_request_data # noqa -from guillotina.exceptions import UnRetryableRequestError # noqa + CloudFileManager = FileManager # b/w compat diff --git a/guillotina/files/adapter.py b/guillotina/files/adapter.py index 0a4671451..8c300a931 100644 --- a/guillotina/files/adapter.py +++ b/guillotina/files/adapter.py @@ -1,28 +1,26 @@ -from .dbfile import DBFile -from .exceptions import RangeNotFound -from .exceptions import RangeNotSupported +import time +from typing import AsyncIterator + from guillotina import configure from guillotina.blob import Blob from guillotina.event import notify -from guillotina.events import FileBeforeUploadFinishedEvent -from guillotina.events import FileUploadFinishedEvent -from guillotina.events import FileUploadStartedEvent -from guillotina.exceptions import BlobChunkNotFound -from guillotina.exceptions import FileNotFoundException -from guillotina.files.utils import generate_key -from guillotina.files.utils import guess_content_type -from guillotina.interfaces import IDBFileField -from guillotina.interfaces import IFileCleanup -from guillotina.interfaces import IFileField -from guillotina.interfaces import IFileNameGenerator -from guillotina.interfaces import IFileStorageManager -from guillotina.interfaces import IRequest -from guillotina.interfaces import IResource -from guillotina.interfaces import IUploadDataManager +from guillotina.events import FileBeforeUploadFinishedEvent, FileUploadFinishedEvent, FileUploadStartedEvent +from guillotina.exceptions import BlobChunkNotFound, FileNotFoundException +from guillotina.files.utils import generate_key, guess_content_type +from guillotina.interfaces import ( + IDBFileField, + IFileCleanup, + IFileField, + IFileNameGenerator, + IFileStorageManager, + IRequest, + IResource, + IUploadDataManager, +) from guillotina.response import HTTPPreconditionFailed -from typing import AsyncIterator -import time +from .dbfile import DBFile +from .exceptions import RangeNotFound, RangeNotSupported @configure.adapter(for_=(IResource, IFileField), provides=IFileNameGenerator) diff --git a/guillotina/files/dbfile.py b/guillotina/files/dbfile.py index de04618c6..e38b26bfd 100644 --- a/guillotina/files/dbfile.py +++ b/guillotina/files/dbfile.py @@ -1,9 +1,12 @@ -from .field import BaseCloudFile -from guillotina.blob import Blob -from guillotina.interfaces import IDBFile from typing import Optional + from zope.interface import implementer +from guillotina.blob import Blob +from guillotina.interfaces import IDBFile + +from .field import BaseCloudFile + @implementer(IDBFile) class DBFile(BaseCloudFile): diff --git a/guillotina/files/field.py b/guillotina/files/field.py index 1dbd04823..f6ebad358 100644 --- a/guillotina/files/field.py +++ b/guillotina/files/field.py @@ -1,19 +1,15 @@ +import base64 +import uuid from functools import partial + +from zope.interface import implementer + from guillotina import configure from guillotina.component import get_multi_adapter -from guillotina.files.utils import convert_base64_to_binary -from guillotina.files.utils import guess_content_type -from guillotina.interfaces import ICloudFileField -from guillotina.interfaces import IContentBehavior -from guillotina.interfaces import IFile -from guillotina.interfaces import IFileManager +from guillotina.files.utils import convert_base64_to_binary, guess_content_type +from guillotina.interfaces import ICloudFileField, IContentBehavior, IFile, IFileManager from guillotina.schema.fieldproperty import FieldProperty -from guillotina.utils import get_current_request -from guillotina.utils import to_str -from zope.interface import implementer - -import base64 -import uuid +from guillotina.utils import get_current_request, to_str @configure.value_serializer(for_=IFile) diff --git a/guillotina/files/manager.py b/guillotina/files/manager.py index d3c745d82..3ee31a2a1 100644 --- a/guillotina/files/manager.py +++ b/guillotina/files/manager.py @@ -1,34 +1,37 @@ -from .const import CHUNK_SIZE -from .exceptions import RangeException -from guillotina import configure -from guillotina import glogging -from guillotina._settings import app_settings -from guillotina.api.service import DictFieldProxy -from guillotina.component import get_adapter -from guillotina.component import get_multi_adapter -from guillotina.files.utils import read_request_data -from guillotina.interfaces import ICloudFileField -from guillotina.interfaces import IFileManager -from guillotina.interfaces import IFileStorageManager -from guillotina.interfaces import IRequest -from guillotina.interfaces import IResource -from guillotina.interfaces import IUploadDataManager -from guillotina.response import HTTPClientClosedRequest -from guillotina.response import HTTPConflict -from guillotina.response import HTTPNotFound -from guillotina.response import HTTPPreconditionFailed -from guillotina.response import HTTPRequestRangeNotSatisfiable -from guillotina.response import Response -from guillotina.utils import apply_coroutine -from guillotina.utils import get_object_url -from guillotina.utils import resolve_dotted_name -from zope.interface import alsoProvides - import asyncio import base64 import posixpath import uuid +from zope.interface import alsoProvides + +from guillotina import configure, glogging +from guillotina._settings import app_settings +from guillotina.api.service import DictFieldProxy +from guillotina.component import get_adapter, get_multi_adapter +from guillotina.files.utils import read_request_data +from guillotina.interfaces import ( + ICloudFileField, + IFileManager, + IFileStorageManager, + IRequest, + IResource, + IUploadDataManager, +) +from guillotina.response import ( + HTTPClientClosedRequest, + HTTPConflict, + HTTPNotFound, + HTTPPreconditionFailed, + HTTPRequestRangeNotSatisfiable, + Response, +) +from guillotina.utils import apply_coroutine, get_object_url, resolve_dotted_name + +from .const import CHUNK_SIZE +from .exceptions import RangeException + + logger = glogging.getLogger("guillotina") diff --git a/guillotina/files/utils.py b/guillotina/files/utils.py index 108575ddf..e095710a9 100644 --- a/guillotina/files/utils.py +++ b/guillotina/files/utils.py @@ -1,15 +1,15 @@ -from .const import MAX_REQUEST_CACHE_SIZE -from guillotina import task_vars -from guillotina.exceptions import UnRetryableRequestError -from guillotina.utils import get_content_path -from guillotina.utils import to_str - import asyncio import base64 import mimetypes import os import uuid +from guillotina import task_vars +from guillotina.exceptions import UnRetryableRequestError +from guillotina.utils import get_content_path, to_str + +from .const import MAX_REQUEST_CACHE_SIZE + async def read_request_data(request, chunk_size): """ diff --git a/guillotina/glogging.py b/guillotina/glogging.py index 1e155640c..77d200352 100644 --- a/guillotina/glogging.py +++ b/guillotina/glogging.py @@ -1,14 +1,13 @@ -from guillotina import task_vars - import logging import uuid +from guillotina import task_vars + def _wrapped(name): def log(self, *args, **kwargs): from guillotina.exceptions import RequestNotFound - from guillotina.utils import get_authenticated_user_id - from guillotina.utils import get_current_request + from guillotina.utils import get_authenticated_user_id, get_current_request func = getattr(self._logger, name) request = kwargs.pop("request", None) diff --git a/guillotina/gtypes.py b/guillotina/gtypes.py index 6b12a039b..5ae56c1e2 100644 --- a/guillotina/gtypes.py +++ b/guillotina/gtypes.py @@ -1,10 +1,6 @@ -from typing import Any -from typing import Dict -from typing import List -from typing import Tuple -from typing import TypeVar - import types +from typing import Any, Dict, List, Tuple, TypeVar + ResolvableType = TypeVar("ResolvableType", types.ModuleType, types.FunctionType, type) diff --git a/guillotina/interfaces/__init__.py b/guillotina/interfaces/__init__.py index a75fb5818..4670a5e7e 100644 --- a/guillotina/interfaces/__init__.py +++ b/guillotina/interfaces/__init__.py @@ -138,6 +138,7 @@ from .views import IPUT # noqa from .views import IView # noqa + DEFAULT_ADD_PERMISSION = "guillotina.AddContent" DEFAULT_READ_PERMISSION = "guillotina.ViewContent" DEFAULT_WRITE_PERMISSION = "guillotina.ManageContent" diff --git a/guillotina/interfaces/async_util.py b/guillotina/interfaces/async_util.py index 1043c3510..8770c7f72 100644 --- a/guillotina/interfaces/async_util.py +++ b/guillotina/interfaces/async_util.py @@ -1,5 +1,5 @@ -from typing import Dict -from typing import Optional +from typing import Dict, Optional + from zope.interface import Interface diff --git a/guillotina/interfaces/behaviors.py b/guillotina/interfaces/behaviors.py index 3c3ee176e..03d2fae76 100644 --- a/guillotina/interfaces/behaviors.py +++ b/guillotina/interfaces/behaviors.py @@ -1,8 +1,8 @@ -from guillotina import schema -from zope.interface import Attribute -from zope.interface import Interface +from zope.interface import Attribute, Interface from zope.interface.interfaces import IInterface +from guillotina import schema + class IBehavior(Interface): """A description of a behavior. These should be registered as named diff --git a/guillotina/interfaces/catalog.py b/guillotina/interfaces/catalog.py index 5fa66558d..9b185c854 100644 --- a/guillotina/interfaces/catalog.py +++ b/guillotina/interfaces/catalog.py @@ -1,8 +1,10 @@ -from .content import IContainer -from guillotina.db.orm.interfaces import IBaseObject +import typing + from zope.interface import Interface -import typing +from guillotina.db.orm.interfaces import IBaseObject + +from .content import IContainer class ICatalogUtility(Interface): diff --git a/guillotina/interfaces/content.py b/guillotina/interfaces/content.py index 7b8d99381..9408befb6 100644 --- a/guillotina/interfaces/content.py +++ b/guillotina/interfaces/content.py @@ -1,14 +1,15 @@ +from typing import TYPE_CHECKING + +from zope.interface import Attribute, Interface +from zope.interface.common.mapping import IEnumerableMapping + +import guillotina.schema from guillotina.component.interfaces import IFactory from guillotina.component.interfaces import ISite as IComponentSite from guillotina.db.orm.interfaces import IBaseObject from guillotina.interfaces.common import IMapping from guillotina.schema import TextLine -from typing import TYPE_CHECKING -from zope.interface import Attribute -from zope.interface import Interface -from zope.interface.common.mapping import IEnumerableMapping -import guillotina.schema if TYPE_CHECKING: # pragma: no cover from guillotina.db.interfaces import ITransactionManager diff --git a/guillotina/interfaces/events.py b/guillotina/interfaces/events.py index 36c997e84..784b9e214 100644 --- a/guillotina/interfaces/events.py +++ b/guillotina/interfaces/events.py @@ -1,6 +1,4 @@ -from zope.interface import Attribute -from zope.interface import Interface -from zope.interface import interfaces +from zope.interface import Attribute, Interface, interfaces class IBeforeObjectModifiedEvent(interfaces.IObjectEvent): diff --git a/guillotina/interfaces/files.py b/guillotina/interfaces/files.py index a30545e73..1eeb6ed15 100644 --- a/guillotina/interfaces/files.py +++ b/guillotina/interfaces/files.py @@ -1,8 +1,10 @@ -from guillotina import schema -from guillotina.schema.interfaces import IObject from typing import AsyncIterator + from zope.interface import Interface +from guillotina import schema +from guillotina.schema.interfaces import IObject + class IUploadDataManager(Interface): """ diff --git a/guillotina/interfaces/json.py b/guillotina/interfaces/json.py index 8d8914891..5047d511b 100644 --- a/guillotina/interfaces/json.py +++ b/guillotina/interfaces/json.py @@ -1,6 +1,8 @@ -from guillotina.i18n import MessageFactory from zope.interface import Interface +from guillotina.i18n import MessageFactory + + _ = MessageFactory("guillotina") diff --git a/guillotina/interfaces/mail.py b/guillotina/interfaces/mail.py index e19d721b5..d1abe8d92 100644 --- a/guillotina/interfaces/mail.py +++ b/guillotina/interfaces/mail.py @@ -1,8 +1,5 @@ -from typing import Any -from typing import Coroutine -from typing import List -from typing import Optional -from typing import Union +from typing import Any, Coroutine, List, Optional, Union + from zope.interface import Interface diff --git a/guillotina/interfaces/misc.py b/guillotina/interfaces/misc.py index caf3a4374..cd3604080 100644 --- a/guillotina/interfaces/misc.py +++ b/guillotina/interfaces/misc.py @@ -1,10 +1,9 @@ +from typing import Dict, Optional, Tuple, Type + +from zope.interface import Interface + from guillotina.db.orm.interfaces import IBaseObject from guillotina.interfaces.content import IApplication -from typing import Dict -from typing import Optional -from typing import Tuple -from typing import Type -from zope.interface import Interface class IRequest(Interface): diff --git a/guillotina/interfaces/registry.py b/guillotina/interfaces/registry.py index e899f94b7..baca5c0a5 100644 --- a/guillotina/interfaces/registry.py +++ b/guillotina/interfaces/registry.py @@ -1,6 +1,8 @@ +from zope.interface import Interface + from guillotina import schema from guillotina.i18n import MessageFactory -from zope.interface import Interface + _ = MessageFactory("guillotina") diff --git a/guillotina/interfaces/response.py b/guillotina/interfaces/response.py index 013005fc7..7d67a2dda 100644 --- a/guillotina/interfaces/response.py +++ b/guillotina/interfaces/response.py @@ -1,7 +1,7 @@ -from multidict import CIMultiDict from typing import Union -from zope.interface import Attribute -from zope.interface import Interface + +from multidict import CIMultiDict +from zope.interface import Attribute, Interface class IResponse(Interface): diff --git a/guillotina/interfaces/security.py b/guillotina/interfaces/security.py index 7905a8647..1cf873a2d 100644 --- a/guillotina/interfaces/security.py +++ b/guillotina/interfaces/security.py @@ -1,17 +1,15 @@ -from .misc import IRequest +import copyreg # type: ignore +import typing + +from zope.interface import Attribute, Interface + from guillotina.db.orm.interfaces import IBaseObject from guillotina.directives import read_permission from guillotina.i18n import MessageFactory -from guillotina.schema import Dict -from guillotina.schema import List -from guillotina.schema import Object -from guillotina.schema import Text -from guillotina.schema import TextLine -from zope.interface import Attribute -from zope.interface import Interface +from guillotina.schema import Dict, List, Object, Text, TextLine + +from .misc import IRequest -import copyreg # type: ignore -import typing _ = MessageFactory("guillotina") diff --git a/guillotina/interfaces/types.py b/guillotina/interfaces/types.py index 30500d1b3..c16309686 100644 --- a/guillotina/interfaces/types.py +++ b/guillotina/interfaces/types.py @@ -1,6 +1,7 @@ -from .content import IResource from zope.interface import Interface +from .content import IResource + class IConstrainTypes(Interface): # pylint: disable=E0239 def __init__(context: IResource, default=None): # noqa: N805 diff --git a/guillotina/json/definitions.py b/guillotina/json/definitions.py index 1e832dd1f..17bbb46dd 100644 --- a/guillotina/json/definitions.py +++ b/guillotina/json/definitions.py @@ -1,5 +1,6 @@ from guillotina import configure + configure.json_schema_definition( "Addon", { diff --git a/guillotina/json/deserialize_content.py b/guillotina/json/deserialize_content.py index 87a8f6061..ccf92c006 100644 --- a/guillotina/json/deserialize_content.py +++ b/guillotina/json/deserialize_content.py @@ -1,39 +1,31 @@ # -*- coding: utf-8 -*- +import asyncio from copy import deepcopy -from guillotina import configure -from guillotina import glogging -from guillotina.component import ComponentLookupError -from guillotina.component import get_adapter -from guillotina.component import query_utility -from guillotina.content import get_all_behaviors -from guillotina.content import get_cached_factory +from typing import Any, Dict, List, Type + +from zope.interface import Interface + +from guillotina import configure, glogging +from guillotina.component import ComponentLookupError, get_adapter, query_utility +from guillotina.content import get_all_behaviors, get_cached_factory from guillotina.db.transaction import _EMPTY -from guillotina.directives import merged_tagged_value_dict -from guillotina.directives import write_permission -from guillotina.exceptions import DeserializationError -from guillotina.exceptions import Invalid -from guillotina.exceptions import Unauthorized -from guillotina.exceptions import ValueDeserializationError -from guillotina.interfaces import IAsyncBehavior -from guillotina.interfaces import IJSONToValue -from guillotina.interfaces import IPermission -from guillotina.interfaces import IRequest -from guillotina.interfaces import IResource -from guillotina.interfaces import IResourceDeserializeFromJson -from guillotina.interfaces import RESERVED_ATTRS +from guillotina.directives import merged_tagged_value_dict, write_permission +from guillotina.exceptions import DeserializationError, Invalid, Unauthorized, ValueDeserializationError +from guillotina.interfaces import ( + RESERVED_ATTRS, + IAsyncBehavior, + IJSONToValue, + IPermission, + IRequest, + IResource, + IResourceDeserializeFromJson, +) from guillotina.json.utils import validate_invariants from guillotina.schema import get_fields from guillotina.schema.exceptions import ValidationError from guillotina.schema.interfaces import IField -from guillotina.utils import apply_coroutine -from guillotina.utils import get_security_policy -from typing import Any -from typing import Dict -from typing import List -from typing import Type -from zope.interface import Interface +from guillotina.utils import apply_coroutine, get_security_policy -import asyncio logger = glogging.getLogger("guillotina") diff --git a/guillotina/json/deserialize_value.py b/guillotina/json/deserialize_value.py index be4905413..cefa5e55c 100644 --- a/guillotina/json/deserialize_value.py +++ b/guillotina/json/deserialize_value.py @@ -1,29 +1,31 @@ # -*- coding: utf-8 -*- +import datetime + from dateutil.parser import parse +from zope.interface import Interface + from guillotina import configure -from guillotina.component import ComponentLookupError -from guillotina.component import get_adapter +from guillotina.component import ComponentLookupError, get_adapter from guillotina.exceptions import ValueDeserializationError from guillotina.interfaces import IJSONToValue from guillotina.profile import profilable from guillotina.schema._bootstrapinterfaces import IFromUnicode -from guillotina.schema.exceptions import ValidationError -from guillotina.schema.exceptions import WrongType -from guillotina.schema.interfaces import IBool -from guillotina.schema.interfaces import IDate -from guillotina.schema.interfaces import IDatetime -from guillotina.schema.interfaces import IDict -from guillotina.schema.interfaces import IField -from guillotina.schema.interfaces import IFrozenSet -from guillotina.schema.interfaces import IJSONField -from guillotina.schema.interfaces import IList -from guillotina.schema.interfaces import IObject -from guillotina.schema.interfaces import ISet -from guillotina.schema.interfaces import ITuple -from guillotina.schema.interfaces import IUnionField -from zope.interface import Interface +from guillotina.schema.exceptions import ValidationError, WrongType +from guillotina.schema.interfaces import ( + IBool, + IDate, + IDatetime, + IDict, + IField, + IFrozenSet, + IJSONField, + IList, + IObject, + ISet, + ITuple, + IUnionField, +) -import datetime _type_conversions = (int, str, float, bool) diff --git a/guillotina/json/serialize_content.py b/guillotina/json/serialize_content.py index bacbde92f..795a9f8ec 100644 --- a/guillotina/json/serialize_content.py +++ b/guillotina/json/serialize_content.py @@ -1,29 +1,26 @@ # -*- coding: utf-8 -*- -from guillotina import app_settings -from guillotina import configure -from guillotina.component import ComponentLookupError -from guillotina.component import get_multi_adapter -from guillotina.component import query_utility -from guillotina.content import get_all_behaviors -from guillotina.content import get_cached_factory -from guillotina.directives import merged_tagged_value_dict -from guillotina.directives import read_permission -from guillotina.interfaces import IAsyncBehavior -from guillotina.interfaces import IFolder -from guillotina.interfaces import IPermission -from guillotina.interfaces import IResource -from guillotina.interfaces import IResourceSerializeToJson -from guillotina.interfaces import IResourceSerializeToJsonSummary +import asyncio +import logging + +from zope.interface import Interface + +from guillotina import app_settings, configure +from guillotina.component import ComponentLookupError, get_multi_adapter, query_utility +from guillotina.content import get_all_behaviors, get_cached_factory +from guillotina.directives import merged_tagged_value_dict, read_permission +from guillotina.interfaces import ( + IAsyncBehavior, + IFolder, + IPermission, + IResource, + IResourceSerializeToJson, + IResourceSerializeToJsonSummary, +) from guillotina.json.serialize_value import json_compatible from guillotina.profile import profilable from guillotina.schema import get_fields -from guillotina.utils import apply_coroutine -from guillotina.utils import get_object_url -from guillotina.utils import get_security_policy -from zope.interface import Interface +from guillotina.utils import apply_coroutine, get_object_url, get_security_policy -import asyncio -import logging logger = logging.getLogger("guillotina") diff --git a/guillotina/json/serialize_schema.py b/guillotina/json/serialize_schema.py index 4b0cc5cac..060d42684 100644 --- a/guillotina/json/serialize_schema.py +++ b/guillotina/json/serialize_schema.py @@ -1,16 +1,18 @@ # -*- coding: utf-8 -*- +from zope.interface import Interface + from guillotina import configure -from guillotina.component import get_multi_adapter -from guillotina.component import get_utility +from guillotina.component import get_multi_adapter, get_utility from guillotina.component.interfaces import IFactory -from guillotina.interfaces import IBehavior -from guillotina.interfaces import IFactorySerializeToJson -from guillotina.interfaces import IRequest -from guillotina.interfaces import ISchemaFieldSerializeToJson -from guillotina.interfaces import ISchemaSerializeToJson +from guillotina.interfaces import ( + IBehavior, + IFactorySerializeToJson, + IRequest, + ISchemaFieldSerializeToJson, + ISchemaSerializeToJson, +) from guillotina.profile import profilable from guillotina.schema import get_fields_in_order -from zope.interface import Interface @configure.adapter(for_=(IFactory, IRequest), provides=IFactorySerializeToJson) diff --git a/guillotina/json/serialize_schema_field.py b/guillotina/json/serialize_schema_field.py index 04734ed36..a1e9cc44a 100644 --- a/guillotina/json/serialize_schema_field.py +++ b/guillotina/json/serialize_schema_field.py @@ -1,30 +1,35 @@ +from zope.interface import Interface, implementedBy + from guillotina import configure from guillotina.component import get_multi_adapter from guillotina.fields.interfaces import IPatchField -from guillotina.interfaces import ICloudFileField -from guillotina.interfaces import IFileField -from guillotina.interfaces import ISchemaFieldSerializeToJson -from guillotina.interfaces import ISchemaSerializeToJson +from guillotina.interfaces import ( + ICloudFileField, + IFileField, + ISchemaFieldSerializeToJson, + ISchemaSerializeToJson, +) from guillotina.json.serialize_value import json_compatible from guillotina.profile import profilable from guillotina.schema import get_fields -from guillotina.schema.interfaces import IBool -from guillotina.schema.interfaces import IChoice -from guillotina.schema.interfaces import ICollection -from guillotina.schema.interfaces import IDate -from guillotina.schema.interfaces import IDatetime -from guillotina.schema.interfaces import IDecimal -from guillotina.schema.interfaces import IDict -from guillotina.schema.interfaces import IField -from guillotina.schema.interfaces import IFloat -from guillotina.schema.interfaces import IInt -from guillotina.schema.interfaces import IJSONField -from guillotina.schema.interfaces import IObject -from guillotina.schema.interfaces import IText -from guillotina.schema.interfaces import ITextLine -from guillotina.schema.interfaces import ITime -from zope.interface import implementedBy -from zope.interface import Interface +from guillotina.schema.interfaces import ( + IBool, + IChoice, + ICollection, + IDate, + IDatetime, + IDecimal, + IDict, + IField, + IFloat, + IInt, + IJSONField, + IObject, + IText, + ITextLine, + ITime, +) + FIELDS_CACHE: dict = {} diff --git a/guillotina/json/serialize_value.py b/guillotina/json/serialize_value.py index 63bf99019..a2c975a91 100644 --- a/guillotina/json/serialize_value.py +++ b/guillotina/json/serialize_value.py @@ -1,10 +1,8 @@ # -*- coding: utf-8 -*- from collections import OrderedDict -from datetime import date -from datetime import datetime -from datetime import time -from datetime import timedelta +from datetime import date, datetime, time, timedelta from decimal import Decimal + from guillotina import configure from guillotina.component import query_adapter from guillotina.i18n import Message @@ -12,6 +10,7 @@ from guillotina.profile import profilable from guillotina.schema.vocabulary import SimpleVocabulary + _MISSING = object() diff --git a/guillotina/json/utils.py b/guillotina/json/utils.py index b32564db1..1511a825f 100644 --- a/guillotina/json/utils.py +++ b/guillotina/json/utils.py @@ -1,17 +1,15 @@ +import asyncio +import logging +from typing import Any, Dict, List, Type + +from zope.interface import Interface, Invalid + from guillotina.component import get_multi_adapter from guillotina.db.orm.interfaces import IBaseObject from guillotina.exceptions import RequestNotFound from guillotina.interfaces import ISchemaSerializeToJson from guillotina.utils import get_current_request -from typing import Any -from typing import Dict -from typing import List -from typing import Type -from zope.interface import Interface -from zope.interface import Invalid -import asyncio -import logging logger = logging.getLogger("guillotina") diff --git a/guillotina/metrics.py b/guillotina/metrics.py index 75cd104a3..4a0f68a5c 100644 --- a/guillotina/metrics.py +++ b/guillotina/metrics.py @@ -1,14 +1,11 @@ -from typing import Dict -from typing import Optional -from typing import Type - import asyncio import time import traceback +from typing import Dict, Optional, Type + try: - from prometheus_client import Counter - from prometheus_client import Histogram + from prometheus_client import Counter, Histogram except ImportError: Counter = Histogram = None diff --git a/guillotina/middlewares/errors.py b/guillotina/middlewares/errors.py index c4984c4c2..4e9dc44ae 100644 --- a/guillotina/middlewares/errors.py +++ b/guillotina/middlewares/errors.py @@ -1,17 +1,14 @@ -from guillotina import error_reasons -from guillotina import logger -from guillotina import response -from guillotina import task_vars +import asyncio +import traceback +import uuid +from typing import Optional + +from guillotina import error_reasons, logger, response, task_vars from guillotina._settings import app_settings from guillotina.browser import View from guillotina.i18n import default_message_factory as _ from guillotina.interfaces import IRequest from guillotina.traversal import apply_rendering -from typing import Optional - -import asyncio -import traceback -import uuid class ErrorsMiddleware: diff --git a/guillotina/permissions.py b/guillotina/permissions.py index 3818623be..886b18c7b 100644 --- a/guillotina/permissions.py +++ b/guillotina/permissions.py @@ -1,5 +1,6 @@ from guillotina import configure + configure.permission("guillotina.AccessContent", "Access content") configure.permission("guillotina.ModifyContent", "Modify content") configure.permission("guillotina.DeleteContent", "Delete content") diff --git a/guillotina/registry.py b/guillotina/registry.py index fcd8982b4..364c0fb2b 100644 --- a/guillotina/registry.py +++ b/guillotina/registry.py @@ -1,10 +1,11 @@ +from zope.interface import alsoProvides, implementer + from guillotina.annotations import AnnotationData from guillotina.browser import get_physical_path from guillotina.db.orm.interfaces import IBaseObject from guillotina.interfaces import IRegistry from guillotina.schema._bootstrapinterfaces import IContextAwareDefaultFactory -from zope.interface import alsoProvides -from zope.interface import implementer + REGISTRY_DATA_KEY = "_registry" diff --git a/guillotina/renderers.py b/guillotina/renderers.py index b89f74d5f..1c416514d 100644 --- a/guillotina/renderers.py +++ b/guillotina/renderers.py @@ -1,14 +1,14 @@ +import json +from typing import Optional, cast + +import orjson +from zope.interface.interface import InterfaceClass + from guillotina import configure from guillotina.interfaces import IResponse from guillotina.interfaces.security import PermissionSetting from guillotina.profile import profilable from guillotina.response import Response -from typing import cast -from typing import Optional -from zope.interface.interface import InterfaceClass - -import json -import orjson def guillotina_json_default(obj): diff --git a/guillotina/request.py b/guillotina/request.py index 82e6c9e9e..cda1f8320 100644 --- a/guillotina/request.py +++ b/guillotina/request.py @@ -1,29 +1,24 @@ +import asyncio +import enum +import time +import urllib.parse +import uuid from collections import OrderedDict from functools import update_wrapper +from http.cookies import SimpleCookie +from typing import Any, Dict, Iterator, List, Optional, Union + +import multidict +import orjson +from zope.interface import implementer + from guillotina import task_vars from guillotina.db.orm.interfaces import IBaseObject -from guillotina.interfaces import IDefaultLayer -from guillotina.interfaces import IRequest +from guillotina.interfaces import IDefaultLayer, IRequest from guillotina.interfaces.content import IApplication from guillotina.profile import profilable from guillotina.utils import execute from guillotina.utils.misc import build_url -from http.cookies import SimpleCookie -from typing import Any -from typing import Dict -from typing import Iterator -from typing import List -from typing import Optional -from typing import Union -from zope.interface import implementer - -import asyncio -import enum -import multidict -import orjson -import time -import urllib.parse -import uuid class reify(object): diff --git a/guillotina/response.py b/guillotina/response.py index cd9db0683..ffeadc0a4 100644 --- a/guillotina/response.py +++ b/guillotina/response.py @@ -1,13 +1,10 @@ +from typing import Dict, List, Optional, Tuple, Union + +from multidict import CIMultiDict, istr +from zope.interface import implementer + from guillotina.interfaces import IResponse from guillotina.request import Request -from multidict import CIMultiDict -from multidict import istr -from typing import Dict -from typing import List -from typing import Optional -from typing import Tuple -from typing import Union -from zope.interface import implementer @implementer(IResponse) diff --git a/guillotina/routes.py b/guillotina/routes.py index fac280174..42efa6642 100644 --- a/guillotina/routes.py +++ b/guillotina/routes.py @@ -1,6 +1,7 @@ +import re + from guillotina.response import InvalidRoute -import re URL_MATCH_RE = re.compile(r"\{[a-zA-Z0-9\_\-]+\}") _EXACT = object() diff --git a/guillotina/schema/__init__.py b/guillotina/schema/__init__.py index 0132ff68a..dc9b4aaa4 100644 --- a/guillotina/schema/__init__.py +++ b/guillotina/schema/__init__.py @@ -15,53 +15,58 @@ ############################################################################## from guillotina.schema._bootstrapinterfaces import NO_VALUE -from guillotina.schema._field import ASCII -from guillotina.schema._field import ASCIILine -from guillotina.schema._field import Bool -from guillotina.schema._field import Bytes -from guillotina.schema._field import BytesLine -from guillotina.schema._field import Choice -from guillotina.schema._field import Container -from guillotina.schema._field import Date -from guillotina.schema._field import Datetime -from guillotina.schema._field import Decimal -from guillotina.schema._field import Dict -from guillotina.schema._field import DottedName -from guillotina.schema._field import Field -from guillotina.schema._field import Float -from guillotina.schema._field import FrozenSet -from guillotina.schema._field import Id -from guillotina.schema._field import Int -from guillotina.schema._field import InterfaceField -from guillotina.schema._field import Iterable -from guillotina.schema._field import JSONField -from guillotina.schema._field import List -from guillotina.schema._field import MaskTextLine -from guillotina.schema._field import MinMaxLen -from guillotina.schema._field import NativeString -from guillotina.schema._field import NativeStringLine -from guillotina.schema._field import Object -from guillotina.schema._field import Orderable -from guillotina.schema._field import OrderedDict -from guillotina.schema._field import Password -from guillotina.schema._field import Set -from guillotina.schema._field import SourceText -from guillotina.schema._field import Text -from guillotina.schema._field import TextLine -from guillotina.schema._field import Time -from guillotina.schema._field import Timedelta -from guillotina.schema._field import Tuple -from guillotina.schema._field import UnionField -from guillotina.schema._field import URI -from guillotina.schema._schema import get_fields -from guillotina.schema._schema import get_fields_in_order -from guillotina.schema._schema import getFieldNames -from guillotina.schema._schema import getFieldNamesInOrder -from guillotina.schema._schema import getSchemaValidationErrors -from guillotina.schema._schema import getValidationErrors +from guillotina.schema._field import ( + ASCII, + URI, + ASCIILine, + Bool, + Bytes, + BytesLine, + Choice, + Container, + Date, + Datetime, + Decimal, + Dict, + DottedName, + Field, + Float, + FrozenSet, + Id, + Int, + InterfaceField, + Iterable, + JSONField, + List, + MaskTextLine, + MinMaxLen, + NativeString, + NativeStringLine, + Object, + Orderable, + OrderedDict, + Password, + Set, + SourceText, + Text, + TextLine, + Time, + Timedelta, + Tuple, + UnionField, +) +from guillotina.schema._schema import ( + get_fields, + get_fields_in_order, + getFieldNames, + getFieldNamesInOrder, + getSchemaValidationErrors, + getValidationErrors, +) from guillotina.schema.accessors import accessors from guillotina.schema.exceptions import ValidationError + getFields = get_fields # b/w getFieldsInOrder = get_fields_in_order # b/w diff --git a/guillotina/schema/_bootstrapfields.py b/guillotina/schema/_bootstrapfields.py index 43f10bf5d..d7a3cd80d 100644 --- a/guillotina/schema/_bootstrapfields.py +++ b/guillotina/schema/_bootstrapfields.py @@ -11,24 +11,26 @@ # FOR A PARTICULAR PURPOSE. # ############################################################################## -from guillotina.schema._bootstrapinterfaces import IContextAwareDefaultFactory -from guillotina.schema._bootstrapinterfaces import IFromUnicode -from guillotina.schema._schema import get_fields -from guillotina.schema.exceptions import ConstraintNotSatisfied -from guillotina.schema.exceptions import InvalidValue -from guillotina.schema.exceptions import NotAContainer -from guillotina.schema.exceptions import NotAnIterator -from guillotina.schema.exceptions import RequiredMissing -from guillotina.schema.exceptions import StopValidation -from guillotina.schema.exceptions import TooBig -from guillotina.schema.exceptions import TooLong -from guillotina.schema.exceptions import TooShort -from guillotina.schema.exceptions import TooSmall -from guillotina.schema.exceptions import WrongType from typing import Any -from zope.interface import Attribute -from zope.interface import implementer -from zope.interface import providedBy + +from zope.interface import Attribute, implementer, providedBy + +from guillotina.schema._bootstrapinterfaces import IContextAwareDefaultFactory, IFromUnicode +from guillotina.schema._schema import get_fields +from guillotina.schema.exceptions import ( + ConstraintNotSatisfied, + InvalidValue, + NotAContainer, + NotAnIterator, + RequiredMissing, + StopValidation, + TooBig, + TooLong, + TooShort, + TooSmall, + WrongType, +) + __docformat__ = "restructuredtext" diff --git a/guillotina/schema/_field.py b/guillotina/schema/_field.py index 38c1a4229..041589956 100644 --- a/guillotina/schema/_field.py +++ b/guillotina/schema/_field.py @@ -11,88 +11,87 @@ # FOR A PARTICULAR PURPOSE. # ############################################################################## -from collections import namedtuple +import decimal +import json +import re from collections import OrderedDict as NativeOrderedDict -from datetime import date -from datetime import datetime -from datetime import time -from datetime import timedelta -from guillotina.schema._bootstrapfields import Bool +from collections import namedtuple +from datetime import date, datetime, time, timedelta + +import jsonschema +from zope.interface import Interface, alsoProvides, classImplements, implementer +from zope.interface.interfaces import IInterface, IMethod + from guillotina.schema._bootstrapfields import Container # API import for __init__ -from guillotina.schema._bootstrapfields import Field -from guillotina.schema._bootstrapfields import Int -from guillotina.schema._bootstrapfields import Iterable -from guillotina.schema._bootstrapfields import MinMaxLen -from guillotina.schema._bootstrapfields import Orderable -from guillotina.schema._bootstrapfields import Password -from guillotina.schema._bootstrapfields import Text -from guillotina.schema._bootstrapfields import TextLine -from guillotina.schema.exceptions import ConstraintNotSatisfied -from guillotina.schema.exceptions import InvalidDottedName -from guillotina.schema.exceptions import InvalidId -from guillotina.schema.exceptions import InvalidURI -from guillotina.schema.exceptions import InvalidValue -from guillotina.schema.exceptions import NotUnique -from guillotina.schema.exceptions import SchemaNotFullyImplemented -from guillotina.schema.exceptions import SchemaNotProvided -from guillotina.schema.exceptions import ValidationError -from guillotina.schema.exceptions import WrongContainedType -from guillotina.schema.exceptions import WrongType +from guillotina.schema._bootstrapfields import ( + Bool, + Field, + Int, + Iterable, + MinMaxLen, + Orderable, + Password, + Text, + TextLine, +) +from guillotina.schema.exceptions import ( + ConstraintNotSatisfied, + InvalidDottedName, + InvalidId, + InvalidURI, + InvalidValue, + NotUnique, + SchemaNotFullyImplemented, + SchemaNotProvided, + ValidationError, + WrongContainedType, + WrongType, +) from guillotina.schema.fieldproperty import FieldProperty -from guillotina.schema.interfaces import IArrayJSONField -from guillotina.schema.interfaces import IASCII -from guillotina.schema.interfaces import IASCIILine -from guillotina.schema.interfaces import IBaseVocabulary -from guillotina.schema.interfaces import IBool -from guillotina.schema.interfaces import IBytes -from guillotina.schema.interfaces import IBytesLine -from guillotina.schema.interfaces import IChoice -from guillotina.schema.interfaces import IContextSourceBinder -from guillotina.schema.interfaces import IDate -from guillotina.schema.interfaces import IDatetime -from guillotina.schema.interfaces import IDecimal -from guillotina.schema.interfaces import IDict -from guillotina.schema.interfaces import IDottedName -from guillotina.schema.interfaces import IField -from guillotina.schema.interfaces import IFloat -from guillotina.schema.interfaces import IFromUnicode -from guillotina.schema.interfaces import IFrozenSet -from guillotina.schema.interfaces import IId -from guillotina.schema.interfaces import IInt -from guillotina.schema.interfaces import IInterfaceField -from guillotina.schema.interfaces import IJSONField -from guillotina.schema.interfaces import IList -from guillotina.schema.interfaces import IMaskTextLine -from guillotina.schema.interfaces import IMinMaxLen -from guillotina.schema.interfaces import IObject -from guillotina.schema.interfaces import IObjectJSONField -from guillotina.schema.interfaces import IOrderedDict -from guillotina.schema.interfaces import IPassword -from guillotina.schema.interfaces import ISet -from guillotina.schema.interfaces import ISource -from guillotina.schema.interfaces import ISourceText -from guillotina.schema.interfaces import IText -from guillotina.schema.interfaces import ITextLine -from guillotina.schema.interfaces import ITime -from guillotina.schema.interfaces import ITimedelta -from guillotina.schema.interfaces import ITuple -from guillotina.schema.interfaces import IUnionField -from guillotina.schema.interfaces import IURI +from guillotina.schema.interfaces import ( + IASCII, + IURI, + IArrayJSONField, + IASCIILine, + IBaseVocabulary, + IBool, + IBytes, + IBytesLine, + IChoice, + IContextSourceBinder, + IDate, + IDatetime, + IDecimal, + IDict, + IDottedName, + IField, + IFloat, + IFromUnicode, + IFrozenSet, + IId, + IInt, + IInterfaceField, + IJSONField, + IList, + IMaskTextLine, + IMinMaxLen, + IObject, + IObjectJSONField, + IOrderedDict, + IPassword, + ISet, + ISource, + ISourceText, + IText, + ITextLine, + ITime, + ITimedelta, + ITuple, + IUnionField, +) from guillotina.schema.utils import make_binary -from guillotina.schema.vocabulary import getVocabularyRegistry -from guillotina.schema.vocabulary import SimpleVocabulary -from guillotina.schema.vocabulary import VocabularyRegistryError -from zope.interface import alsoProvides -from zope.interface import classImplements -from zope.interface import implementer -from zope.interface import Interface -from zope.interface.interfaces import IInterface -from zope.interface.interfaces import IMethod +from guillotina.schema.vocabulary import SimpleVocabulary, VocabularyRegistryError, getVocabularyRegistry -import decimal -import json -import jsonschema -import re __docformat__ = "restructuredtext" diff --git a/guillotina/schema/_messageid.py b/guillotina/schema/_messageid.py index 1d56c6b6a..b0338ca76 100644 --- a/guillotina/schema/_messageid.py +++ b/guillotina/schema/_messageid.py @@ -14,4 +14,5 @@ from guillotina.i18n import MessageFactory + _ = MessageFactory("guillotina") diff --git a/guillotina/schema/_schema.py b/guillotina/schema/_schema.py index 85362eeb4..d1f89da15 100644 --- a/guillotina/schema/_schema.py +++ b/guillotina/schema/_schema.py @@ -11,9 +11,10 @@ # FOR A PARTICULAR PURPOSE. # ############################################################################## -import guillotina.schema import zope.interface.verify +import guillotina.schema + def getFieldNames(schema): """Return a list of all the Field names in a schema.""" diff --git a/guillotina/schema/accessors.py b/guillotina/schema/accessors.py index 6c32cb1a6..34c7c3dd3 100644 --- a/guillotina/schema/accessors.py +++ b/guillotina/schema/accessors.py @@ -11,10 +11,10 @@ # FOR A PARTICULAR PURPOSE. # ############################################################################## -from zope.interface import implementedBy -from zope.interface import providedBy +from zope.interface import implementedBy, providedBy from zope.interface.interface import Method + """ Field accessors =============== diff --git a/guillotina/schema/exceptions.py b/guillotina/schema/exceptions.py index ec69e7d73..45c202601 100644 --- a/guillotina/schema/exceptions.py +++ b/guillotina/schema/exceptions.py @@ -1,7 +1,7 @@ -from guillotina.schema._messageid import _ - import zope.interface +from guillotina.schema._messageid import _ + class StopValidation(Exception): """Raised if the validation is completed early. diff --git a/guillotina/schema/fieldproperty.py b/guillotina/schema/fieldproperty.py index a59256aac..068539f5d 100644 --- a/guillotina/schema/fieldproperty.py +++ b/guillotina/schema/fieldproperty.py @@ -11,14 +11,16 @@ # FOR A PARTICULAR PURPOSE. # ############################################################################## +import sys from copy import copy + +from zope import interface + +import guillotina.schema from guillotina import event from guillotina.schema import interfaces from guillotina.schema._bootstrapinterfaces import NO_VALUE -from zope import interface -import guillotina.schema -import sys _marker = object() diff --git a/guillotina/schema/interfaces.py b/guillotina/schema/interfaces.py index 7c57eaff4..e113d61f6 100644 --- a/guillotina/schema/interfaces.py +++ b/guillotina/schema/interfaces.py @@ -12,18 +12,14 @@ # ############################################################################## -from guillotina.schema._bootstrapfields import Bool -from guillotina.schema._bootstrapfields import Field -from guillotina.schema._bootstrapfields import Int -from guillotina.schema._bootstrapfields import Text -from guillotina.schema._bootstrapfields import TextLine -from guillotina.schema._bootstrapinterfaces import IContextAwareDefaultFactory -from guillotina.schema._bootstrapinterfaces import IFromUnicode -from guillotina.schema._messageid import _ -from zope.interface import Attribute -from zope.interface import Interface +from zope.interface import Attribute, Interface from zope.interface.common.mapping import IEnumerableMapping +from guillotina.schema._bootstrapfields import Bool, Field, Int, Text, TextLine +from guillotina.schema._bootstrapinterfaces import IContextAwareDefaultFactory, IFromUnicode +from guillotina.schema._messageid import _ + + __docformat__ = "reStructuredText" # pep 8 friendlyness diff --git a/guillotina/schema/tests/states.py b/guillotina/schema/tests/states.py index 097d3825d..850221e0e 100644 --- a/guillotina/schema/tests/states.py +++ b/guillotina/schema/tests/states.py @@ -12,10 +12,11 @@ # ############################################################################## # flake8: noqa -from guillotina.schema import Choice -from guillotina.schema import interfaces from zope.interface import implementer +from guillotina.schema import Choice, interfaces + + # This table is based on information from the United States Postal Service: # http://www.usps.com/ncsc/lookups/abbreviations.html#states _states = { diff --git a/guillotina/schema/tests/test__bootstrapfields.py b/guillotina/schema/tests/test__bootstrapfields.py index 834e0aaad..4d1423568 100644 --- a/guillotina/schema/tests/test__bootstrapfields.py +++ b/guillotina/schema/tests/test__bootstrapfields.py @@ -130,9 +130,10 @@ def _provoke(inst): self.assertRaises(ValueError, _provoke, inst) def test___get___w_defaultFactory_w_ICAF_w_check(self): - from guillotina.schema._bootstrapinterfaces import IContextAwareDefaultFactory from zope.interface import directlyProvides + from guillotina.schema._bootstrapinterfaces import IContextAwareDefaultFactory + _checked = [] def _check(inst, value): @@ -589,15 +590,17 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_ITextLine(self): - from guillotina.schema.interfaces import ITextLine from zope.interface.verify import verifyClass + from guillotina.schema.interfaces import ITextLine + verifyClass(ITextLine, self._getTargetClass()) def test_instance_conforms_to_ITextLine(self): - from guillotina.schema.interfaces import ITextLine from zope.interface.verify import verifyObject + from guillotina.schema.interfaces import ITextLine + verifyObject(ITextLine, self._makeOne()) def test_validate_wrong_types(self): @@ -784,8 +787,7 @@ def test_validate_max(self): self.assertRaises(TooBig, field.validate, 20) def test_validate_min_and_max(self): - from guillotina.schema.exceptions import TooBig - from guillotina.schema.exceptions import TooSmall + from guillotina.schema.exceptions import TooBig, TooSmall field = self._makeOne(min=0, max=10) field.validate(0) diff --git a/guillotina/schema/tests/test__field.py b/guillotina/schema/tests/test__field.py index 866d14ca8..d660a0ce3 100644 --- a/guillotina/schema/tests/test__field.py +++ b/guillotina/schema/tests/test__field.py @@ -25,15 +25,17 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IBytes(self): - from guillotina.schema.interfaces import IBytes from zope.interface.verify import verifyClass + from guillotina.schema.interfaces import IBytes + verifyClass(IBytes, self._getTargetClass()) def test_instance_conforms_to_IBytes(self): - from guillotina.schema.interfaces import IBytes from zope.interface.verify import verifyObject + from guillotina.schema.interfaces import IBytes + verifyObject(IBytes, self._makeOne()) def test_validate_wrong_types(self): @@ -91,15 +93,17 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IASCII(self): - from guillotina.schema.interfaces import IASCII from zope.interface.verify import verifyClass + from guillotina.schema.interfaces import IASCII + verifyClass(IASCII, self._getTargetClass()) def test_instance_conforms_to_IASCII(self): - from guillotina.schema.interfaces import IASCII from zope.interface.verify import verifyObject + from guillotina.schema.interfaces import IASCII + verifyObject(IASCII, self._makeOne()) def test_validate_wrong_types(self): @@ -143,15 +147,17 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IBytesLine(self): - from guillotina.schema.interfaces import IBytesLine from zope.interface.verify import verifyClass + from guillotina.schema.interfaces import IBytesLine + verifyClass(IBytesLine, self._getTargetClass()) def test_instance_conforms_to_IBytesLine(self): - from guillotina.schema.interfaces import IBytesLine from zope.interface.verify import verifyObject + from guillotina.schema.interfaces import IBytesLine + verifyObject(IBytesLine, self._makeOne()) def test_validate_wrong_types(self): @@ -203,15 +209,17 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IASCIILine(self): - from guillotina.schema.interfaces import IASCIILine from zope.interface.verify import verifyClass + from guillotina.schema.interfaces import IASCIILine + verifyClass(IASCIILine, self._getTargetClass()) def test_instance_conforms_to_IASCIILine(self): - from guillotina.schema.interfaces import IASCIILine from zope.interface.verify import verifyObject + from guillotina.schema.interfaces import IASCIILine + verifyObject(IASCIILine, self._makeOne()) def test_validate_wrong_types(self): @@ -239,8 +247,7 @@ def test_validate_not_required(self): self.assertRaises(InvalidValue, field.validate, "\xab\xde") def test_validate_required(self): - from guillotina.schema.exceptions import InvalidValue - from guillotina.schema.exceptions import RequiredMissing + from guillotina.schema.exceptions import InvalidValue, RequiredMissing field = self._makeOne(required=True) field.validate("") @@ -268,15 +275,17 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IFloat(self): - from guillotina.schema.interfaces import IFloat from zope.interface.verify import verifyClass + from guillotina.schema.interfaces import IFloat + verifyClass(IFloat, self._getTargetClass()) def test_instance_conforms_to_IFloat(self): - from guillotina.schema.interfaces import IFloat from zope.interface.verify import verifyObject + from guillotina.schema.interfaces import IFloat + verifyObject(IFloat, self._makeOne()) def test_validate_not_required(self): @@ -314,8 +323,7 @@ def test_validate_max(self): self.assertRaises(TooBig, field.validate, 20.7) def test_validate_min_and_max(self): - from guillotina.schema.exceptions import TooBig - from guillotina.schema.exceptions import TooSmall + from guillotina.schema.exceptions import TooBig, TooSmall field = self._makeOne(min=-0.6, max=10.1) field.validate(0.0) @@ -351,15 +359,17 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IDecimal(self): - from guillotina.schema.interfaces import IDecimal from zope.interface.verify import verifyClass + from guillotina.schema.interfaces import IDecimal + verifyClass(IDecimal, self._getTargetClass()) def test_instance_conforms_to_IDecimal(self): - from guillotina.schema.interfaces import IDecimal from zope.interface.verify import verifyObject + from guillotina.schema.interfaces import IDecimal + verifyObject(IDecimal, self._makeOne()) def test_validate_not_required(self): @@ -372,10 +382,10 @@ def test_validate_not_required(self): field.validate(None) def test_validate_required(self): - from guillotina.schema.exceptions import RequiredMissing - import decimal + from guillotina.schema.exceptions import RequiredMissing + field = self._makeOne(required=True) field.validate(decimal.Decimal("10.0")) field.validate(decimal.Decimal("0.93")) @@ -383,10 +393,10 @@ def test_validate_required(self): self.assertRaises(RequiredMissing, field.validate, None) def test_validate_min(self): - from guillotina.schema.exceptions import TooSmall - import decimal + from guillotina.schema.exceptions import TooSmall + field = self._makeOne(min=decimal.Decimal("10.5")) field.validate(decimal.Decimal("10.6")) field.validate(decimal.Decimal("20.2")) @@ -394,10 +404,10 @@ def test_validate_min(self): self.assertRaises(TooSmall, field.validate, decimal.Decimal("10.4")) def test_validate_max(self): - from guillotina.schema.exceptions import TooBig - import decimal + from guillotina.schema.exceptions import TooBig + field = self._makeOne(max=decimal.Decimal("10.5")) field.validate(decimal.Decimal("5.3")) field.validate(decimal.Decimal("-9.1")) @@ -405,11 +415,10 @@ def test_validate_max(self): self.assertRaises(TooBig, field.validate, decimal.Decimal("20.7")) def test_validate_min_and_max(self): - from guillotina.schema.exceptions import TooBig - from guillotina.schema.exceptions import TooSmall - import decimal + from guillotina.schema.exceptions import TooBig, TooSmall + field = self._makeOne(min=decimal.Decimal("-0.6"), max=decimal.Decimal("10.1")) field.validate(decimal.Decimal("0.0")) field.validate(decimal.Decimal("-0.03")) @@ -446,19 +455,22 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IDatetime(self): - from guillotina.schema.interfaces import IDatetime from zope.interface.verify import verifyClass + from guillotina.schema.interfaces import IDatetime + verifyClass(IDatetime, self._getTargetClass()) def test_instance_conforms_to_IDatetime(self): - from guillotina.schema.interfaces import IDatetime from zope.interface.verify import verifyObject + from guillotina.schema.interfaces import IDatetime + verifyObject(IDatetime, self._makeOne()) def test_validate_wrong_types(self): from datetime import date + from guillotina.schema.exceptions import WrongType field = self._makeOne() @@ -489,6 +501,7 @@ def test_validate_required(self): def test_validate_w_min(self): from datetime import datetime + from guillotina.schema.exceptions import TooSmall d1 = datetime(2000, 10, 1) @@ -500,6 +513,7 @@ def test_validate_w_min(self): def test_validate_w_max(self): from datetime import datetime + from guillotina.schema.exceptions import TooBig d1 = datetime(2000, 10, 1) @@ -512,8 +526,8 @@ def test_validate_w_max(self): def test_validate_w_min_and_max(self): from datetime import datetime - from guillotina.schema.exceptions import TooBig - from guillotina.schema.exceptions import TooSmall + + from guillotina.schema.exceptions import TooBig, TooSmall d1 = datetime(2000, 10, 1) d2 = datetime(2000, 10, 2) @@ -538,19 +552,22 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IDate(self): - from guillotina.schema.interfaces import IDate from zope.interface.verify import verifyClass + from guillotina.schema.interfaces import IDate + verifyClass(IDate, self._getTargetClass()) def test_instance_conforms_to_IDate(self): - from guillotina.schema.interfaces import IDate from zope.interface.verify import verifyObject + from guillotina.schema.interfaces import IDate + verifyObject(IDate, self._makeOne()) def test_validate_wrong_types(self): from datetime import datetime + from guillotina.schema.exceptions import WrongType field = self._makeOne() @@ -575,6 +592,7 @@ def test_validate_not_required(self): def test_validate_required(self): from datetime import datetime + from guillotina.schema.exceptions import RequiredMissing field = self._makeOne(required=True) @@ -582,8 +600,8 @@ def test_validate_required(self): self.assertRaises(RequiredMissing, field.validate, None) def test_validate_w_min(self): - from datetime import date - from datetime import datetime + from datetime import date, datetime + from guillotina.schema.exceptions import TooSmall d1 = date(2000, 10, 1) @@ -596,6 +614,7 @@ def test_validate_w_min(self): def test_validate_w_max(self): from datetime import date + from guillotina.schema.exceptions import TooBig d1 = date(2000, 10, 1) @@ -608,8 +627,8 @@ def test_validate_w_max(self): def test_validate_w_min_and_max(self): from datetime import date - from guillotina.schema.exceptions import TooBig - from guillotina.schema.exceptions import TooSmall + + from guillotina.schema.exceptions import TooBig, TooSmall d1 = date(2000, 10, 1) d2 = date(2000, 10, 2) @@ -634,15 +653,17 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_ITimedelta(self): - from guillotina.schema.interfaces import ITimedelta from zope.interface.verify import verifyClass + from guillotina.schema.interfaces import ITimedelta + verifyClass(ITimedelta, self._getTargetClass()) def test_instance_conforms_to_ITimedelta(self): - from guillotina.schema.interfaces import ITimedelta from zope.interface.verify import verifyObject + from guillotina.schema.interfaces import ITimedelta + verifyObject(ITimedelta, self._makeOne()) def test_validate_not_required(self): @@ -654,6 +675,7 @@ def test_validate_not_required(self): def test_validate_required(self): from datetime import timedelta + from guillotina.schema.exceptions import RequiredMissing field = self._makeOne(required=True) @@ -662,6 +684,7 @@ def test_validate_required(self): def test_validate_min(self): from datetime import timedelta + from guillotina.schema.exceptions import TooSmall t1 = timedelta(hours=2) @@ -673,6 +696,7 @@ def test_validate_min(self): def test_validate_max(self): from datetime import timedelta + from guillotina.schema.exceptions import TooBig t1 = timedelta(minutes=1) @@ -685,8 +709,8 @@ def test_validate_max(self): def test_validate_min_and_max(self): from datetime import timedelta - from guillotina.schema.exceptions import TooBig - from guillotina.schema.exceptions import TooSmall + + from guillotina.schema.exceptions import TooBig, TooSmall t1 = timedelta(days=1) t2 = timedelta(days=2) @@ -711,15 +735,17 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_ITime(self): - from guillotina.schema.interfaces import ITime from zope.interface.verify import verifyClass + from guillotina.schema.interfaces import ITime + verifyClass(ITime, self._getTargetClass()) def test_instance_conforms_to_ITime(self): - from guillotina.schema.interfaces import ITime from zope.interface.verify import verifyObject + from guillotina.schema.interfaces import ITime + verifyObject(ITime, self._makeOne()) def test_validate_not_required(self): @@ -731,6 +757,7 @@ def test_validate_not_required(self): def test_validate_required(self): from datetime import time + from guillotina.schema.exceptions import RequiredMissing field = self._makeOne(required=True) @@ -739,6 +766,7 @@ def test_validate_required(self): def test_validate_min(self): from datetime import time + from guillotina.schema.exceptions import TooSmall t1 = time(12, 15, 37) @@ -751,6 +779,7 @@ def test_validate_min(self): def test_validate_max(self): from datetime import time + from guillotina.schema.exceptions import TooBig t1 = time(12, 15, 37) @@ -763,8 +792,8 @@ def test_validate_max(self): def test_validate_min_and_max(self): from datetime import time - from guillotina.schema.exceptions import TooBig - from guillotina.schema.exceptions import TooSmall + + from guillotina.schema.exceptions import TooBig, TooSmall t1 = time(12, 15, 37) t2 = time(12, 25, 18) @@ -799,15 +828,17 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IChoice(self): - from guillotina.schema.interfaces import IChoice from zope.interface.verify import verifyClass + from guillotina.schema.interfaces import IChoice + verifyClass(IChoice, self._getTargetClass()) def test_instance_conforms_to_IChoice(self): - from guillotina.schema.interfaces import IChoice from zope.interface.verify import verifyObject + from guillotina.schema.interfaces import IChoice + verifyObject(IChoice, self._makeOne(values=[1, 2, 3])) def test_ctor_wo_values_vocabulary_or_source(self): @@ -862,10 +893,10 @@ def _provoke(bound): self.assertRaises(ValidationError, _provoke, bound) def test_bind_w_voc_not_ICSB(self): - from guillotina.schema.interfaces import IBaseVocabulary - from guillotina.schema.interfaces import ISource from zope.interface import implementer + from guillotina.schema.interfaces import IBaseVocabulary, ISource + @implementer(IBaseVocabulary) @implementer(ISource) class Vocab(object): @@ -878,10 +909,10 @@ def __init__(self): self.assertTrue(target.vocabulary is source.vocabulary) def test_bind_w_voc_is_ICSB(self): - from guillotina.schema.interfaces import IContextSourceBinder - from guillotina.schema.interfaces import ISource from zope.interface import implementer + from guillotina.schema.interfaces import IContextSourceBinder, ISource + @implementer(IContextSourceBinder) @implementer(ISource) class Vocab(object): @@ -900,9 +931,10 @@ def __call__(self, context): self.assertEqual(target.vocabulary.context, instance) def test_bind_w_voc_is_ICSB_but_not_ISource(self): - from guillotina.schema.interfaces import IContextSourceBinder from zope.interface import implementer + from guillotina.schema.interfaces import IContextSourceBinder + @implementer(IContextSourceBinder) class Vocab(object): def __init__(self, context): @@ -984,9 +1016,10 @@ def test__validate_w_named_vocabulary(self): self.assertRaises(ConstraintNotSatisfied, choose._validate, 42) def test__validate_source_is_ICSB_unbound(self): - from guillotina.schema.interfaces import IContextSourceBinder from zope.interface import implementer + from guillotina.schema.interfaces import IContextSourceBinder + @implementer(IContextSourceBinder) class SampleContextSourceBinder(object): def __call__(self, context): @@ -996,10 +1029,11 @@ def __call__(self, context): self.assertRaises(TypeError, choice.validate, 1) def test__validate_source_is_ICSB_bound(self): + from zope.interface import implementer + from guillotina.schema.exceptions import ConstraintNotSatisfied from guillotina.schema.interfaces import IContextSourceBinder from guillotina.schema.tests.test_vocabulary import _makeSampleVocabulary - from zope.interface import implementer @implementer(IContextSourceBinder) class SampleContextSourceBinder(object): @@ -1027,15 +1061,17 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IURI(self): - from guillotina.schema.interfaces import IURI from zope.interface.verify import verifyClass + from guillotina.schema.interfaces import IURI + verifyClass(IURI, self._getTargetClass()) def test_instance_conforms_to_IURI(self): - from guillotina.schema.interfaces import IURI from zope.interface.verify import verifyObject + from guillotina.schema.interfaces import IURI + verifyObject(IURI, self._makeOne()) def test_validate_wrong_types(self): @@ -1066,8 +1102,7 @@ def test_validate_required(self): self.assertRaises(RequiredMissing, field.validate, None) def test_validate_not_a_uri(self): - from guillotina.schema.exceptions import ConstraintNotSatisfied - from guillotina.schema.exceptions import InvalidURI + from guillotina.schema.exceptions import ConstraintNotSatisfied, InvalidURI field = self._makeOne() self.assertRaises(InvalidURI, field.validate, "") @@ -1080,8 +1115,7 @@ def test_from_unicode_ok(self): self.assertEqual(field.from_unicode("http://example.com/"), "http://example.com/") def test_from_unicode_invalid(self): - from guillotina.schema.exceptions import ConstraintNotSatisfied - from guillotina.schema.exceptions import InvalidURI + from guillotina.schema.exceptions import ConstraintNotSatisfied, InvalidURI field = self._makeOne() self.assertRaises(InvalidURI, field.from_unicode, "") @@ -1099,15 +1133,17 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IDottedName(self): - from guillotina.schema.interfaces import IDottedName from zope.interface.verify import verifyClass + from guillotina.schema.interfaces import IDottedName + verifyClass(IDottedName, self._getTargetClass()) def test_instance_conforms_to_IDottedName(self): - from guillotina.schema.interfaces import IDottedName from zope.interface.verify import verifyObject + from guillotina.schema.interfaces import IDottedName + verifyObject(IDottedName, self._makeOne()) def test_ctor_defaults(self): @@ -1175,8 +1211,7 @@ def test_validate_w_max_dots(self): self.assertRaises(InvalidDottedName, field.validate, "moar.dotted.name") def test_validate_not_a_dotted_name(self): - from guillotina.schema.exceptions import ConstraintNotSatisfied - from guillotina.schema.exceptions import InvalidDottedName + from guillotina.schema.exceptions import ConstraintNotSatisfied, InvalidDottedName field = self._makeOne() self.assertRaises(InvalidDottedName, field.validate, "") @@ -1188,8 +1223,7 @@ def test_from_unicode_dotted_name_ok(self): self.assertEqual(field.from_unicode("dotted.name"), "dotted.name") def test_from_unicode_invalid(self): - from guillotina.schema.exceptions import ConstraintNotSatisfied - from guillotina.schema.exceptions import InvalidDottedName + from guillotina.schema.exceptions import ConstraintNotSatisfied, InvalidDottedName field = self._makeOne() self.assertRaises(InvalidDottedName, field.from_unicode, "") @@ -1206,15 +1240,17 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IId(self): - from guillotina.schema.interfaces import IId from zope.interface.verify import verifyClass + from guillotina.schema.interfaces import IId + verifyClass(IId, self._getTargetClass()) def test_instance_conforms_to_IId(self): - from guillotina.schema.interfaces import IId from zope.interface.verify import verifyObject + from guillotina.schema.interfaces import IId + verifyObject(IId, self._makeOne()) def test_validate_wrong_types(self): @@ -1247,8 +1283,7 @@ def test_validate_required(self): self.assertRaises(RequiredMissing, field.validate, None) def test_validate_not_a_uri(self): - from guillotina.schema.exceptions import ConstraintNotSatisfied - from guillotina.schema.exceptions import InvalidId + from guillotina.schema.exceptions import ConstraintNotSatisfied, InvalidId field = self._makeOne() self.assertRaises(InvalidId, field.validate, "") @@ -1265,8 +1300,7 @@ def test_from_unicode_dotted_name_ok(self): self.assertEqual(field.from_unicode("dotted.name"), "dotted.name") def test_from_unicode_invalid(self): - from guillotina.schema.exceptions import ConstraintNotSatisfied - from guillotina.schema.exceptions import InvalidId + from guillotina.schema.exceptions import ConstraintNotSatisfied, InvalidId field = self._makeOne() self.assertRaises(InvalidId, field.from_unicode, "") @@ -1284,19 +1318,22 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IInterfaceField(self): - from guillotina.schema.interfaces import IInterfaceField from zope.interface.verify import verifyClass + from guillotina.schema.interfaces import IInterfaceField + verifyClass(IInterfaceField, self._getTargetClass()) def test_instance_conforms_to_IInterfaceField(self): - from guillotina.schema.interfaces import IInterfaceField from zope.interface.verify import verifyObject + from guillotina.schema.interfaces import IInterfaceField + verifyObject(IInterfaceField, self._makeOne()) def test_validate_wrong_types(self): from datetime import date + from guillotina.schema.exceptions import WrongType field = self._makeOne() @@ -1323,9 +1360,10 @@ class DummyInterface(Interface): field.validate(None) def test_validate_required(self): - from guillotina.schema.exceptions import RequiredMissing from zope.interface import Interface + from guillotina.schema.exceptions import RequiredMissing + class DummyInterface(Interface): pass @@ -1409,15 +1447,17 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_ITuple(self): - from guillotina.schema.interfaces import ITuple from zope.interface.verify import verifyClass + from guillotina.schema.interfaces import ITuple + verifyClass(ITuple, self._getTargetClass()) def test_instance_conforms_to_ITuple(self): - from guillotina.schema.interfaces import ITuple from zope.interface.verify import verifyObject + from guillotina.schema.interfaces import ITuple + verifyObject(ITuple, self._makeOne()) def test_validate_wrong_types(self): @@ -1469,8 +1509,7 @@ def test_validate_max_length(self): self.assertRaises(TooLong, field.validate, (1, 2, 3)) def test_validate_min_length_and_max_length(self): - from guillotina.schema.exceptions import TooLong - from guillotina.schema.exceptions import TooShort + from guillotina.schema.exceptions import TooLong, TooShort field = self._makeOne(min_length=1, max_length=2) field.validate((1,)) @@ -1489,15 +1528,17 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IList(self): - from guillotina.schema.interfaces import IList from zope.interface.verify import verifyClass + from guillotina.schema.interfaces import IList + verifyClass(IList, self._getTargetClass()) def test_instance_conforms_to_IList(self): - from guillotina.schema.interfaces import IList from zope.interface.verify import verifyObject + from guillotina.schema.interfaces import IList + verifyObject(IList, self._makeOne()) def test_validate_wrong_types(self): @@ -1550,8 +1591,7 @@ def test_validate_max_length(self): self.assertRaises(TooLong, field.validate, [1, 2, 3]) def test_validate_min_length_and_max_length(self): - from guillotina.schema.exceptions import TooLong - from guillotina.schema.exceptions import TooShort + from guillotina.schema.exceptions import TooLong, TooShort field = self._makeOne(min_length=1, max_length=2) field.validate([1]) @@ -1570,15 +1610,17 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_ISet(self): - from guillotina.schema.interfaces import ISet from zope.interface.verify import verifyClass + from guillotina.schema.interfaces import ISet + verifyClass(ISet, self._getTargetClass()) def test_instance_conforms_to_ISet(self): - from guillotina.schema.interfaces import ISet from zope.interface.verify import verifyObject + from guillotina.schema.interfaces import ISet + verifyObject(ISet, self._makeOne()) def test_ctor_disallows_unique(self): @@ -1639,8 +1681,7 @@ def test_validate_max_length(self): self.assertRaises(TooLong, field.validate, set((1, 2, 3))) def test_validate_min_length_and_max_length(self): - from guillotina.schema.exceptions import TooLong - from guillotina.schema.exceptions import TooShort + from guillotina.schema.exceptions import TooLong, TooShort field = self._makeOne(min_length=1, max_length=2) field.validate(set((1,))) @@ -1659,15 +1700,17 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IFrozenSet(self): - from guillotina.schema.interfaces import IFrozenSet from zope.interface.verify import verifyClass + from guillotina.schema.interfaces import IFrozenSet + verifyClass(IFrozenSet, self._getTargetClass()) def test_instance_conforms_to_IFrozenSet(self): - from guillotina.schema.interfaces import IFrozenSet from zope.interface.verify import verifyObject + from guillotina.schema.interfaces import IFrozenSet + verifyObject(IFrozenSet, self._makeOne()) def test_ctor_disallows_unique(self): @@ -1725,8 +1768,7 @@ def test_validate_max_length(self): self.assertRaises(TooLong, field.validate, frozenset((1, 2, 3))) def test_validate_min_length_and_max_length(self): - from guillotina.schema.exceptions import TooLong - from guillotina.schema.exceptions import TooShort + from guillotina.schema.exceptions import TooLong, TooShort field = self._makeOne(min_length=1, max_length=2) field.validate(frozenset((1,))) @@ -1775,11 +1817,10 @@ def _getErrors(self, f, *args, **kw): self.fail("Expected WrongContainedType Error") def _makeCycles(self): - from guillotina.schema import List - from guillotina.schema import Object + from zope.interface import Interface, implementer + + from guillotina.schema import List, Object from guillotina.schema._messageid import _ - from zope.interface import implementer - from zope.interface import Interface class IUnit(Interface): """A schema that participate to a cycle""" @@ -1816,15 +1857,17 @@ def __init__(self, unit): return IUnit, Person, Unit def test_class_conforms_to_IObject(self): - from guillotina.schema.interfaces import IObject from zope.interface.verify import verifyClass + from guillotina.schema.interfaces import IObject + verifyClass(IObject, self._getTargetClass()) def test_instance_conforms_to_IObject(self): - from guillotina.schema.interfaces import IObject from zope.interface.verify import verifyObject + from guillotina.schema.interfaces import IObject + verifyObject(IObject, self._makeOne()) def test_ctor_w_bad_schema(self): @@ -1856,13 +1899,16 @@ def test__validate_w_value_not_providing_schema(self): self.assertRaises(SchemaNotProvided, objf.validate, object()) def test__validate_w_value_providing_invalid_schema(self): - from guillotina.schema._bootstrapfields import Text - from guillotina.schema.exceptions import RequiredMissing - from guillotina.schema.exceptions import SchemaNotProvided - from guillotina.schema.exceptions import WrongContainedType - from guillotina.schema.exceptions import WrongType from zope.interface import implementer + from guillotina.schema._bootstrapfields import Text + from guillotina.schema.exceptions import ( + RequiredMissing, + SchemaNotProvided, + WrongContainedType, + WrongType, + ) + schema = self._makeSchema(foo=Text()) objf = self._makeOne(schema) @@ -1877,11 +1923,11 @@ def test__validate_w_value_providing_invalid_schema(self): self.assertRaises(SchemaNotProvided, objf.validate, {"foo": "val", "bar": "val"}) def test__validate_w_value_providing_schema_but_missing_fields(self): - from guillotina.schema._bootstrapfields import Text - from guillotina.schema.exceptions import RequiredMissing - from guillotina.schema.exceptions import WrongContainedType from zope.interface import implementer + from guillotina.schema._bootstrapfields import Text + from guillotina.schema.exceptions import RequiredMissing, WrongContainedType + schema = self._makeSchema(foo=Text(required=True), bar=Text(required=True)) @implementer(schema) @@ -1897,12 +1943,11 @@ class Broken(object): self.assertTrue(isinstance(err, RequiredMissing)) def test__validate_w_value_providing_schema_but_invalid_fields(self): - from guillotina.schema._bootstrapfields import Text - from guillotina.schema.exceptions import RequiredMissing - from guillotina.schema.exceptions import WrongContainedType - from guillotina.schema.exceptions import WrongType from zope.interface import implementer + from guillotina.schema._bootstrapfields import Text + from guillotina.schema.exceptions import RequiredMissing, WrongContainedType, WrongType + schema = self._makeSchema(foo=Text(required=True), bar=Text(required=True)) @implementer(schema) @@ -1923,9 +1968,10 @@ class Broken(object): self.assertEqual(err.args[:3], (1, str, "bar")) def test__validate_w_value_providing_schema(self): + from zope.interface import implementer + from guillotina.schema._bootstrapfields import Text from guillotina.schema._field import Choice - from zope.interface import implementer schema = self._makeSchema(foo=Text(), bar=Text(), baz=Choice(values=[1, 2, 3])) @@ -1939,10 +1985,9 @@ class OK(object): objf.validate(OK()) # doesn't raise def test__validate_interface_inheritance(self): - from guillotina.schema import Int - from guillotina.schema import Object - from zope.interface import implementer - from zope.interface import Interface + from zope.interface import Interface, implementer + + from guillotina.schema import Int, Object class IFoo(Interface): foo = Int() @@ -2009,15 +2054,17 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IDict(self): - from guillotina.schema.interfaces import IDict from zope.interface.verify import verifyClass + from guillotina.schema.interfaces import IDict + verifyClass(IDict, self._getTargetClass()) def test_instance_conforms_to_IDict(self): - from guillotina.schema.interfaces import IDict from zope.interface.verify import verifyObject + from guillotina.schema.interfaces import IDict + verifyObject(IDict, self._makeOne()) def test_ctor_key_type_not_IField(self): @@ -2094,8 +2141,7 @@ def test_validate_max_length(self): self.assertRaises(TooLong, field.validate, {1: "a", 2: "b", 3: "c"}) def test_validate_min_length_and_max_length(self): - from guillotina.schema.exceptions import TooLong - from guillotina.schema.exceptions import TooShort + from guillotina.schema.exceptions import TooLong, TooShort field = self._makeOne(min_length=1, max_length=2) field.validate({1: "a"}) @@ -2137,22 +2183,22 @@ def _getTargetClass(self): return UnionField def _makeOne(self, *args, **kw): - from guillotina.schema._field import Int - from guillotina.schema._field import List - from guillotina.schema._field import Text + from guillotina.schema._field import Int, List, Text return self._getTargetClass()(Text(**kw), Int(**kw), List(**kw), *args, **kw) def test_class_conforms_to_IUnionField(self): - from guillotina.schema.interfaces import IUnionField from zope.interface.verify import verifyClass + from guillotina.schema.interfaces import IUnionField + verifyClass(IUnionField, self._getTargetClass()) def test_instance_conforms_to_IUnionField(self): - from guillotina.schema.interfaces import IUnionField from zope.interface.verify import verifyObject + from guillotina.schema.interfaces import IUnionField + verifyObject(IUnionField, self._makeOne()) def test_validate_wrong_types(self): @@ -2203,9 +2249,10 @@ class DummyInstance(object): def _makeSampleVocabulary(): - from guillotina.schema.interfaces import IVocabulary from zope.interface import implementer + from guillotina.schema.interfaces import IVocabulary + class SampleTerm(object): pass diff --git a/guillotina/schema/tests/test_accessors.py b/guillotina/schema/tests/test_accessors.py index c09ea0257..4668dfc17 100644 --- a/guillotina/schema/tests/test_accessors.py +++ b/guillotina/schema/tests/test_accessors.py @@ -38,9 +38,10 @@ def test_ctor_not_created_inside_interface(self): self.assertEqual(wrapped.__doc__, "get Hmm") def test_ctor_created_inside_interface(self): - from guillotina.schema import Text from zope.interface import Interface + from guillotina.schema import Text + field = Text(title="Hmm") class IFoo(Interface): @@ -51,16 +52,15 @@ class IFoo(Interface): self.assertEqual(getter.__doc__, "get Hmm") def test___provides___w_field_no_provides(self): - from zope.interface import implementedBy - from zope.interface import providedBy + from zope.interface import implementedBy, providedBy wrapped = self._makeOne(object()) self.assertEqual(list(providedBy(wrapped)), list(implementedBy(self._getTargetClass()))) def test___provides___w_field_w_provides(self): + from zope.interface import implementedBy, providedBy + from guillotina.schema import Text - from zope.interface import implementedBy - from zope.interface import providedBy field = Text() field_provides = list(providedBy(field)) @@ -151,9 +151,10 @@ def getter(self): self.assertEqual(getter.query(Foo()), "123") def test_set_readonly(self): - from guillotina.schema import Text from zope.interface import Interface + from guillotina.schema import Text + field = Text(readonly=True) class IFoo(Interface): @@ -241,9 +242,10 @@ def test_ctor_not_created_inside_interface(self): self.assertEqual(wrapped.__doc__, "set Hmm") def test_ctor_created_inside_interface(self): - from guillotina.schema import Text from zope.interface import Interface + from guillotina.schema import Text + field = Text(title="Hmm") class IFoo(Interface): @@ -274,9 +276,10 @@ def _callFUT(self, *args, **kw): return accessors(*args, **kw) def test_w_only_read_accessor(self): - from guillotina.schema import Text from zope.interface import Interface + from guillotina.schema import Text + field = Text(title="Hmm", readonly=True) class IFoo(Interface): @@ -294,9 +297,10 @@ class IFoo(Interface): self.assertEqual(info["kwargs"], None) def test_w_read_and_write_accessors(self): - from guillotina.schema import Text from zope.interface import Interface + from guillotina.schema import Text + field = Text(title="Hmm") class IFoo(Interface): diff --git a/guillotina/schema/tests/test_fieldproperty.py b/guillotina/schema/tests/test_fieldproperty.py index b10bbaa90..d58727e68 100644 --- a/guillotina/schema/tests/test_fieldproperty.py +++ b/guillotina/schema/tests/test_fieldproperty.py @@ -590,11 +590,10 @@ def test_field_event(self): def _getSchema(): - from guillotina.schema import Bytes - from guillotina.schema import Float - from guillotina.schema import Text from zope.interface import Interface + from guillotina.schema import Bytes, Float, Text + class Schema(Interface): title = Text(description="Short summary", default="say something") weight = Float(min=0.0) @@ -608,8 +607,7 @@ class CreateFieldPropertiesTests(unittest.TestCase): """Testing ..fieldproperty.createFieldProperties.""" def test_creates_fieldproperties_on_class(self): - from guillotina.schema.fieldproperty import createFieldProperties - from guillotina.schema.fieldproperty import FieldProperty + from guillotina.schema.fieldproperty import FieldProperty, createFieldProperties schema = _getSchema() diff --git a/guillotina/schema/tests/test_interfaces.py b/guillotina/schema/tests/test_interfaces.py index ae10c4142..b4409f96f 100644 --- a/guillotina/schema/tests/test_interfaces.py +++ b/guillotina/schema/tests/test_interfaces.py @@ -23,11 +23,7 @@ def test_non_fields(self): self.assertEqual(self._callFUT(object()), False) def test_w_normal_fields(self): - from guillotina.schema import Bytes - from guillotina.schema import Decimal - from guillotina.schema import Float - from guillotina.schema import Int - from guillotina.schema import Text + from guillotina.schema import Bytes, Decimal, Float, Int, Text self.assertEqual(self._callFUT(Text()), True) self.assertEqual(self._callFUT(Bytes()), True) @@ -36,9 +32,10 @@ def test_w_normal_fields(self): self.assertEqual(self._callFUT(Decimal()), True) def test_w_explicitly_provided(self): - from guillotina.schema.interfaces import IField from zope.interface import directlyProvides + from guillotina.schema.interfaces import IField + class Foo(object): pass @@ -64,11 +61,7 @@ def test_w_non_fields(self): self.assertEqual(self._callFUT([object()]), False) def test_w_fields(self): - from guillotina.schema import Bytes - from guillotina.schema import Decimal - from guillotina.schema import Float - from guillotina.schema import Int - from guillotina.schema import Text + from guillotina.schema import Bytes, Decimal, Float, Int, Text self.assertEqual(self._callFUT([Text()]), True) self.assertEqual(self._callFUT([Bytes()]), True) @@ -78,11 +71,7 @@ def test_w_fields(self): self.assertEqual(self._callFUT([Text(), Bytes(), Int(), Float(), Decimal()]), True) def test_w_mixed(self): - from guillotina.schema import Bytes - from guillotina.schema import Decimal - from guillotina.schema import Float - from guillotina.schema import Int - from guillotina.schema import Text + from guillotina.schema import Bytes, Decimal, Float, Int, Text self.assertEqual(self._callFUT([Text(), 0]), False) self.assertEqual(self._callFUT([Text(), Bytes(), Int(), Float(), Decimal(), 0]), False) diff --git a/guillotina/schema/tests/test_schema.py b/guillotina/schema/tests/test_schema.py index 524e493d0..b8954c926 100644 --- a/guillotina/schema/tests/test_schema.py +++ b/guillotina/schema/tests/test_schema.py @@ -12,15 +12,16 @@ # ############################################################################## # flake8: noqa -from zope.interface.interface import InterfaceClass - import unittest +from zope.interface.interface import InterfaceClass + def _makeSchema(): - from guillotina.schema import Bytes from zope.interface import Interface + from guillotina.schema import Bytes + return InterfaceClass( "ISchemaTest", (Interface,), @@ -153,9 +154,10 @@ class IEmpty(Interface): self.assertEqual(len(errors), 0) def test_schema_with_field_errors(self): + from zope.interface import Interface + from guillotina.schema import Text from guillotina.schema.exceptions import SchemaNotFullyImplemented - from zope.interface import Interface class IWithRequired(Interface): must = Text(required=True) @@ -166,8 +168,7 @@ class IWithRequired(Interface): self.assertEqual(errors[0][1].__class__, SchemaNotFullyImplemented) def test_schema_with_invariant_errors(self): - from zope.interface import Interface - from zope.interface import invariant + from zope.interface import Interface, invariant from zope.interface.exceptions import Invalid class IWithFailingInvariant(Interface): @@ -181,8 +182,7 @@ def _epic_fail(obj): self.assertEqual(errors[0][1].__class__, Invalid) def test_schema_with_invariant_ok(self): - from zope.interface import Interface - from zope.interface import invariant + from zope.interface import Interface, invariant class IWithPassingInvariant(Interface): @invariant @@ -200,8 +200,7 @@ def _callFUT(self, schema, object): return getSchemaValidationErrors(schema, object) def test_schema_wo_fields(self): - from zope.interface import Attribute - from zope.interface import Interface + from zope.interface import Attribute, Interface class INoFields(Interface): def method(): @@ -213,9 +212,10 @@ def method(): self.assertEqual(len(errors), 0) def test_schema_with_fields_ok(self): - from guillotina.schema import Text from zope.interface import Interface + from guillotina.schema import Text + class IWithFields(Interface): foo = Text() bar = Text() @@ -228,9 +228,10 @@ class Obj(object): self.assertEqual(len(errors), 0) def test_schema_with_missing_field(self): + from zope.interface import Interface + from guillotina.schema import Text from guillotina.schema.exceptions import SchemaNotFullyImplemented - from zope.interface import Interface class IWithRequired(Interface): must = Text(required=True) @@ -241,9 +242,10 @@ class IWithRequired(Interface): self.assertEqual(errors[0][1].__class__, SchemaNotFullyImplemented) def test_schema_with_invalid_field(self): + from zope.interface import Interface + from guillotina.schema import Int from guillotina.schema.exceptions import TooSmall - from zope.interface import Interface class IWithMinium(Interface): value = Int(required=True, min=0) diff --git a/guillotina/schema/tests/test_states.py b/guillotina/schema/tests/test_states.py index 27c271a2b..d828b48b5 100644 --- a/guillotina/schema/tests/test_states.py +++ b/guillotina/schema/tests/test_states.py @@ -18,8 +18,7 @@ class StateSelectionTest(unittest.TestCase): def setUp(self): from guillotina.schema.tests.states import StateVocabulary - from guillotina.schema.vocabulary import _clear - from guillotina.schema.vocabulary import getVocabularyRegistry + from guillotina.schema.vocabulary import _clear, getVocabularyRegistry _clear() vr = getVocabularyRegistry() @@ -31,9 +30,10 @@ def tearDown(self): _clear() def _makeSchema(self): + from zope.interface import Interface + from guillotina.schema import Choice from guillotina.schema.tests.states import StateVocabulary - from zope.interface import Interface class IBirthInfo(Interface): state1 = Choice( @@ -58,9 +58,10 @@ class IBirthInfo(Interface): return IBirthInfo def test_default_presentation(self): - from guillotina.schema.interfaces import IVocabulary from zope.interface.verify import verifyObject + from guillotina.schema.interfaces import IVocabulary + schema = self._makeSchema() field = schema.getDescriptionFor("state1") bound = field.bind(object()) @@ -68,9 +69,10 @@ def test_default_presentation(self): self.assertEqual(bound.vocabulary.getTerm("VA").title, "Virginia") def test_contains(self): + from zope.interface.verify import verifyObject + from guillotina.schema.interfaces import IVocabulary from guillotina.schema.tests.states import StateVocabulary - from zope.interface.verify import verifyObject vocab = StateVocabulary() self.assertTrue(verifyObject(IVocabulary, vocab)) @@ -88,9 +90,10 @@ def test_contains(self): self.assertEqual(L, L2) def test_prebound_vocabulary(self): - from guillotina.schema.interfaces import IVocabulary from zope.interface.verify import verifyObject + from guillotina.schema.interfaces import IVocabulary + schema = self._makeSchema() field = schema.getDescriptionFor("state3") # type: ignore bound = field.bind(None) diff --git a/guillotina/schema/tests/test_vocabulary.py b/guillotina/schema/tests/test_vocabulary.py index 1e6168254..af92e1bc3 100644 --- a/guillotina/schema/tests/test_vocabulary.py +++ b/guillotina/schema/tests/test_vocabulary.py @@ -25,15 +25,17 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_ITokenizedTerm(self): - from guillotina.schema.interfaces import ITokenizedTerm from zope.interface.verify import verifyClass + from guillotina.schema.interfaces import ITokenizedTerm + verifyClass(ITokenizedTerm, self._getTargetClass()) def test_instance_conforms_to_ITokenizedTerm(self): - from guillotina.schema.interfaces import ITokenizedTerm from zope.interface.verify import verifyObject + from guillotina.schema.interfaces import ITokenizedTerm + verifyObject(ITokenizedTerm, self._makeOne("VALUE")) def test_ctor_defaults(self): @@ -73,21 +75,24 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_class_conforms_to_IVocabularyTokenized(self): - from guillotina.schema.interfaces import IVocabularyTokenized from zope.interface.verify import verifyClass + from guillotina.schema.interfaces import IVocabularyTokenized + verifyClass(IVocabularyTokenized, self._getTargetClass()) def test_instance_conforms_to_IVocabularyTokenized(self): - from guillotina.schema.interfaces import IVocabularyTokenized from zope.interface.verify import verifyObject + from guillotina.schema.interfaces import IVocabularyTokenized + verifyObject(IVocabularyTokenized, self._makeOne(())) def test_ctor_additional_interfaces(self): - from guillotina.schema.vocabulary import SimpleTerm from zope.interface import Interface + from guillotina.schema.vocabulary import SimpleTerm + class IStupid(Interface): pass @@ -105,9 +110,10 @@ class IStupid(Interface): self.assertTrue(vocabulary.getTermByToken(term.token) is term) def test_fromValues(self): - from guillotina.schema.interfaces import ITokenizedTerm from zope.interface import Interface + from guillotina.schema.interfaces import ITokenizedTerm + class IStupid(Interface): pass @@ -121,9 +127,10 @@ class IStupid(Interface): self.assertTrue(value in vocabulary) def test_fromItems(self): - from guillotina.schema.interfaces import ITokenizedTerm from zope.interface import Interface + from guillotina.schema.interfaces import ITokenizedTerm + class IStupid(Interface): pass @@ -240,12 +247,11 @@ def tree_vocab_3(self): return self._getTargetClass().fromDict(self.business_tree()) def test_implementation(self): - from guillotina.schema.interfaces import ITreeVocabulary - from guillotina.schema.interfaces import IVocabulary - from guillotina.schema.interfaces import IVocabularyTokenized from zope.interface.common.mapping import IEnumerableMapping from zope.interface.verify import verifyObject + from guillotina.schema.interfaces import ITreeVocabulary, IVocabulary, IVocabularyTokenized + for v in [self.tree_vocab_2(), self.tree_vocab_3()]: self.assertTrue(verifyObject(IEnumerableMapping, v)) self.assertTrue(verifyObject(IVocabulary, v)) @@ -508,8 +514,7 @@ def tearDown(self): _clear() def test_setVocabularyRegistry(self): - from guillotina.schema.vocabulary import getVocabularyRegistry - from guillotina.schema.vocabulary import setVocabularyRegistry + from guillotina.schema.vocabulary import getVocabularyRegistry, setVocabularyRegistry r = _makeDummyRegistry() setVocabularyRegistry(r) @@ -526,9 +531,10 @@ def test_getVocabularyRegistry(self): def _makeSampleVocabulary(): - from guillotina.schema.interfaces import IVocabulary from zope.interface import implementer + from guillotina.schema.interfaces import IVocabulary + class SampleTerm(object): pass diff --git a/guillotina/schema/utils.py b/guillotina/schema/utils.py index 50960443b..03e623267 100644 --- a/guillotina/schema/utils.py +++ b/guillotina/schema/utils.py @@ -1,4 +1,5 @@ from copy import deepcopy + from guillotina.schema.interfaces import IContextAwareDefaultFactory diff --git a/guillotina/schema/vocabulary.py b/guillotina/schema/vocabulary.py index 456d52bba..9c8b44554 100644 --- a/guillotina/schema/vocabulary.py +++ b/guillotina/schema/vocabulary.py @@ -15,13 +15,17 @@ """ from collections import OrderedDict -from guillotina.schema.interfaces import ITitledTokenizedTerm -from guillotina.schema.interfaces import ITokenizedTerm -from guillotina.schema.interfaces import ITreeVocabulary -from guillotina.schema.interfaces import IVocabularyRegistry -from guillotina.schema.interfaces import IVocabularyTokenized -from zope.interface import directlyProvides -from zope.interface import implementer + +from zope.interface import directlyProvides, implementer + +from guillotina.schema.interfaces import ( + ITitledTokenizedTerm, + ITokenizedTerm, + ITreeVocabulary, + IVocabularyRegistry, + IVocabularyTokenized, +) + # simple vocabularies performing enumerated-like tasks _marker = object() diff --git a/guillotina/security/__init__.py b/guillotina/security/__init__.py index fdc3108ae..244a554e7 100644 --- a/guillotina/security/__init__.py +++ b/guillotina/security/__init__.py @@ -1,3 +1,4 @@ from . import cache + security_map_cache = cache.SecurityMapCacheManager() diff --git a/guillotina/security/permission.py b/guillotina/security/permission.py index 8e5996e89..bfe046e13 100644 --- a/guillotina/security/permission.py +++ b/guillotina/security/permission.py @@ -1,6 +1,7 @@ +from zope.interface import implementer + from guillotina.component import get_utilities_for from guillotina.interfaces import IPermission -from zope.interface import implementer @implementer(IPermission) diff --git a/guillotina/security/policy.py b/guillotina/security/policy.py index 9f9546dc4..907c64731 100644 --- a/guillotina/security/policy.py +++ b/guillotina/security/policy.py @@ -1,30 +1,33 @@ +from typing import Dict, List, Optional, Union + +from lru import LRU + from guillotina import configure from guillotina.auth.users import SystemUser -from guillotina.component import get_utility -from guillotina.component import query_adapter +from guillotina.component import get_utility, query_adapter from guillotina.db.orm.interfaces import IBaseObject -from guillotina.interfaces import Allow -from guillotina.interfaces import AllowSingle -from guillotina.interfaces import Deny -from guillotina.interfaces import IGroups -from guillotina.interfaces import IInheritPermissionMap -from guillotina.interfaces import IPrincipal -from guillotina.interfaces import IPrincipalPermissionMap -from guillotina.interfaces import IPrincipalRoleMap -from guillotina.interfaces import IRolePermissionMap -from guillotina.interfaces import ISecurityPolicy -from guillotina.interfaces import IView -from guillotina.interfaces import Public -from guillotina.interfaces import Unset +from guillotina.interfaces import ( + Allow, + AllowSingle, + Deny, + IGroups, + IInheritPermissionMap, + IPrincipal, + IPrincipalPermissionMap, + IPrincipalRoleMap, + IRolePermissionMap, + ISecurityPolicy, + IView, + Public, + Unset, +) from guillotina.profile import profilable -from guillotina.security.security_code import principal_permission_manager -from guillotina.security.security_code import principal_role_manager -from guillotina.security.security_code import role_permission_manager -from lru import LRU -from typing import Dict -from typing import List -from typing import Optional -from typing import Union +from guillotina.security.security_code import ( + principal_permission_manager, + principal_role_manager, + role_permission_manager, +) + code_principal_permission_setting = principal_permission_manager.get_setting code_roles_for_permission = role_permission_manager.get_roles_for_permission diff --git a/guillotina/security/security_code.py b/guillotina/security/security_code.py index a571ce3b4..b410c39ae 100644 --- a/guillotina/security/security_code.py +++ b/guillotina/security/security_code.py @@ -1,15 +1,18 @@ +from zope.interface import implementer + from guillotina.auth.role import check_role -from guillotina.interfaces import Allow -from guillotina.interfaces import AllowSingle -from guillotina.interfaces import Deny -from guillotina.interfaces import IInheritPermissionManager -from guillotina.interfaces import IPrincipalPermissionManager -from guillotina.interfaces import IPrincipalRoleManager -from guillotina.interfaces import IRolePermissionManager -from guillotina.interfaces import Unset +from guillotina.interfaces import ( + Allow, + AllowSingle, + Deny, + IInheritPermissionManager, + IPrincipalPermissionManager, + IPrincipalRoleManager, + IRolePermissionManager, + Unset, +) from guillotina.security.permission import get_all_permissions from guillotina.security.securitymap import SecurityMap -from zope.interface import implementer @implementer(IPrincipalRoleManager) diff --git a/guillotina/security/security_local.py b/guillotina/security/security_local.py index 4201c49fe..720a85c80 100644 --- a/guillotina/security/security_local.py +++ b/guillotina/security/security_local.py @@ -1,14 +1,16 @@ from guillotina import configure -from guillotina.interfaces import Allow -from guillotina.interfaces import AllowSingle -from guillotina.interfaces import Deny -from guillotina.interfaces import IInheritPermissionManager -from guillotina.interfaces import INHERIT_KEY -from guillotina.interfaces import IPrincipalPermissionManager -from guillotina.interfaces import IPrincipalRoleManager -from guillotina.interfaces import IResource -from guillotina.interfaces import IRolePermissionManager -from guillotina.interfaces import Unset +from guillotina.interfaces import ( + INHERIT_KEY, + Allow, + AllowSingle, + Deny, + IInheritPermissionManager, + IPrincipalPermissionManager, + IPrincipalRoleManager, + IResource, + IRolePermissionManager, + Unset, +) from guillotina.security.securitymap import GuillotinaSecurityMap diff --git a/guillotina/security/utils.py b/guillotina/security/utils.py index 8585d1428..9bc5d3777 100644 --- a/guillotina/security/utils.py +++ b/guillotina/security/utils.py @@ -2,20 +2,23 @@ from guillotina.event import notify from guillotina.events import ObjectPermissionsModifiedEvent from guillotina.exceptions import PreconditionFailed -from guillotina.interfaces import Deny -from guillotina.interfaces import IInheritPermissionManager -from guillotina.interfaces import IInheritPermissionMap -from guillotina.interfaces import IPrincipalPermissionManager -from guillotina.interfaces import IPrincipalPermissionMap -from guillotina.interfaces import IPrincipalRoleManager -from guillotina.interfaces import IPrincipalRoleMap -from guillotina.interfaces import IRolePermissionManager -from guillotina.interfaces import IRolePermissionMap -from guillotina.security.policy import cached_principals -from guillotina.security.policy import cached_roles -from guillotina.security.security_code import principal_permission_manager -from guillotina.security.security_code import principal_role_manager -from guillotina.security.security_code import role_permission_manager +from guillotina.interfaces import ( + Deny, + IInheritPermissionManager, + IInheritPermissionMap, + IPrincipalPermissionManager, + IPrincipalPermissionMap, + IPrincipalRoleManager, + IPrincipalRoleMap, + IRolePermissionManager, + IRolePermissionMap, +) +from guillotina.security.policy import cached_principals, cached_roles +from guillotina.security.security_code import ( + principal_permission_manager, + principal_role_manager, + role_permission_manager, +) def protect_view(cls, permission): diff --git a/guillotina/subscribers.py b/guillotina/subscribers.py index dbbd59463..91bc99901 100644 --- a/guillotina/subscribers.py +++ b/guillotina/subscribers.py @@ -1,11 +1,12 @@ from datetime import datetime + from dateutil.tz import tzutc + from guillotina import configure from guillotina.component._api import get_component_registry -from guillotina.component.interfaces import ComponentLookupError -from guillotina.component.interfaces import IObjectEvent -from guillotina.interfaces import IObjectModifiedEvent -from guillotina.interfaces import IResource +from guillotina.component.interfaces import ComponentLookupError, IObjectEvent +from guillotina.interfaces import IObjectModifiedEvent, IResource + _zone = tzutc() diff --git a/guillotina/task_vars.py b/guillotina/task_vars.py index 70f4e2731..1dd5bcf31 100644 --- a/guillotina/task_vars.py +++ b/guillotina/task_vars.py @@ -1,14 +1,9 @@ from contextvars import ContextVar -from guillotina.db.interfaces import ITransaction -from guillotina.db.interfaces import ITransactionManager -from guillotina.interfaces import IContainer -from guillotina.interfaces import IDatabase -from guillotina.interfaces import IPrincipal -from guillotina.interfaces import IRegistry -from guillotina.interfaces import IRequest -from guillotina.interfaces import ISecurityPolicy -from typing import Dict -from typing import Optional +from typing import Dict, Optional + +from guillotina.db.interfaces import ITransaction, ITransactionManager +from guillotina.interfaces import IContainer, IDatabase, IPrincipal, IRegistry, IRequest, ISecurityPolicy + request: ContextVar[Optional[IRequest]] = ContextVar("g_request", default=None) txn: ContextVar[Optional[ITransaction]] = ContextVar("g_txn", default=None) diff --git a/guillotina/test_package.py b/guillotina/test_package.py index 8dbfe7409..4c08a2157 100644 --- a/guillotina/test_package.py +++ b/guillotina/test_package.py @@ -1,50 +1,44 @@ # this is for testing.py, do not import into other modules -from guillotina import configure -from guillotina import fields -from guillotina import schema +import asyncio +import json +import os +import tempfile +import typing +from shutil import copyfile +from typing import AsyncIterator + +from zope.interface import Interface, implementer + +from guillotina import configure, fields, schema from guillotina.async_util import IAsyncUtility -from guillotina.behaviors.instance import AnnotationBehavior -from guillotina.behaviors.instance import ContextBehavior +from guillotina.behaviors.instance import AnnotationBehavior, ContextBehavior from guillotina.behaviors.properties import ContextProperty from guillotina.component import get_multi_adapter -from guillotina.content import Item -from guillotina.content import Resource -from guillotina.directives import index_field -from guillotina.directives import metadata -from guillotina.directives import read_permission -from guillotina.directives import write_permission -from guillotina.exceptions import FileNotFoundException -from guillotina.exceptions import NoIndexField +from guillotina.content import Item, Resource +from guillotina.directives import index_field, metadata, read_permission, write_permission +from guillotina.exceptions import FileNotFoundException, NoIndexField from guillotina.fields import CloudFileField from guillotina.files import BaseCloudFile from guillotina.files.exceptions import RangeNotFound -from guillotina.interfaces import IApplication -from guillotina.interfaces import IContainer -from guillotina.interfaces import IExternalFileStorageManager -from guillotina.interfaces import IFile -from guillotina.interfaces import IFileField -from guillotina.interfaces import IFileNameGenerator -from guillotina.interfaces import IIDGenerator -from guillotina.interfaces import IItem -from guillotina.interfaces import IJSONToValue -from guillotina.interfaces import IObjectAddedEvent -from guillotina.interfaces import IRequest -from guillotina.interfaces import IResource +from guillotina.interfaces import ( + IApplication, + IContainer, + IExternalFileStorageManager, + IFile, + IFileField, + IFileNameGenerator, + IIDGenerator, + IItem, + IJSONToValue, + IObjectAddedEvent, + IRequest, + IResource, +) from guillotina.response import HTTPUnprocessableEntity from guillotina.schema import Object from guillotina.schema.interfaces import IContextAwareDefaultFactory -from guillotina.utils import apply_coroutine -from guillotina.utils import execute -from shutil import copyfile -from typing import AsyncIterator -from zope.interface import implementer -from zope.interface import Interface +from guillotina.utils import apply_coroutine, execute -import asyncio -import json -import os -import tempfile -import typing app_settings = {"applications": ["guillotina"]} diff --git a/guillotina/testing.py b/guillotina/testing.py index ce59d4426..303073bd2 100644 --- a/guillotina/testing.py +++ b/guillotina/testing.py @@ -1,11 +1,11 @@ +import base64 +import os from copy import deepcopy +from typing import Any, Dict + from guillotina.auth.users import ROOT_USER_ID from guillotina.utils import lazy_apply -from typing import Any -from typing import Dict -import base64 -import os TESTING_PORT = 55001 diff --git a/guillotina/tests/__init__.py b/guillotina/tests/__init__.py index 796b58a07..3bdbda876 100644 --- a/guillotina/tests/__init__.py +++ b/guillotina/tests/__init__.py @@ -1,4 +1,5 @@ import os + TEST_DIR = os.path.dirname(os.path.realpath(__file__)) TEST_RESOURCES_DIR = os.path.join(TEST_DIR, "resources") diff --git a/guillotina/tests/cache/test_cache_memory.py b/guillotina/tests/cache/test_cache_memory.py index eeae81ff3..0181d2a1a 100644 --- a/guillotina/tests/cache/test_cache_memory.py +++ b/guillotina/tests/cache/test_cache_memory.py @@ -1,15 +1,16 @@ +import asyncio +from unittest import mock +from unittest.mock import MagicMock, Mock + +import pytest + from guillotina.component import get_utility from guillotina.contrib.cache.strategy import BasicCache from guillotina.db.transaction import Transaction from guillotina.interfaces import ICacheUtility from guillotina.tests import mocks from guillotina.tests.utils import create_content -from unittest import mock -from unittest.mock import MagicMock -from unittest.mock import Mock -import asyncio -import pytest pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/cache/test_cache_pubsub.py b/guillotina/tests/cache/test_cache_pubsub.py index 87c49afea..94c884421 100644 --- a/guillotina/tests/cache/test_cache_pubsub.py +++ b/guillotina/tests/cache/test_cache_pubsub.py @@ -1,16 +1,17 @@ +import asyncio +import pickle + +import pytest + from guillotina import app_settings from guillotina.component import get_utility -from guillotina.contrib.cache import CACHE_PREFIX -from guillotina.contrib.cache import serialize +from guillotina.contrib.cache import CACHE_PREFIX, serialize from guillotina.contrib.cache.strategy import BasicCache from guillotina.interfaces import ICacheUtility from guillotina.tests import mocks from guillotina.tests.utils import create_content from guillotina.utils import resolve_dotted_name -import asyncio -import pickle -import pytest pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/cache/test_cache_store.py b/guillotina/tests/cache/test_cache_store.py index 9aa966f62..b5c15a02e 100644 --- a/guillotina/tests/cache/test_cache_store.py +++ b/guillotina/tests/cache/test_cache_store.py @@ -1,12 +1,12 @@ +import pytest + from guillotina.component import get_utility -from guillotina.contrib.cache import CACHE_PREFIX -from guillotina.contrib.cache import serialize +from guillotina.contrib.cache import CACHE_PREFIX, serialize from guillotina.contrib.cache.strategy import BasicCache from guillotina.interfaces import ICacheUtility from guillotina.tests import mocks from guillotina.utils import resolve_dotted_name -import pytest pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/cache/test_cache_txn.py b/guillotina/tests/cache/test_cache_txn.py index e58337bee..577d28848 100644 --- a/guillotina/tests/cache/test_cache_txn.py +++ b/guillotina/tests/cache/test_cache_txn.py @@ -1,13 +1,13 @@ +import pytest + from guillotina import app_settings from guillotina.annotations import AnnotationData from guillotina.api.container import create_container from guillotina.component import get_utility -from guillotina.interfaces import IAnnotations -from guillotina.interfaces import ICacheUtility +from guillotina.interfaces import IAnnotations, ICacheUtility from guillotina.transactions import transaction from guillotina.utils import get_database -import pytest pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/cache/test_utils.py b/guillotina/tests/cache/test_utils.py index a1292e1e8..ab01106ce 100644 --- a/guillotina/tests/cache/test_utils.py +++ b/guillotina/tests/cache/test_utils.py @@ -1,15 +1,15 @@ -from guillotina.contrib.cache.utility import CacheUtility - import pytest +from guillotina.contrib.cache.utility import CacheUtility + @pytest.mark.asyncio async def test_get_size_of_item(): rcache = CacheUtility() - from guillotina.contrib.cache.utility import _default_size - import sys + from guillotina.contrib.cache.utility import _default_size + assert rcache.get_size(dict(a=1)) == _default_size assert rcache.get_size(1) == sys.getsizeof(1) assert rcache.get_size(dict(state=b"x" * 10)) == 10 diff --git a/guillotina/tests/conftest.py b/guillotina/tests/conftest.py index 2719bba22..1166a425f 100644 --- a/guillotina/tests/conftest.py +++ b/guillotina/tests/conftest.py @@ -1,5 +1,6 @@ from pytest_docker_fixtures import images + images.configure( name="cockroach", image="cockroachdb/cockroach", diff --git a/guillotina/tests/dbusers/test_api.py b/guillotina/tests/dbusers/test_api.py index 4d504894a..3f7d8d07a 100644 --- a/guillotina/tests/dbusers/test_api.py +++ b/guillotina/tests/dbusers/test_api.py @@ -1,10 +1,13 @@ -from . import settings -from guillotina.tests.utils import get_container - import base64 import json + import pytest +from guillotina.tests.utils import get_container + +from . import settings + + pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/dbusers/test_manage_groups.py b/guillotina/tests/dbusers/test_manage_groups.py index 21843bb5c..afc38201d 100644 --- a/guillotina/tests/dbusers/test_manage_groups.py +++ b/guillotina/tests/dbusers/test_manage_groups.py @@ -1,11 +1,14 @@ -from . import settings -from guillotina.tests.test_catalog import NOT_POSTGRES - import copy import json + import pytest import pytest_asyncio +from guillotina.tests.test_catalog import NOT_POSTGRES + +from . import settings + + pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/dbusers/test_manage_users.py b/guillotina/tests/dbusers/test_manage_users.py index 0c6a97c2e..92a91adf2 100644 --- a/guillotina/tests/dbusers/test_manage_users.py +++ b/guillotina/tests/dbusers/test_manage_users.py @@ -1,11 +1,14 @@ -from . import settings -from guillotina.tests.test_catalog import NOT_POSTGRES - import copy import json + import pytest import pytest_asyncio +from guillotina.tests.test_catalog import NOT_POSTGRES + +from . import settings + + pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/dbusers/test_registration.py b/guillotina/tests/dbusers/test_registration.py index e304a5a49..d19923c01 100644 --- a/guillotina/tests/dbusers/test_registration.py +++ b/guillotina/tests/dbusers/test_registration.py @@ -1,9 +1,12 @@ -from . import settings +import json + +import pytest + from guillotina.component import get_utility from guillotina.interfaces import IMailer -import json -import pytest +from . import settings + pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/dbusers/test_reset_password.py b/guillotina/tests/dbusers/test_reset_password.py index b7f509aa3..b8368630a 100644 --- a/guillotina/tests/dbusers/test_reset_password.py +++ b/guillotina/tests/dbusers/test_reset_password.py @@ -1,11 +1,14 @@ -from . import settings -from guillotina.component import get_utility -from guillotina.interfaces import IMailer - import base64 import json + import pytest +from guillotina.component import get_utility +from guillotina.interfaces import IMailer + +from . import settings + + pytestmark = pytest.mark.asyncio NEW_PASSWORD = "password2" diff --git a/guillotina/tests/dbusers/test_security.py b/guillotina/tests/dbusers/test_security.py index c2f96b678..3eb9184fd 100644 --- a/guillotina/tests/dbusers/test_security.py +++ b/guillotina/tests/dbusers/test_security.py @@ -1,9 +1,12 @@ -from . import settings +import json + +import pytest + from guillotina import configure from guillotina.interfaces import IFolder -import json -import pytest +from . import settings + pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/dbusers/test_setup.py b/guillotina/tests/dbusers/test_setup.py index e5cddc9a6..79f4e8380 100644 --- a/guillotina/tests/dbusers/test_setup.py +++ b/guillotina/tests/dbusers/test_setup.py @@ -1,7 +1,9 @@ -from . import settings +import pytest + from guillotina.tests.utils import get_container -import pytest +from . import settings + pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/dyncontent/test_dynapi.py b/guillotina/tests/dyncontent/test_dynapi.py index 3dc380a9c..07ec80344 100644 --- a/guillotina/tests/dyncontent/test_dynapi.py +++ b/guillotina/tests/dyncontent/test_dynapi.py @@ -1,8 +1,10 @@ -from . import settings - import json + import pytest +from . import settings + + pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/fixtures.py b/guillotina/tests/fixtures.py index 8b63b3aaa..014afcf1f 100644 --- a/guillotina/tests/fixtures.py +++ b/guillotina/tests/fixtures.py @@ -1,33 +1,31 @@ +import asyncio +import json +import os +from unittest import mock + +import aiohttp +import pytest +import pytest_asyncio from async_asgi_testclient import TestClient -from guillotina import task_vars -from guillotina import testing -from guillotina.component import get_utility -from guillotina.component import globalregistry -from guillotina.const import ROOT_ID -from guillotina.const import TRASHED_ID -from guillotina.db.interfaces import ICockroachStorage -from guillotina.db.interfaces import IPostgresStorage + +from guillotina import task_vars, testing +from guillotina.component import get_utility, globalregistry +from guillotina.const import ROOT_ID, TRASHED_ID +from guillotina.db.interfaces import ICockroachStorage, IPostgresStorage from guillotina.db.storages.cockroach import CockroachStorage from guillotina.factory import make_app -from guillotina.interfaces import IApplication -from guillotina.interfaces import IDatabase +from guillotina.interfaces import IApplication, IDatabase from guillotina.tests import mocks -from guillotina.tests.utils import ContainerRequesterAsyncContextManager -from guillotina.tests.utils import get_mocked_request -from guillotina.tests.utils import login -from guillotina.tests.utils import logout -from guillotina.tests.utils import wrap_request -from guillotina.transactions import get_tm -from guillotina.transactions import transaction +from guillotina.tests.utils import ( + ContainerRequesterAsyncContextManager, + get_mocked_request, + login, + logout, + wrap_request, +) +from guillotina.transactions import get_tm, transaction from guillotina.utils import merge_dicts -from unittest import mock -import aiohttp -import asyncio -import json -import os -import pytest -import pytest_asyncio _dir = os.path.dirname(os.path.realpath(__file__)) @@ -497,8 +495,7 @@ async def app(event_loop, db, request): host = server_settings.get("host", "127.0.0.1") port = int(server_settings.get("port", 8000)) - from uvicorn import Config - from uvicorn import Server + from uvicorn import Config, Server config = Config(app, host=host, port=port, lifespan="on", server_header=False) server = Server(config=config) diff --git a/guillotina/tests/image/__init__.py b/guillotina/tests/image/__init__.py index d2fbd0e60..7d132ed83 100644 --- a/guillotina/tests/image/__init__.py +++ b/guillotina/tests/image/__init__.py @@ -1,3 +1,4 @@ import os.path + TEST_DATA_LOCATION = os.path.join(os.path.dirname(__file__), "data") diff --git a/guillotina/tests/image/test_field.py b/guillotina/tests/image/test_field.py index 2c0387328..653400bec 100644 --- a/guillotina/tests/image/test_field.py +++ b/guillotina/tests/image/test_field.py @@ -1,14 +1,18 @@ -from guillotina.contrib.image.behaviors import IImageAttachment -from guillotina.contrib.image.behaviors import IMultiImageAttachment -from guillotina.contrib.image.behaviors import IMultiImageOrderedAttachment +import json +import os + +import pytest + +from guillotina.contrib.image.behaviors import ( + IImageAttachment, + IMultiImageAttachment, + IMultiImageOrderedAttachment, +) from guillotina.directives import index_field from guillotina.test_package import IExample from guillotina.tests.image import TEST_DATA_LOCATION from guillotina.utils import get_behavior -import json -import os -import pytest pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/image/test_scale.py b/guillotina/tests/image/test_scale.py index ce2a88730..10c16d09e 100644 --- a/guillotina/tests/image/test_scale.py +++ b/guillotina/tests/image/test_scale.py @@ -1,14 +1,15 @@ # -*- coding: utf-8 -*- -from guillotina.contrib.image.scale import scaleImage -from guillotina.contrib.image.scale import scalePILImage -from guillotina.tests.image import TEST_DATA_LOCATION +import os.path +import warnings from io import BytesIO from unittest import TestCase -import os.path import PIL.Image import PIL.ImageDraw -import warnings + +from guillotina.contrib.image.scale import scaleImage, scalePILImage +from guillotina.tests.image import TEST_DATA_LOCATION + with open(os.path.join(TEST_DATA_LOCATION, "logo.png"), "rb") as fio: PNG = fio.read() diff --git a/guillotina/tests/mailer/test_mailer.py b/guillotina/tests/mailer/test_mailer.py index 090372e51..ffa5d2029 100644 --- a/guillotina/tests/mailer/test_mailer.py +++ b/guillotina/tests/mailer/test_mailer.py @@ -1,7 +1,8 @@ +import pytest + from guillotina.component import get_utility from guillotina.interfaces import IMailer -import pytest pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/mcp/test_mcp.py b/guillotina/tests/mcp/test_mcp.py index 0d6e8addb..71ee54020 100644 --- a/guillotina/tests/mcp/test_mcp.py +++ b/guillotina/tests/mcp/test_mcp.py @@ -1,10 +1,12 @@ -from guillotina.contrib.mcp import resources as mcp_resources -from guillotina.utils import resolve_dotted_name - import asyncio import json + import pytest +from guillotina.contrib.mcp import resources as mcp_resources +from guillotina.utils import resolve_dotted_name + + pytestmark = pytest.mark.asyncio MCP_SETTINGS = { diff --git a/guillotina/tests/memcached/test_cache.py b/guillotina/tests/memcached/test_cache.py index 4ff3ff8cc..12b08e6a1 100644 --- a/guillotina/tests/memcached/test_cache.py +++ b/guillotina/tests/memcached/test_cache.py @@ -8,10 +8,11 @@ except ModuleNotFoundError: pymemcache = None +import pytest + from guillotina.component import get_utility from guillotina.interfaces import ICacheUtility -import pytest pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/memcached/test_memcached_driver.py b/guillotina/tests/memcached/test_memcached_driver.py index d5ef3649f..6057f6b2b 100644 --- a/guillotina/tests/memcached/test_memcached_driver.py +++ b/guillotina/tests/memcached/test_memcached_driver.py @@ -1,9 +1,7 @@ try: - from guillotina.contrib.memcached.driver import MemcachedDriver - from guillotina.contrib.memcached.driver import safe_key - from guillotina.contrib.memcached.driver import update_connection_pool_metrics - import emcache + + from guillotina.contrib.memcached.driver import MemcachedDriver, safe_key, update_connection_pool_metrics except ModuleNotFoundError: emcache = None @@ -12,13 +10,15 @@ except ModuleNotFoundError: pymemcache = None -from guillotina.utils import resolve_dotted_name +import asyncio from unittest import mock -import asyncio import pytest import pytest_asyncio +from guillotina.utils import resolve_dotted_name + + pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/memcached/test_metrics.py b/guillotina/tests/memcached/test_metrics.py index 1140da22e..abf0dbb4d 100644 --- a/guillotina/tests/memcached/test_metrics.py +++ b/guillotina/tests/memcached/test_metrics.py @@ -3,10 +3,11 @@ except ModuleNotFoundError: MemcachedDriver = None # type: ignore -from asyncmock import AsyncMock from unittest import mock import pytest +from asyncmock import AsyncMock + pytestmark = pytest.mark.asyncio MEMCACHED_SETTINGS = {"applications": ["guillotina", "memcached", "guillotina.contrib.memcached"]} diff --git a/guillotina/tests/mocks.py b/guillotina/tests/mocks.py index 3232ccdf7..e5dac1225 100644 --- a/guillotina/tests/mocks.py +++ b/guillotina/tests/mocks.py @@ -1,14 +1,12 @@ +import asyncio +import uuid from collections import OrderedDict -from guillotina import app_settings -from guillotina import task_vars -from guillotina.db.cache.dummy import DummyCache -from guillotina.db.interfaces import IStorage -from guillotina.db.interfaces import ITransaction -from guillotina.db.interfaces import IWriter + from zope.interface import implementer -import asyncio -import uuid +from guillotina import app_settings, task_vars +from guillotina.db.cache.dummy import DummyCache +from guillotina.db.interfaces import IStorage, ITransaction, IWriter class MockDBTransaction: diff --git a/guillotina/tests/pubsub/test_pubsub.py b/guillotina/tests/pubsub/test_pubsub.py index 3a47358a1..c4b0006ad 100644 --- a/guillotina/tests/pubsub/test_pubsub.py +++ b/guillotina/tests/pubsub/test_pubsub.py @@ -1,10 +1,11 @@ -from guillotina.component import get_utility -from guillotina.interfaces import IPubSubUtility - import asyncio + import pytest import pytest_docker_fixtures +from guillotina.component import get_utility +from guillotina.interfaces import IPubSubUtility + @pytest.mark.app_settings( {"applications": ["guillotina", "guillotina.contrib.redis", "guillotina.contrib.pubsub"]} diff --git a/guillotina/tests/redis/test_driver.py b/guillotina/tests/redis/test_driver.py index 16ec3155d..d40e7ff48 100644 --- a/guillotina/tests/redis/test_driver.py +++ b/guillotina/tests/redis/test_driver.py @@ -1,8 +1,10 @@ -from guillotina.utils import resolve_dotted_name - import asyncio + import pytest +from guillotina.utils import resolve_dotted_name + + pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/redis_session/test_session.py b/guillotina/tests/redis_session/test_session.py index ef594ecfb..1616be0ec 100644 --- a/guillotina/tests/redis_session/test_session.py +++ b/guillotina/tests/redis_session/test_session.py @@ -1,11 +1,14 @@ -from . import settings -from guillotina.auth.users import ROOT_USER_ID -from guillotina.testing import TESTING_SETTINGS - import json + import jwt import pytest +from guillotina.auth.users import ROOT_USER_ID +from guillotina.testing import TESTING_SETTINGS + +from . import settings + + pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_adapters.py b/guillotina/tests/test_adapters.py index dcc70e3cd..f5716a0c6 100644 --- a/guillotina/tests/test_adapters.py +++ b/guillotina/tests/test_adapters.py @@ -1,32 +1,30 @@ +import pytest + from guillotina import schema -from guillotina.component import get_adapter -from guillotina.component import get_multi_adapter -from guillotina.component import get_utility -from guillotina.content import Container -from guillotina.content import Item -from guillotina.factory.security import ApplicationSpecialPermissions -from guillotina.factory.security import DatabaseSpecialPermissions -from guillotina.interfaces import IApplication -from guillotina.interfaces import IFactorySerializeToJson -from guillotina.interfaces import IItem -from guillotina.interfaces import IPrincipalPermissionManager -from guillotina.interfaces import IResource -from guillotina.interfaces import IResourceDeserializeFromJson -from guillotina.interfaces import IResourceFactory -from guillotina.interfaces import IResourceSerializeToJson -from guillotina.interfaces import IResourceSerializeToJsonSummary -from guillotina.interfaces import ISchemaFieldSerializeToJson -from guillotina.interfaces import ISchemaSerializeToJson -from guillotina.json import deserialize_content -from guillotina.json import serialize_schema -from guillotina.json import serialize_schema_field -from guillotina.json.serialize_content import DefaultJSONSummarySerializer -from guillotina.json.serialize_content import SerializeFolderToJson -from guillotina.json.serialize_content import SerializeToJson +from guillotina.component import get_adapter, get_multi_adapter, get_utility +from guillotina.content import Container, Item +from guillotina.factory.security import ApplicationSpecialPermissions, DatabaseSpecialPermissions +from guillotina.interfaces import ( + IApplication, + IFactorySerializeToJson, + IItem, + IPrincipalPermissionManager, + IResource, + IResourceDeserializeFromJson, + IResourceFactory, + IResourceSerializeToJson, + IResourceSerializeToJsonSummary, + ISchemaFieldSerializeToJson, + ISchemaSerializeToJson, +) +from guillotina.json import deserialize_content, serialize_schema, serialize_schema_field +from guillotina.json.serialize_content import ( + DefaultJSONSummarySerializer, + SerializeFolderToJson, + SerializeToJson, +) from guillotina.json.serialize_value import json_compatible -import pytest - @pytest.mark.asyncio async def test_DatabaseSpecialPermissions_IDatabase(dummy_txn_root): # noqa: N802 diff --git a/guillotina/tests/test_annotations.py b/guillotina/tests/test_annotations.py index 05e00ec48..316ab7cb6 100644 --- a/guillotina/tests/test_annotations.py +++ b/guillotina/tests/test_annotations.py @@ -1,3 +1,9 @@ +import os +import time +from uuid import uuid4 + +import pytest + from guillotina.annotations import AnnotationData from guillotina.content import create_content_in_container from guillotina.fields.annotation import BucketDictValue @@ -5,11 +11,7 @@ from guillotina.tests.utils import login from guillotina.transactions import transaction from guillotina.utils import get_database -from uuid import uuid4 -import os -import pytest -import time pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_api.py b/guillotina/tests/test_api.py index 9c5936253..bb126f942 100644 --- a/guillotina/tests/test_api.py +++ b/guillotina/tests/test_api.py @@ -1,7 +1,11 @@ -from datetime import datetime -from datetime import time -from guillotina import configure -from guillotina import schema +import base64 +import json +from datetime import datetime, time + +import pytest +from zope.interface import Interface + +from guillotina import configure, schema from guillotina.addons import Addon from guillotina.api.service import _safe_int_or_float_cast from guillotina.behaviors.attachment import IAttachment @@ -9,19 +13,13 @@ from guillotina.configure import contenttype from guillotina.content import Item from guillotina.fields.patch import PatchField -from guillotina.interfaces import IAnnotations -from guillotina.interfaces import IFile -from guillotina.interfaces import IResource +from guillotina.interfaces import IAnnotations, IFile, IResource from guillotina.test_package import ITestBehavior from guillotina.tests import utils from guillotina.tests.dbusers.settings import DEFAULT_SETTINGS as DBUSERS_DEFAULT_SETTINGS from guillotina.transactions import transaction from guillotina.utils import get_behavior -from zope.interface import Interface -import base64 -import json -import pytest pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_attachment.py b/guillotina/tests/test_attachment.py index da51721b9..d708d229f 100644 --- a/guillotina/tests/test_attachment.py +++ b/guillotina/tests/test_attachment.py @@ -1,17 +1,18 @@ +import asyncio +import base64 +import hashlib +import json +import random + +import pytest + from guillotina import task_vars -from guillotina.behaviors.attachment import IAttachment -from guillotina.behaviors.attachment import IMultiAttachment +from guillotina.behaviors.attachment import IAttachment, IMultiAttachment from guillotina.component import get_multi_adapter from guillotina.interfaces import IFileManager from guillotina.tests import utils from guillotina.transactions import transaction -import asyncio -import base64 -import hashlib -import json -import pytest -import random pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_auth.py b/guillotina/tests/test_auth.py index f95043d85..5d1f51bda 100644 --- a/guillotina/tests/test_auth.py +++ b/guillotina/tests/test_auth.py @@ -1,11 +1,12 @@ -from datetime import datetime -from datetime import timedelta -from guillotina._settings import app_settings -from guillotina.auth import validators +from datetime import datetime, timedelta import jwt import pytest +from guillotina._settings import app_settings +from guillotina.auth import validators + + pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_blob.py b/guillotina/tests/test_blob.py index 1df273598..b81294226 100644 --- a/guillotina/tests/test_blob.py +++ b/guillotina/tests/test_blob.py @@ -1,17 +1,16 @@ +import pytest + from guillotina.behaviors.attachment import IAttachment from guillotina.blob import Blob from guillotina.component import get_multi_adapter from guillotina.content import create_content_in_container from guillotina.exceptions import BlobChunkNotFound -from guillotina.files.exceptions import RangeNotFound -from guillotina.files.exceptions import RangeNotSupported +from guillotina.files.exceptions import RangeNotFound, RangeNotSupported from guillotina.interfaces import IFileManager from guillotina.tests.utils import login from guillotina.transactions import transaction -from guillotina.utils import get_behavior -from guillotina.utils import get_database +from guillotina.utils import get_behavior, get_database -import pytest pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_catalog.py b/guillotina/tests/test_catalog.py index 6fbb3636e..3cc144b5c 100644 --- a/guillotina/tests/test_catalog.py +++ b/guillotina/tests/test_catalog.py @@ -1,28 +1,21 @@ +import json +import os from datetime import datetime -from guillotina import configure -from guillotina import task_vars + +import pytest + +from guillotina import configure, task_vars from guillotina.catalog import index -from guillotina.catalog.utils import get_index_fields -from guillotina.catalog.utils import get_metadata_fields -from guillotina.catalog.utils import parse_query -from guillotina.component import get_adapter -from guillotina.component import query_utility -from guillotina.content import Container -from guillotina.content import create_content -from guillotina.content import Resource +from guillotina.catalog.utils import get_index_fields, get_metadata_fields, parse_query +from guillotina.component import get_adapter, query_utility +from guillotina.content import Container, Resource, create_content from guillotina.directives import index_field from guillotina.event import notify from guillotina.events import ObjectModifiedEvent -from guillotina.interfaces import ICatalogDataAdapter -from guillotina.interfaces import ICatalogUtility -from guillotina.interfaces import IResource -from guillotina.interfaces import ISecurityInfo +from guillotina.interfaces import ICatalogDataAdapter, ICatalogUtility, IResource, ISecurityInfo from guillotina.tests import mocks from guillotina.tests import utils as test_utils -import json -import os -import pytest pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_cockroach.py b/guillotina/tests/test_cockroach.py index 8ba54fb03..5fa362dc3 100644 --- a/guillotina/tests/test_cockroach.py +++ b/guillotina/tests/test_cockroach.py @@ -1,12 +1,14 @@ +import asyncio +import os + +import pytest + from guillotina.component import get_adapter from guillotina.content import Folder from guillotina.db.interfaces import IVacuumProvider from guillotina.db.transaction_manager import TransactionManager from guillotina.tests.utils import create_content -import asyncio -import os -import pytest pytestmark = [ pytest.mark.asyncio, diff --git a/guillotina/tests/test_commands.py b/guillotina/tests/test_commands.py index a661762b7..f0ac01e98 100644 --- a/guillotina/tests/test_commands.py +++ b/guillotina/tests/test_commands.py @@ -1,19 +1,20 @@ +import io +import json +import os from contextlib import redirect_stdout +from tempfile import mkstemp + +import pytest + from guillotina import testing -from guillotina.commands import Command -from guillotina.commands import get_settings +from guillotina.commands import Command, get_settings from guillotina.commands.crypto import CryptoCommand from guillotina.commands.migrate import MigrateCommand from guillotina.commands.run import RunCommand from guillotina.commands.vacuum import VacuumCommand from guillotina.exceptions import TransactionNotFound from guillotina.utils import get_current_transaction -from tempfile import mkstemp -import io -import json -import os -import pytest DATABASE = os.environ.get("DATABASE", "DUMMY") DB_SCHEMA = os.environ.get("DB_SCHEMA", "public") diff --git a/guillotina/tests/test_configure.py b/guillotina/tests/test_configure.py index 5ca819288..ccf77c18f 100644 --- a/guillotina/tests/test_configure.py +++ b/guillotina/tests/test_configure.py @@ -1,22 +1,19 @@ +import pytest +from zope.interface import Interface + from guillotina import configure from guillotina.addons import Addon from guillotina.api.service import Service -from guillotina.component import get_utility -from guillotina.component import query_multi_adapter +from guillotina.component import get_utility, query_multi_adapter from guillotina.configure.config import ConfigurationMachine -from guillotina.content import get_all_possible_schemas_for_type -from guillotina.content import Item +from guillotina.content import Item, get_all_possible_schemas_for_type from guillotina.event import notify from guillotina.events import ObjectAddedEvent -from guillotina.factory.app import ApplicationConfigurator -from guillotina.factory.app import configure_application +from guillotina.factory.app import ApplicationConfigurator, configure_application from guillotina.factory.content import ApplicationRoot -from guillotina.interfaces import IApplication -from guillotina.interfaces import IContainer +from guillotina.interfaces import IApplication, IContainer from guillotina.tests.utils import create_content -from zope.interface import Interface -import pytest pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_configure_component.py b/guillotina/tests/test_configure_component.py index a4c4040e6..0235ef7ee 100644 --- a/guillotina/tests/test_configure_component.py +++ b/guillotina/tests/test_configure_component.py @@ -23,11 +23,11 @@ def _callFUT(self, *args, **kw): # noqa: N802 return handler(*args, **kw) def test_uses_configured_site_manager(self): + from zope.interface.registry import Components + from guillotina.component import get_component_registry from guillotina.component._compat import _BLANK - from guillotina.component.testfiles.components import comp - from guillotina.component.testfiles.components import IApp - from zope.interface.registry import Components + from guillotina.component.testfiles.components import IApp, comp registry = Components() @@ -87,9 +87,10 @@ def _callFUT(self, *args, **kw): # noqa: N802 return adapter(*args, **kw) def test_empty_factory(self): - from guillotina.configure.component import ComponentConfigurationError from zope.interface import Interface + from guillotina.configure.component import ComponentConfigurationError + class IFoo(Interface): pass @@ -97,9 +98,10 @@ class IFoo(Interface): self.assertRaises(ComponentConfigurationError, self._callFUT, _cfg_ctx, [], [Interface], IFoo) def test_multiple_factory_multiple_for_(self): - from guillotina.configure.component import ComponentConfigurationError from zope.interface import Interface + from guillotina.configure.component import ComponentConfigurationError + class IFoo(Interface): pass @@ -135,9 +137,9 @@ class IFoo(Interface): class IBar(Interface): pass + from zope.interface import implementer, named + from guillotina.component import adapter - from zope.interface import implementer - from zope.interface import named @adapter(IFoo) @implementer(IBar) @@ -153,9 +155,10 @@ def __init__(self, context): self.assertEqual(action["args"][4], "bar") def test_no_for__factory_adapts_no_provides_factory_not_implement(self): - from guillotina.component._declaration import adapter from zope.interface import Interface + from guillotina.component._declaration import adapter + @adapter(Interface) class _Factory(object): def __init__(self, context): @@ -165,9 +168,10 @@ def __init__(self, context): self.assertRaises(TypeError, self._callFUT, _cfg_ctx, [_Factory]) def test_multiple_factory_single_for__w_name(self): + from zope.interface import Interface + from guillotina.component.interface import provide_interface from guillotina.configure.component import handler - from zope.interface import Interface class IFoo(Interface): pass @@ -205,10 +209,10 @@ class Bar(object): self.assertEqual(action["args"], ("", Interface)) def test_no_for__no_provides_factory_adapts_factory_implement(self): + from zope.interface import Interface, implementer + from guillotina.component._declaration import adapter from guillotina.configure.component import handler - from zope.interface import implementer - from zope.interface import Interface class IFoo(Interface): pass @@ -288,9 +292,10 @@ class Foo(object): self.assertRaises(TypeError, self._callFUT, _cfg_ctx, factory=Foo, provides=IFoo) def test_no_factory_w_handler_no_provides(self): + from zope.interface import Interface + from guillotina.component.interface import provide_interface from guillotina.configure.component import handler - from zope.interface import Interface def _handler(*args): pass @@ -315,9 +320,10 @@ def _handler(*args): self.assertEqual(action["args"], ("", Interface)) def test_w_factory_w_provides(self): + from zope.interface import Interface + from guillotina.component.interface import provide_interface from guillotina.configure.component import handler - from zope.interface import Interface class IFoo(Interface): pass @@ -382,9 +388,10 @@ def test_w_component_wo_provides_component_no_provides(self): self.assertRaises(TypeError, self._callFUT, _cfg_ctx, component=_COMPONENT) def test_w_factory_w_provides(self): + from zope.interface import Interface + from guillotina.component.interface import provide_interface from guillotina.configure.component import handler - from zope.interface import Interface class IFoo(Interface): pass @@ -413,10 +420,10 @@ class Foo(object): self.assertEqual(action["args"], ("", IFoo)) def test_w_factory_wo_provides_factory_implement(self): + from zope.interface import Interface, implementer + from guillotina.component.interface import provide_interface from guillotina.configure.component import handler - from zope.interface import implementer - from zope.interface import Interface class IFoo(Interface): pass @@ -446,9 +453,10 @@ class Foo(object): self.assertEqual(action["args"], ("", IFoo)) def test_w_component_w_provides_w_name(self): + from zope.interface import Interface + from guillotina.component.interface import provide_interface from guillotina.configure.component import handler - from zope.interface import Interface class IFoo(Interface): pass @@ -474,9 +482,7 @@ class IFoo(Interface): self.assertEqual(action["args"], ("", IFoo)) def test_w_component_wo_provides_wo_name(self): - from zope.interface import implementer - from zope.interface import Interface - from zope.interface import named + from zope.interface import Interface, implementer, named class IFoo(Interface): pass @@ -495,10 +501,10 @@ class Foo(object): self.assertEqual(action["args"][3], "foo") def test_w_component_wo_provides_component_provides(self): + from zope.interface import Interface, directlyProvides + from guillotina.component.interface import provide_interface from guillotina.configure.component import handler - from zope.interface import directlyProvides - from zope.interface import Interface class IFoo(Interface): pass @@ -535,9 +541,10 @@ def _callFUT(self, *args, **kw): # noqa: N802 return interface(*args, **kw) def test_wo_name_wo_type(self): - from guillotina.component.interface import provide_interface from zope.interface import Interface + from guillotina.component.interface import provide_interface + class IFoo(Interface): pass @@ -551,9 +558,10 @@ class IFoo(Interface): self.assertEqual(action["args"], ("", IFoo, None)) def test_w_name_w_type(self): - from guillotina.component.interface import provide_interface from zope.interface import Interface + from guillotina.component.interface import provide_interface + class IFoo(Interface): pass @@ -577,9 +585,10 @@ def _callFUT(self, *args, **kw): # noqa: N802 return view(*args, **kw) def test_w_factory_as_empty(self): - from guillotina.configure.component import ComponentConfigurationError from zope.interface import Interface + from guillotina.configure.component import ComponentConfigurationError + class IViewType(Interface): pass @@ -595,9 +604,10 @@ class IViewType(Interface): ) def test_w_multiple_factory_multiple_for_(self): - from guillotina.configure.component import ComponentConfigurationError from zope.interface import Interface + from guillotina.configure.component import ComponentConfigurationError + class IViewType(Interface): pass @@ -619,9 +629,10 @@ class Bar(object): ) def test_w_for__as_empty(self): - from guillotina.configure.component import ComponentConfigurationError from zope.interface import Interface + from guillotina.configure.component import ComponentConfigurationError + class IViewType(Interface): pass @@ -635,9 +646,10 @@ def __init__(self, context): ) def test_w_single_factory_single_for__wo_permission_w_name(self): + from zope.interface import Interface + from guillotina.component.interface import provide_interface from guillotina.configure.component import handler - from zope.interface import Interface class IViewType(Interface): pass @@ -678,9 +690,10 @@ def __init__(self, context): self.assertEqual(action["args"], ("", IViewType)) def test_w_multiple_factory_single_for__wo_permission(self): - from guillotina.configure.component import handler from zope.interface import Interface + from guillotina.configure.component import handler + class IViewType(Interface): pass @@ -741,13 +754,11 @@ def test_suite(): def test_configuration_machine_allows_overriding(): - from guillotina.component import adapter - from guillotina.component import get_adapter + from zope.interface import Interface, implementer, named + + from guillotina.component import adapter, get_adapter from guillotina.configure import component from guillotina.configure.config import ConfigurationMachine - from zope.interface import implementer - from zope.interface import Interface - from zope.interface import named class IFoo(Interface): pass diff --git a/guillotina/tests/test_content.py b/guillotina/tests/test_content.py index ba9b5ec1e..52fdb6386 100644 --- a/guillotina/tests/test_content.py +++ b/guillotina/tests/test_content.py @@ -1,31 +1,30 @@ +import json +import pickle + +import pytest + from guillotina import configure from guillotina.behaviors.attachment import IAttachment from guillotina.behaviors.dublincore import IDublinCore from guillotina.component import get_utility from guillotina.component.interfaces import ComponentLookupError -from guillotina.content import create_content -from guillotina.content import create_content_in_container -from guillotina.content import Folder -from guillotina.content import get_all_behaviors -from guillotina.content import Item -from guillotina.content import load_cached_schema -from guillotina.exceptions import NoPermissionToAdd -from guillotina.exceptions import NotAllowedContentType -from guillotina.interfaces import IApplication -from guillotina.interfaces import IItem +from guillotina.content import ( + Folder, + Item, + create_content, + create_content_in_container, + get_all_behaviors, + load_cached_schema, +) +from guillotina.exceptions import NoPermissionToAdd, NotAllowedContentType +from guillotina.interfaces import IApplication, IItem from guillotina.interfaces.types import IConstrainTypes -from guillotina.schema import Dict -from guillotina.schema import TextLine +from guillotina.schema import Dict, TextLine from guillotina.test_package import ITestBehavior from guillotina.tests import utils from guillotina.transactions import transaction -from guillotina.utils import get_behavior -from guillotina.utils import get_database -from guillotina.utils import get_object_by_oid +from guillotina.utils import get_behavior, get_database, get_object_by_oid -import json -import pickle -import pytest pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_contentapi.py b/guillotina/tests/test_contentapi.py index 5d00781fb..291e09ec5 100644 --- a/guillotina/tests/test_contentapi.py +++ b/guillotina/tests/test_contentapi.py @@ -1,7 +1,8 @@ +import pytest + from guillotina.contentapi import ContentAPI from guillotina.utils import get_content_path -import pytest pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_cors.py b/guillotina/tests/test_cors.py index 8d90fee78..c36d49f41 100644 --- a/guillotina/tests/test_cors.py +++ b/guillotina/tests/test_cors.py @@ -1,10 +1,12 @@ from copy import deepcopy + +import pytest + from guillotina import cors from guillotina._settings import app_settings from guillotina.response import HTTPUnauthorized from guillotina.tests.utils import get_mocked_request -import pytest pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_dbobjects.py b/guillotina/tests/test_dbobjects.py index 58a16d49c..00d5b68a4 100644 --- a/guillotina/tests/test_dbobjects.py +++ b/guillotina/tests/test_dbobjects.py @@ -1,12 +1,12 @@ +import pytest +from zope.interface import implementer + from guillotina.behaviors.dublincore import IDublinCore from guillotina.content import Item from guillotina.db.orm.base import BaseObject from guillotina.db.transaction import Transaction -from guillotina.interfaces import IAnnotations -from guillotina.interfaces import IResource -from zope.interface import implementer +from guillotina.interfaces import IAnnotations, IResource -import pytest pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_dynamic_schema.py b/guillotina/tests/test_dynamic_schema.py index e34e59956..59c7e4144 100644 --- a/guillotina/tests/test_dynamic_schema.py +++ b/guillotina/tests/test_dynamic_schema.py @@ -1,15 +1,15 @@ +import json + +import pytest +import pytest_asyncio +from zope.interface import Interface + from guillotina import configure -from guillotina.behaviors.dynamic import IDynamicFields -from guillotina.behaviors.dynamic import IDynamicFieldValues +from guillotina.behaviors.dynamic import IDynamicFields, IDynamicFieldValues from guillotina.behaviors.properties import FunctionProperty -from guillotina.content import Item -from guillotina.content import load_cached_schema +from guillotina.content import Item, load_cached_schema from guillotina.tests.utils import ContainerRequesterAsyncContextManager -from zope.interface import Interface -import json -import pytest -import pytest_asyncio pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_errors.py b/guillotina/tests/test_errors.py index a0a108967..0c5e49ab5 100644 --- a/guillotina/tests/test_errors.py +++ b/guillotina/tests/test_errors.py @@ -1,11 +1,11 @@ -from guillotina.exceptions import DeserializationError -from guillotina.exceptions import ValueDeserializationError -from guillotina.response import Response +import asyncio from unittest import mock -import asyncio import pytest +from guillotina.exceptions import DeserializationError, ValueDeserializationError +from guillotina.response import Response + @pytest.mark.asyncio async def test_non_existing_container(container_requester): diff --git a/guillotina/tests/test_extrafieldattr.py b/guillotina/tests/test_extrafieldattr.py index 161192890..1e294575b 100644 --- a/guillotina/tests/test_extrafieldattr.py +++ b/guillotina/tests/test_extrafieldattr.py @@ -1,5 +1,6 @@ import pytest + pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_files.py b/guillotina/tests/test_files.py index 3e56fcfcb..6395c72e2 100644 --- a/guillotina/tests/test_files.py +++ b/guillotina/tests/test_files.py @@ -1,10 +1,9 @@ +import pytest + from guillotina.exceptions import UnRetryableRequestError -from guillotina.files.utils import get_contenttype -from guillotina.files.utils import read_request_data +from guillotina.files.utils import get_contenttype, read_request_data from guillotina.tests.utils import get_mocked_request -import pytest - @pytest.mark.asyncio async def test_read_request_data_handles_retries(): diff --git a/guillotina/tests/test_guillo.py b/guillotina/tests/test_guillo.py index 521e3f53a..8fc424612 100644 --- a/guillotina/tests/test_guillo.py +++ b/guillotina/tests/test_guillo.py @@ -1,12 +1,13 @@ +import json +import logging from copy import deepcopy -from guillotina import testing -from guillotina import utils + +import pytest + +from guillotina import testing, utils from guillotina.component import globalregistry from guillotina.factory import make_app -import json -import logging -import pytest pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_info.py b/guillotina/tests/test_info.py index a2709c04c..b18d1d7fe 100644 --- a/guillotina/tests/test_info.py +++ b/guillotina/tests/test_info.py @@ -1,6 +1,7 @@ +import pytest + from guillotina.auth.recaptcha import VALIDATION_HEADER -import pytest pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_jsonschema.py b/guillotina/tests/test_jsonschema.py index 2aa990645..41bfcb15e 100644 --- a/guillotina/tests/test_jsonschema.py +++ b/guillotina/tests/test_jsonschema.py @@ -1,11 +1,10 @@ -from guillotina.behaviors.dublincore import IDublinCore -from guillotina.json.utils import convert_interfaces_to_schema -from guillotina.utils import get_schema_validator -from guillotina.utils import JSONSchemaRefResolver - import jsonschema import pytest +from guillotina.behaviors.dublincore import IDublinCore +from guillotina.json.utils import convert_interfaces_to_schema +from guillotina.utils import JSONSchemaRefResolver, get_schema_validator + def test_convert_dublin_core(dummy_guillotina): all_schemas = convert_interfaces_to_schema([IDublinCore]) diff --git a/guillotina/tests/test_login.py b/guillotina/tests/test_login.py index 3e8513cb0..4b0465b9d 100644 --- a/guillotina/tests/test_login.py +++ b/guillotina/tests/test_login.py @@ -1,10 +1,12 @@ -from guillotina.auth.users import ROOT_USER_ID -from guillotina.testing import TESTING_SETTINGS - import json + import jwt import pytest +from guillotina.auth.users import ROOT_USER_ID +from guillotina.testing import TESTING_SETTINGS + + pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_metrics.py b/guillotina/tests/test_metrics.py index 2a4479341..a2580a688 100644 --- a/guillotina/tests/test_metrics.py +++ b/guillotina/tests/test_metrics.py @@ -1,4 +1,11 @@ +import asyncio +import pickle +from unittest.mock import MagicMock + +import prometheus_client +import pytest from asyncmock import AsyncMock + from guillotina import metrics from guillotina.const import ROOT_ID from guillotina.content import Container @@ -8,12 +15,7 @@ from guillotina.db.transaction import Transaction from guillotina.db.transaction_manager import TransactionManager from guillotina.tests.utils import create_content -from unittest.mock import MagicMock -import asyncio -import pickle -import prometheus_client -import pytest pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_middlewares.py b/guillotina/tests/test_middlewares.py index 8d9d1b80a..8ac068612 100644 --- a/guillotina/tests/test_middlewares.py +++ b/guillotina/tests/test_middlewares.py @@ -1,10 +1,11 @@ -from guillotina.middlewares.errors import generate_error_response - import asyncio -import pytest import time import unittest +import pytest + +from guillotina.middlewares.errors import generate_error_response + class AsgiMiddlewate: def __init__(self, app): diff --git a/guillotina/tests/test_postgres.py b/guillotina/tests/test_postgres.py index 92f8391c7..cb0ad5491 100644 --- a/guillotina/tests/test_postgres.py +++ b/guillotina/tests/test_postgres.py @@ -1,3 +1,11 @@ +import asyncio +import os +from unittest.mock import Mock, patch +from uuid import uuid4 + +import asyncpg +import pytest + from guillotina.api.container import create_container from guillotina.component import get_adapter from guillotina.content import Folder @@ -5,18 +13,10 @@ from guillotina.db.storages.cockroach import CockroachStorage from guillotina.db.storages.pg import PostgresqlStorage from guillotina.db.transaction_manager import TransactionManager -from guillotina.exceptions import ConflictError -from guillotina.exceptions import ConflictIdOnContainer +from guillotina.exceptions import ConflictError, ConflictIdOnContainer from guillotina.tests import mocks from guillotina.tests.utils import create_content -from unittest.mock import Mock -from unittest.mock import patch -from uuid import uuid4 -import asyncio -import asyncpg -import os -import pytest pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_queue.py b/guillotina/tests/test_queue.py index d745f2a2f..92c5f5656 100644 --- a/guillotina/tests/test_queue.py +++ b/guillotina/tests/test_queue.py @@ -1,12 +1,13 @@ -from guillotina.async_util import IAsyncJobPool -from guillotina.async_util import IQueueUtility +import asyncio + +import pytest + +from guillotina.async_util import IAsyncJobPool, IQueueUtility from guillotina.browser import View from guillotina.component import get_utility from guillotina.tests import utils from guillotina.transactions import transaction -import asyncio -import pytest pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_renderers.py b/guillotina/tests/test_renderers.py index 4b462dab9..c2799e6a7 100644 --- a/guillotina/tests/test_renderers.py +++ b/guillotina/tests/test_renderers.py @@ -1,8 +1,9 @@ from datetime import datetime -from guillotina.renderers import guillotina_json_default import pytest +from guillotina.renderers import guillotina_json_default + def test_guillotina_json_default_doesnt_serialize_datetime(): def _makeOne(obj): diff --git a/guillotina/tests/test_request.py b/guillotina/tests/test_request.py index 8ae297dfe..94d9b7c4d 100644 --- a/guillotina/tests/test_request.py +++ b/guillotina/tests/test_request.py @@ -1,8 +1,10 @@ -from guillotina.tests.utils import make_mocked_request -from multidict import CIMultiDict - import asyncio + import pytest +from multidict import CIMultiDict + +from guillotina.tests.utils import make_mocked_request + pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_routes.py b/guillotina/tests/test_routes.py index 28bf03be7..aadb46d2f 100644 --- a/guillotina/tests/test_routes.py +++ b/guillotina/tests/test_routes.py @@ -1,8 +1,8 @@ +import pytest + from guillotina import routes from guillotina.response import InvalidRoute -import pytest - def test_convert_simple_route_to_view_name(): assert routes.Route("@foobar").view_name == "@foobar" diff --git a/guillotina/tests/test_security.py b/guillotina/tests/test_security.py index 5603d047e..125af3fc0 100644 --- a/guillotina/tests/test_security.py +++ b/guillotina/tests/test_security.py @@ -1,21 +1,22 @@ +import json + +import pytest + from guillotina.api.container import create_container from guillotina.auth.users import GuillotinaUser from guillotina.content import create_content_in_container -from guillotina.interfaces import Allow -from guillotina.interfaces import IRolePermissionManager +from guillotina.interfaces import Allow, IRolePermissionManager from guillotina.security.policy import cached_roles -from guillotina.security.utils import get_principals_with_access_content -from guillotina.security.utils import get_roles_with_access_content -from guillotina.security.utils import settings_for_object +from guillotina.security.utils import ( + get_principals_with_access_content, + get_roles_with_access_content, + settings_for_object, +) from guillotina.tests import utils from guillotina.tests.utils import get_db from guillotina.transactions import transaction -from guillotina.utils import get_authenticated_user -from guillotina.utils import get_roles_principal -from guillotina.utils import get_security_policy +from guillotina.utils import get_authenticated_user, get_roles_principal, get_security_policy -import json -import pytest pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_serialize.py b/guillotina/tests/test_serialize.py index 576774550..45d771997 100644 --- a/guillotina/tests/test_serialize.py +++ b/guillotina/tests/test_serialize.py @@ -1,30 +1,25 @@ -from datetime import datetime -from datetime import time +import uuid +from datetime import datetime, time + +import pytest from dateutil.tz import tzutc -from guillotina import fields -from guillotina import schema -from guillotina.component import get_adapter -from guillotina.component import get_multi_adapter +from zope.interface import Interface, Invalid +from zope.interface.interface import invariant + +from guillotina import fields, schema +from guillotina.component import get_adapter, get_multi_adapter from guillotina.exceptions import ValueDeserializationError from guillotina.fields import patch from guillotina.fields.interfaces import IPatchFieldOperation from guillotina.files.dbfile import DBFile -from guillotina.interfaces import IJSONToValue -from guillotina.interfaces import IResourceDeserializeFromJson -from guillotina.interfaces import IResourceSerializeToJson +from guillotina.interfaces import IJSONToValue, IResourceDeserializeFromJson, IResourceSerializeToJson from guillotina.json import deserialize_value from guillotina.json.deserialize_value import schema_compatible from guillotina.json.serialize_value import json_compatible from guillotina.json.utils import validate_invariants from guillotina.schema.exceptions import WrongType -from guillotina.tests.utils import create_content -from guillotina.tests.utils import login -from zope.interface import Interface -from zope.interface import Invalid -from zope.interface.interface import invariant +from guillotina.tests.utils import create_content, login -import pytest -import uuid pytestmark = pytest.mark.asyncio @@ -78,8 +73,7 @@ async def test_serialize_omit_main_interface_field(dummy_request, mock_txn): async def test_serialize_cloud_file(dummy_request, mock_txn): from guillotina.interfaces import IFileManager - from guillotina.test_package import FileContent - from guillotina.test_package import IFileContent + from guillotina.test_package import FileContent, IFileContent obj = create_content(FileContent) obj.file = DBFile(filename="foobar.json", md5="foobar") @@ -101,8 +95,7 @@ async def _data(): async def test_deserialize_cloud_file(dummy_request, mock_txn): - from guillotina.test_package import FileContent - from guillotina.test_package import IFileContent + from guillotina.test_package import FileContent, IFileContent obj = create_content(FileContent) obj.file = None diff --git a/guillotina/tests/test_server.py b/guillotina/tests/test_server.py index 4aa6593f2..f1d272d3b 100644 --- a/guillotina/tests/test_server.py +++ b/guillotina/tests/test_server.py @@ -1,12 +1,13 @@ +import asyncio +from unittest import mock + +import pytest + from guillotina.component import get_utility from guillotina.exceptions import ConflictError from guillotina.factory.app import close_utilities from guillotina.test_package import ITestAsyncUtility from guillotina.traversal import TraversalRouter -from unittest import mock - -import asyncio -import pytest def test_make_app(dummy_guillotina): diff --git a/guillotina/tests/test_static.py b/guillotina/tests/test_static.py index 461f56917..f4dc49865 100644 --- a/guillotina/tests/test_static.py +++ b/guillotina/tests/test_static.py @@ -1,7 +1,8 @@ +import pytest + from guillotina.component import get_utility from guillotina.interfaces import IApplication -import pytest pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_storages.py b/guillotina/tests/test_storages.py index 4b8f29f3d..50cdfd2ed 100644 --- a/guillotina/tests/test_storages.py +++ b/guillotina/tests/test_storages.py @@ -1,12 +1,14 @@ +import json +import os + +import pytest + from guillotina._settings import app_settings from guillotina.component import get_adapter from guillotina.db.factory import CockroachDatabaseManager from guillotina.db.interfaces import IDatabaseManager from guillotina.utils import get_database -import json -import os -import pytest pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_swagger.py b/guillotina/tests/test_swagger.py index d332999ad..b2a6b8df5 100644 --- a/guillotina/tests/test_swagger.py +++ b/guillotina/tests/test_swagger.py @@ -1,8 +1,9 @@ -from openapi_spec_validator import validate_v3_spec - import json import os + import pytest +from openapi_spec_validator import validate_v3_spec + pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_transactions.py b/guillotina/tests/test_transactions.py index fc65f82b4..033c5c349 100644 --- a/guillotina/tests/test_transactions.py +++ b/guillotina/tests/test_transactions.py @@ -1,15 +1,17 @@ +import pytest + from guillotina import task_vars from guillotina.content import create_content_in_container from guillotina.db import ROOT_ID -from guillotina.exceptions import TransactionClosedException -from guillotina.exceptions import TransactionNotFound -from guillotina.exceptions import TransactionObjectRegistrationMismatchException +from guillotina.exceptions import ( + TransactionClosedException, + TransactionNotFound, + TransactionObjectRegistrationMismatchException, +) from guillotina.tests import utils from guillotina.transactions import transaction -from guillotina.utils import get_database -from guillotina.utils import get_object_by_uid +from guillotina.utils import get_database, get_object_by_uid -import pytest pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/test_traversal.py b/guillotina/tests/test_traversal.py index 3b2b24a5b..12dabec50 100644 --- a/guillotina/tests/test_traversal.py +++ b/guillotina/tests/test_traversal.py @@ -1,9 +1,9 @@ +import pytest + from guillotina.response import Response from guillotina.tests.utils import get_mocked_request from guillotina.traversal import apply_cors -import pytest - class CorsTestRenderer: def __init__(self, settings): diff --git a/guillotina/tests/test_urls.py b/guillotina/tests/test_urls.py index c99bfc0c4..13b3c5c52 100644 --- a/guillotina/tests/test_urls.py +++ b/guillotina/tests/test_urls.py @@ -1,9 +1,10 @@ -from guillotina.tests.utils import make_mocked_request -from guillotina.utils import get_url - import json + import pytest +from guillotina.tests.utils import make_mocked_request +from guillotina.utils import get_url + def test_url(dummy_guillotina): request = make_mocked_request("GET", "/a/b", query_string=b"include=title") diff --git a/guillotina/tests/test_utils.py b/guillotina/tests/test_utils.py index a03f68190..5b6699fc4 100644 --- a/guillotina/tests/test_utils.py +++ b/guillotina/tests/test_utils.py @@ -1,17 +1,14 @@ +import json + +import pytest + from guillotina import utils from guillotina.behaviors.dublincore import IDublinCore -from guillotina.interfaces import IPrincipalRoleManager -from guillotina.interfaces import IResource -from guillotina.tests.utils import create_content -from guillotina.tests.utils import get_mocked_request -from guillotina.tests.utils import get_root -from guillotina.tests.utils import login +from guillotina.interfaces import IPrincipalRoleManager, IResource +from guillotina.tests.utils import create_content, get_mocked_request, get_root, login from guillotina.utils import get_behavior from guillotina.utils.navigator import Navigator -import json -import pytest - def test_module_resolve_path(): assert utils.resolve_module_path("guillotina") == "guillotina" diff --git a/guillotina/tests/test_vocabulary.py b/guillotina/tests/test_vocabulary.py index 95ba89406..bb4a4ef3c 100644 --- a/guillotina/tests/test_vocabulary.py +++ b/guillotina/tests/test_vocabulary.py @@ -1,8 +1,8 @@ +import pytest + from guillotina import configure from guillotina.schema.vocabulary import getVocabularyRegistry -import pytest - @configure.vocabulary(name="testvocab") class VocabTest: diff --git a/guillotina/tests/test_ws.py b/guillotina/tests/test_ws.py index 0e944e0ec..ba4967c5a 100644 --- a/guillotina/tests/test_ws.py +++ b/guillotina/tests/test_ws.py @@ -1,8 +1,10 @@ -from guillotina.testing import ADMIN_TOKEN - import json + import pytest +from guillotina.testing import ADMIN_TOKEN + + pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/utils.py b/guillotina/tests/utils.py index c1ae98d1c..b19efc909 100644 --- a/guillotina/tests/utils.py +++ b/guillotina/tests/utils.py @@ -1,20 +1,20 @@ +import asyncio +import json +import uuid +from typing import Dict + +from zope.interface import alsoProvides + from guillotina import task_vars from guillotina._settings import app_settings from guillotina.auth.users import RootUser from guillotina.auth.utils import set_authenticated_user from guillotina.behaviors import apply_markers from guillotina.content import Item -from guillotina.interfaces import IDefaultLayer -from guillotina.interfaces import IRequest +from guillotina.interfaces import IDefaultLayer, IRequest from guillotina.request import Request from guillotina.transactions import transaction from guillotina.utils import get_database -from typing import Dict -from zope.interface import alsoProvides - -import asyncio -import json -import uuid def get_db(app, db_id): diff --git a/guillotina/tests/vocabularies/test_vocabularies.py b/guillotina/tests/vocabularies/test_vocabularies.py index df7973b5a..f750677ab 100644 --- a/guillotina/tests/vocabularies/test_vocabularies.py +++ b/guillotina/tests/vocabularies/test_vocabularies.py @@ -1,5 +1,6 @@ import pytest + pytestmark = pytest.mark.asyncio diff --git a/guillotina/tests/workflows/test_workflow_basic.py b/guillotina/tests/workflows/test_workflow_basic.py index ae8748994..efbb6da0b 100644 --- a/guillotina/tests/workflows/test_workflow_basic.py +++ b/guillotina/tests/workflows/test_workflow_basic.py @@ -1,5 +1,6 @@ import pytest + pytestmark = pytest.mark.asyncio guillotina_basic_with_translations = { diff --git a/guillotina/transactions.py b/guillotina/transactions.py index cd622c5a0..647ec1409 100644 --- a/guillotina/transactions.py +++ b/guillotina/transactions.py @@ -1,10 +1,10 @@ -from guillotina import task_vars -from guillotina.db.interfaces import ITransaction -from guillotina.db.interfaces import ITransactionManager - import logging import typing +from guillotina import task_vars +from guillotina.db.interfaces import ITransaction, ITransactionManager + + logger = logging.getLogger("guillotina") diff --git a/guillotina/traversal.py b/guillotina/traversal.py index 331a28a0a..b793dfba3 100644 --- a/guillotina/traversal.py +++ b/guillotina/traversal.py @@ -1,61 +1,48 @@ """Main routing traversal class.""" +import asyncio +import traceback from contextlib import contextmanager -from guillotina import __version__ -from guillotina import logger -from guillotina import response -from guillotina import routes -from guillotina import task_vars +from typing import Optional, Tuple + +from zope.interface import alsoProvides + +from guillotina import __version__, logger, response, routes, task_vars from guillotina._settings import app_settings from guillotina.api.content import DefaultOPTIONS from guillotina.auth.users import AnonymousUser -from guillotina.auth.utils import authenticate_request -from guillotina.auth.utils import set_authenticated_user +from guillotina.auth.utils import authenticate_request, set_authenticated_user from guillotina.browser import View -from guillotina.component import get_utility -from guillotina.component import query_adapter -from guillotina.component import query_multi_adapter -from guillotina.contentnegotiation import get_acceptable_content_types -from guillotina.contentnegotiation import get_acceptable_languages +from guillotina.component import get_utility, query_adapter, query_multi_adapter +from guillotina.contentnegotiation import get_acceptable_content_types, get_acceptable_languages from guillotina.db.orm.interfaces import IBaseObject from guillotina.event import notify -from guillotina.events import BeforeRenderViewEvent -from guillotina.events import ObjectLoadedEvent -from guillotina.events import TraversalRouteMissEvent -from guillotina.events import TraversalViewMissEvent -from guillotina.exceptions import ApplicationNotFound -from guillotina.exceptions import ConflictError -from guillotina.exceptions import TIDConflictError -from guillotina.interfaces import ACTIVE_LAYERS_KEY -from guillotina.interfaces import IApplication -from guillotina.interfaces import IAsyncContainer -from guillotina.interfaces import IContainer -from guillotina.interfaces import IDatabase -from guillotina.interfaces import ILanguage -from guillotina.interfaces import IOPTIONS -from guillotina.interfaces import IPermission -from guillotina.interfaces import IRenderer -from guillotina.interfaces import IRequest -from guillotina.interfaces import IResponse -from guillotina.interfaces import ITraversable +from guillotina.events import ( + BeforeRenderViewEvent, + ObjectLoadedEvent, + TraversalRouteMissEvent, + TraversalViewMissEvent, +) +from guillotina.exceptions import ApplicationNotFound, ConflictError, TIDConflictError +from guillotina.interfaces import ( + ACTIVE_LAYERS_KEY, + IOPTIONS, + IApplication, + IAsyncContainer, + IContainer, + IDatabase, + ILanguage, + IPermission, + IRenderer, + IRequest, + IResponse, + ITraversable, +) from guillotina.profile import profilable -from guillotina.response import HTTPBadRequest -from guillotina.response import HTTPMethodNotAllowed -from guillotina.response import HTTPNotFound -from guillotina.response import HTTPUnauthorized -from guillotina.response import Response +from guillotina.response import HTTPBadRequest, HTTPMethodNotAllowed, HTTPNotFound, HTTPUnauthorized, Response from guillotina.security.utils import get_view_permission -from guillotina.transactions import abort -from guillotina.transactions import commit -from guillotina.utils import get_registry -from guillotina.utils import get_security_policy -from guillotina.utils import import_class -from typing import Optional -from typing import Tuple -from zope.interface import alsoProvides - -import asyncio -import traceback +from guillotina.transactions import abort, commit +from guillotina.utils import get_registry, get_security_policy, import_class async def traverse( diff --git a/guillotina/utils/__init__.py b/guillotina/utils/__init__.py index 745f7c7af..30f477733 100644 --- a/guillotina/utils/__init__.py +++ b/guillotina/utils/__init__.py @@ -17,6 +17,7 @@ from .content import valid_id # noqa from .crypto import get_jwk_key # noqa from .crypto import secure_passphrase # noqa +from .misc import JSONSchemaRefResolver # noqa from .misc import apply_coroutine # noqa; noqa from .misc import dump_task_vars # noqa from .misc import find_container # noqa @@ -29,7 +30,6 @@ from .misc import get_request_scheme # noqa from .misc import get_schema_validator # noqa from .misc import get_url # noqa -from .misc import JSONSchemaRefResolver # noqa from .misc import lazy_apply # noqa from .misc import list_or_dict_items # noqa from .misc import load_task_vars # noqa @@ -51,4 +51,5 @@ from .modules import resolve_path # noqa from .navigator import Navigator # noqa + get_object_by_oid = get_object_by_uid diff --git a/guillotina/utils/auth.py b/guillotina/utils/auth.py index 5b41c075a..2320fe139 100644 --- a/guillotina/utils/auth.py +++ b/guillotina/utils/auth.py @@ -1,8 +1,8 @@ +from typing import Optional + from guillotina import task_vars from guillotina.component import get_adapter -from guillotina.interfaces import IPrincipal -from guillotina.interfaces import ISecurityPolicy -from typing import Optional +from guillotina.interfaces import IPrincipal, ISecurityPolicy def get_authenticated_user() -> Optional[IPrincipal]: diff --git a/guillotina/utils/content.py b/guillotina/utils/content.py index 2c89a0ba3..b0595923e 100644 --- a/guillotina/utils/content.py +++ b/guillotina/utils/content.py @@ -1,25 +1,25 @@ -from .misc import get_current_request -from .misc import list_or_dict_items -from guillotina import glogging -from guillotina import task_vars +import typing + +from guillotina import glogging, task_vars from guillotina._settings import app_settings -from guillotina.component import get_adapter -from guillotina.component import get_utility -from guillotina.component import query_multi_adapter +from guillotina.component import get_adapter, get_utility, query_multi_adapter from guillotina.const import TRASHED_ID from guillotina.db.interfaces import IDatabaseManager from guillotina.db.orm.interfaces import IBaseObject from guillotina.exceptions import DatabaseNotFound -from guillotina.interfaces import IAbsoluteURL -from guillotina.interfaces import IApplication -from guillotina.interfaces import IAsyncContainer -from guillotina.interfaces import IContainer -from guillotina.interfaces import IDatabase -from guillotina.interfaces import IPrincipalRoleMap -from guillotina.interfaces import IRequest -from guillotina.interfaces import IResource +from guillotina.interfaces import ( + IAbsoluteURL, + IApplication, + IAsyncContainer, + IContainer, + IDatabase, + IPrincipalRoleMap, + IRequest, + IResource, +) + +from .misc import get_current_request, list_or_dict_items -import typing logger = glogging.getLogger("guillotina") diff --git a/guillotina/utils/crypto.py b/guillotina/utils/crypto.py index 4e0867946..1ef417f8a 100644 --- a/guillotina/utils/crypto.py +++ b/guillotina/utils/crypto.py @@ -1,9 +1,11 @@ -from guillotina._settings import app_settings -from jwcrypto import jwk - import logging import string +from jwcrypto import jwk + +from guillotina._settings import app_settings + + logger = logging.getLogger("guillotina") diff --git a/guillotina/utils/execute.py b/guillotina/utils/execute.py index 54bcdaf17..bdf950013 100644 --- a/guillotina/utils/execute.py +++ b/guillotina/utils/execute.py @@ -1,20 +1,16 @@ +import asyncio +import uuid from functools import partial +from typing import Any, Callable, Coroutine, Optional + from guillotina import task_vars from guillotina.component import get_utility from guillotina.exceptions import TransactionNotFound -from guillotina.interfaces import IAsyncJobPool -from guillotina.interfaces import IQueueUtility +from guillotina.interfaces import IAsyncJobPool, IQueueUtility from guillotina.profile import profilable from guillotina.task_vars import txn from guillotina.transactions import get_transaction from guillotina.utils import notice_on_error_internal -from typing import Any -from typing import Callable -from typing import Coroutine -from typing import Optional - -import asyncio -import uuid class ExecuteContext: diff --git a/guillotina/utils/misc.py b/guillotina/utils/misc.py index 5a8b7989a..2c3ecc912 100644 --- a/guillotina/utils/misc.py +++ b/guillotina/utils/misc.py @@ -1,27 +1,5 @@ -from collections.abc import MutableMapping -from functools import partial -from guillotina import glogging -from guillotina import task_vars -from guillotina._settings import app_settings -from guillotina.component import get_utility -from guillotina.db.interfaces import ITransaction -from guillotina.exceptions import ContainerNotFound -from guillotina.exceptions import DatabaseNotFound -from guillotina.exceptions import RequestNotFound -from guillotina.exceptions import TransactionNotFound -from guillotina.interfaces import IAnnotations -from guillotina.interfaces import IApplication -from guillotina.interfaces import IContainer -from guillotina.interfaces import IDatabase -from guillotina.interfaces import IRegistry -from guillotina.interfaces import IRequest -from guillotina.profile import profilable -from hashlib import sha256 as sha -from urllib.parse import unquote - import asyncio import inspect -import jsonschema.validators import os import random import string @@ -29,6 +7,21 @@ import types import typing import urllib.parse +from collections.abc import MutableMapping +from functools import partial +from hashlib import sha256 as sha +from urllib.parse import unquote + +import jsonschema.validators + +from guillotina import glogging, task_vars +from guillotina._settings import app_settings +from guillotina.component import get_utility +from guillotina.db.interfaces import ITransaction +from guillotina.exceptions import ContainerNotFound, DatabaseNotFound, RequestNotFound, TransactionNotFound +from guillotina.interfaces import IAnnotations, IApplication, IContainer, IDatabase, IRegistry, IRequest +from guillotina.profile import profilable + try: random = random.SystemRandom() # type: ignore diff --git a/guillotina/utils/modules.py b/guillotina/utils/modules.py index cd84a109d..40189ffef 100644 --- a/guillotina/utils/modules.py +++ b/guillotina/utils/modules.py @@ -1,14 +1,14 @@ -from guillotina.gtypes import ResolvableType -from typing import Any -from typing import Optional -from zope.interface.interfaces import IInterface - import importlib import inspect import os import pathlib import sys import types +from typing import Any, Optional + +from zope.interface.interfaces import IInterface + +from guillotina.gtypes import ResolvableType def import_class(import_string: str) -> Optional[types.ModuleType]: diff --git a/guillotina/utils/navigator.py b/guillotina/utils/navigator.py index fa627c7f1..488646d4d 100644 --- a/guillotina/utils/navigator.py +++ b/guillotina/utils/navigator.py @@ -1,9 +1,10 @@ -from .content import get_content_path +import posixpath +import weakref + from guillotina import task_vars from guillotina.interfaces import IResource -import posixpath -import weakref +from .content import get_content_path class Navigator: From 63900e57ddbbe9f25047c5580c30667075d078db Mon Sep 17 00:00:00 2001 From: nilbacardit26 Date: Mon, 30 Mar 2026 13:32:57 +0200 Subject: [PATCH 26/31] using jinja2 FileSystemloader and upgrade test suite semantics --- guillotina/__init__.py | 13 +++++++++++-- guillotina/api/behaviors.py | 2 +- guillotina/api/content.py | 3 ++- guillotina/api/files.py | 20 ++++++++++---------- guillotina/api/registry.py | 2 +- guillotina/contrib/pubsub/utility.py | 1 - guillotina/contrib/swagger/services.py | 4 ++-- guillotina/contrib/templates/utility.py | 16 ++++++++++++++-- guillotina/documentation/sphinx/__init__.py | 5 ++--- guillotina/tests/image/test_scale.py | 2 +- guillotina/tests/test_middlewares.py | 4 ++-- guillotina/tests/test_swagger.py | 4 ++-- requirements.txt | 4 ++-- setup.py | 4 ++-- 14 files changed, 52 insertions(+), 32 deletions(-) diff --git a/guillotina/__init__.py b/guillotina/__init__.py index ac27d0392..a8c34e5f1 100644 --- a/guillotina/__init__.py +++ b/guillotina/__init__.py @@ -1,7 +1,9 @@ # load the patch before anything else. import os +from importlib.metadata import PackageNotFoundError +from importlib.metadata import version +from pathlib import Path -import pkg_resources from zope.interface import Interface # noqa from guillotina import glogging @@ -13,7 +15,14 @@ from guillotina.i18n import default_message_factory as _ # noqa -__version__ = pkg_resources.get_distribution("guillotina").version +def _resolve_version(): + try: + return version("guillotina") + except PackageNotFoundError: + return (Path(__file__).resolve().parents[1] / "VERSION").read_text().strip() + + +__version__ = _resolve_version() # create logging diff --git a/guillotina/api/behaviors.py b/guillotina/api/behaviors.py index 29da66631..ade95f0d2 100644 --- a/guillotina/api/behaviors.py +++ b/guillotina/api/behaviors.py @@ -46,7 +46,7 @@ async def default_patch(context, request): permission="guillotina.ModifyContent", name="@behaviors/{behavior}", summary="Remove behavior from resource", - parameters=[{"in": "path", "name": "key", "required": True, "schema": {"type": "string"}}], + parameters=[{"in": "path", "name": "behavior", "required": True, "schema": {"type": "string"}}], requestBody={ "required": True, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Behavior"}}}, diff --git a/guillotina/api/content.py b/guillotina/api/content.py index 7dcc83396..23751f822 100644 --- a/guillotina/api/content.py +++ b/guillotina/api/content.py @@ -608,6 +608,7 @@ async def move(context, request): "content": { "application/json": { "schema": { + "type": "object", "properties": { "destination": { "type": "string", @@ -615,7 +616,7 @@ async def move(context, request): }, "new_id": {"type": "string", "description": "Optional new id to assign object"}, "reset_acl": { - "type": "bool", + "type": "boolean", "description": "Remove users and roles from acl, except for the request user", "default": False, }, diff --git a/guillotina/api/files.py b/guillotina/api/files.py index 327d89033..3126b4fa7 100644 --- a/guillotina/api/files.py +++ b/guillotina/api/files.py @@ -36,12 +36,12 @@ def _traversed_file_doc(summary, parameters=None, responses=None): TUS_PARAMETERS = [ - {"name": "Upload-Offset", "in": "headers", "required": True, "schema": {"type": "integer"}}, - {"name": "UPLOAD-LENGTH", "in": "headers", "required": True, "schema": {"type": "integer"}}, - {"name": "UPLOAD-MD5", "in": "headers", "required": False, "schema": {"type": "string"}}, - {"name": "UPLOAD-EXTENSION", "in": "headers", "required": False, "schema": {"type": "string"}}, - {"name": "TUS-RESUMABLE", "in": "headers", "required": True, "schema": {"type": "string"}}, - {"name": "UPLOAD-METADATA", "in": "headers", "required": False, "schema": {"type": "string"}}, + {"name": "Upload-Offset", "in": "header", "required": True, "schema": {"type": "integer"}}, + {"name": "UPLOAD-LENGTH", "in": "header", "required": True, "schema": {"type": "integer"}}, + {"name": "UPLOAD-MD5", "in": "header", "required": False, "schema": {"type": "string"}}, + {"name": "UPLOAD-EXTENSION", "in": "header", "required": False, "schema": {"type": "string"}}, + {"name": "TUS-RESUMABLE", "in": "header", "required": True, "schema": {"type": "string"}}, + {"name": "UPLOAD-METADATA", "in": "header", "required": False, "schema": {"type": "string"}}, ] @@ -311,8 +311,8 @@ async def __call__(self): **_traversed_file_doc( "TUS endpoint", parameters=[ - {"name": "Upload-Offset", "in": "headers", "required": True, "schema": {"type": "integer"}}, - {"name": "CONTENT-LENGTH", "in": "headers", "required": True, "schema": {"type": "integer"}}, + {"name": "Upload-Offset", "in": "header", "required": True, "schema": {"type": "integer"}}, + {"name": "CONTENT-LENGTH", "in": "header", "required": True, "schema": {"type": "integer"}}, ], responses={ "204": { @@ -330,8 +330,8 @@ async def __call__(self): **_traversed_file_doc( "TUS endpoint", parameters=[ - {"name": "Upload-Offset", "in": "headers", "required": True, "schema": {"type": "integer"}}, - {"name": "CONTENT-LENGTH", "in": "headers", "required": True, "schema": {"type": "integer"}}, + {"name": "Upload-Offset", "in": "header", "required": True, "schema": {"type": "integer"}}, + {"name": "CONTENT-LENGTH", "in": "header", "required": True, "schema": {"type": "integer"}}, {"name": "file_key", "in": "path", "required": True, "schema": {"type": "string"}}, ], responses={ diff --git a/guillotina/api/registry.py b/guillotina/api/registry.py index 407ba5dcb..4648488fd 100644 --- a/guillotina/api/registry.py +++ b/guillotina/api/registry.py @@ -144,7 +144,7 @@ async def __call__(self): name="@registry/{dotted_name}", summary="Update registry setting", validate=True, - parameters=[{"in": "path", "name": "dotter_name", "required": True, "schema": {"type": "string"}}], + parameters=[{"in": "path", "name": "dotted_name", "required": True, "schema": {"type": "string"}}], requestBody={ "required": True, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UpdateRegistry"}}}, diff --git a/guillotina/contrib/pubsub/utility.py b/guillotina/contrib/pubsub/utility.py index 76a3fa219..733d29ac0 100644 --- a/guillotina/contrib/pubsub/utility.py +++ b/guillotina/contrib/pubsub/utility.py @@ -54,7 +54,6 @@ async def finalize(self, app): channel.cancel() self._initialized = False await asyncio.sleep(0.1) - async def real_subscribe(self, channel, channel_name): while channel_name in self._subscribers: try: diff --git a/guillotina/contrib/swagger/services.py b/guillotina/contrib/swagger/services.py index c33e23ee8..8600ba2a1 100644 --- a/guillotina/contrib/swagger/services.py +++ b/guillotina/contrib/swagger/services.py @@ -3,10 +3,10 @@ import os from urllib.parse import urlparse -import pkg_resources from zope.interface import Interface from guillotina import app_settings, configure +from guillotina import __version__ from guillotina.api.service import Service from guillotina.utils import ( get_authenticated_user, @@ -165,7 +165,7 @@ async def __call__(self): definition["servers"][0]["url"] = url if "version" not in definition["info"]: - definition["info"]["version"] = pkg_resources.get_distribution("guillotina").version + definition["info"]["version"] = __version__ api_defs = app_settings["api_definition"] diff --git a/guillotina/contrib/templates/utility.py b/guillotina/contrib/templates/utility.py index cdce63eee..3fdd1433d 100644 --- a/guillotina/contrib/templates/utility.py +++ b/guillotina/contrib/templates/utility.py @@ -1,8 +1,10 @@ import logging +from importlib import import_module +from pathlib import Path from concurrent.futures import ThreadPoolExecutor from functools import partial -from jinja2 import BaseLoader, Environment, PackageLoader, select_autoescape +from jinja2 import BaseLoader, Environment, FileSystemLoader, select_autoescape from jinja2.exceptions import TemplateNotFound from lru import LRU @@ -14,6 +16,15 @@ logger = logging.getLogger("guillotina") +def _resolve_template_dir(package_name, folder): + module = import_module(package_name) + if getattr(module, "__file__", None): + package_dir = Path(module.__file__).resolve().parent + else: # pragma: no cover + package_dir = Path(next(iter(module.__path__))).resolve() + return package_dir / folder + + class JinjaUtility: def __init__(self, settings): self.envs = [] @@ -32,7 +43,8 @@ async def initialize(self, app=None): for template in self.templates: package, folder = template.split(":") env = Environment( - loader=PackageLoader(package, folder), autoescape=select_autoescape(["html", "xml", "pt"]) + loader=FileSystemLoader(str(_resolve_template_dir(package, folder))), + autoescape=select_autoescape(["html", "xml", "pt"]), ) self.envs.append(env) diff --git a/guillotina/documentation/sphinx/__init__.py b/guillotina/documentation/sphinx/__init__.py index 69f8e73e6..ec8c97a88 100644 --- a/guillotina/documentation/sphinx/__init__.py +++ b/guillotina/documentation/sphinx/__init__.py @@ -6,13 +6,13 @@ from typing import Any, Dict, Optional import docutils.statemachine -import pkg_resources from async_asgi_testclient import TestClient from docutils import nodes from docutils.parsers.rst import Directive # type: ignore from docutils.parsers.rst import directives # type: ignore from zope.interface import Interface +from guillotina import __version__ from guillotina import routes from guillotina._settings import app_settings from guillotina.component import query_multi_adapter @@ -268,5 +268,4 @@ def run(self): def setup(app): app.add_directive_to_domain("http", "gapi", APICall) - dist = pkg_resources.get_distribution("guillotina") - return {"version": dist.version} + return {"version": __version__} diff --git a/guillotina/tests/image/test_scale.py b/guillotina/tests/image/test_scale.py index 10c16d09e..ab7d81962 100644 --- a/guillotina/tests/image/test_scale.py +++ b/guillotina/tests/image/test_scale.py @@ -202,7 +202,7 @@ def testQuality(self): img3 = scaleImage(CMYK, 84, 103, quality=20)[0] self.assertNotEqual(img1, img2) self.assertNotEqual(img1, img3) - self.failUnless(len(img1) > len(img2) > len(img3)) + self.assertTrue(len(img1) > len(img2) > len(img3)) def testResultBuffer(self): img1 = scaleImage(PNG, 84, 103)[0] diff --git a/guillotina/tests/test_middlewares.py b/guillotina/tests/test_middlewares.py index 8ac068612..e12c770e3 100644 --- a/guillotina/tests/test_middlewares.py +++ b/guillotina/tests/test_middlewares.py @@ -42,9 +42,9 @@ def _makeOne(self, exc, request=None): def test_cancelled_error(self): resp = self._makeOne(asyncio.CancelledError()) assert resp.content["message"].startswith("Cancelled execution") - self.assertEquals(resp.content["reason"], "unknownError") + self.assertEqual(resp.content["reason"], "unknownError") def test_other_error(self): resp = self._makeOne(ValueError()) assert resp.content["message"].startswith("Error on execution") - self.assertEquals(resp.content["reason"], "unknownError") + self.assertEqual(resp.content["reason"], "unknownError") diff --git a/guillotina/tests/test_swagger.py b/guillotina/tests/test_swagger.py index b2a6b8df5..4ae48d2c8 100644 --- a/guillotina/tests/test_swagger.py +++ b/guillotina/tests/test_swagger.py @@ -2,7 +2,7 @@ import os import pytest -from openapi_spec_validator import validate_v3_spec +from openapi_spec_validator import validate_spec pytestmark = pytest.mark.asyncio @@ -42,7 +42,7 @@ async def test_validate_swagger_definition(container_requester): for path in ("/", "/db", "/db/guillotina"): resp, status = await requester("GET", os.path.join(path, "@swagger")) assert status == 200 - validate_v3_spec(resp) + validate_spec(resp) @pytest.mark.app_settings(SWAGGER_SETTINGS) diff --git a/requirements.txt b/requirements.txt index a2c121025..128c28fad 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,14 +2,14 @@ Cython==0.29.24 asyncpg>=0.28.0 cffi==1.14.6 chardet==3.0.4 -jsonschema==2.6.0 +jsonschema==4.24.1 multidict==6.0.4 pycparser==2.20 pycryptodome==3.6.6 PyJWT~=2.1.0 python-dateutil==2.8.2 PyYaml>=5.1 -six==1.11.0 +six==1.17.0 orjson>=3,<4 zope.interface==5.1.0 uvicorn==0.42.0 diff --git a/setup.py b/setup.py index 85599187b..de37eee2f 100644 --- a/setup.py +++ b/setup.py @@ -57,7 +57,7 @@ install_requires=[ "uvicorn", "websockets", - "jsonschema==2.6.0", + "jsonschema==4.24.1", "python-dateutil", "pycryptodome", "jwcrypto", @@ -88,7 +88,7 @@ "pytest-docker-fixtures==1.4.2", "pytest-rerunfailures>=12.0,<13.0", "async-asgi-testclient<2.0.0", - "openapi-spec-validator==0.2.9", + "openapi-spec-validator==0.8.4", "aiohttp>=3.0.0,<4.0.0", "asyncmock", "prometheus-client", From fd05354d69cf9443ed3c505394f9056654a0face Mon Sep 17 00:00:00 2001 From: nilbacardit26 Date: Mon, 30 Mar 2026 13:35:03 +0200 Subject: [PATCH 27/31] flake8 isort --- guillotina/__init__.py | 3 +-- guillotina/contrib/pubsub/utility.py | 1 + guillotina/contrib/swagger/services.py | 3 +-- guillotina/contrib/templates/utility.py | 4 ++-- guillotina/documentation/sphinx/__init__.py | 3 +-- 5 files changed, 6 insertions(+), 8 deletions(-) diff --git a/guillotina/__init__.py b/guillotina/__init__.py index a8c34e5f1..52f51f0f9 100644 --- a/guillotina/__init__.py +++ b/guillotina/__init__.py @@ -1,7 +1,6 @@ # load the patch before anything else. import os -from importlib.metadata import PackageNotFoundError -from importlib.metadata import version +from importlib.metadata import PackageNotFoundError, version from pathlib import Path from zope.interface import Interface # noqa diff --git a/guillotina/contrib/pubsub/utility.py b/guillotina/contrib/pubsub/utility.py index 733d29ac0..76a3fa219 100644 --- a/guillotina/contrib/pubsub/utility.py +++ b/guillotina/contrib/pubsub/utility.py @@ -54,6 +54,7 @@ async def finalize(self, app): channel.cancel() self._initialized = False await asyncio.sleep(0.1) + async def real_subscribe(self, channel, channel_name): while channel_name in self._subscribers: try: diff --git a/guillotina/contrib/swagger/services.py b/guillotina/contrib/swagger/services.py index 8600ba2a1..ceaecaa72 100644 --- a/guillotina/contrib/swagger/services.py +++ b/guillotina/contrib/swagger/services.py @@ -5,8 +5,7 @@ from zope.interface import Interface -from guillotina import app_settings, configure -from guillotina import __version__ +from guillotina import __version__, app_settings, configure from guillotina.api.service import Service from guillotina.utils import ( get_authenticated_user, diff --git a/guillotina/contrib/templates/utility.py b/guillotina/contrib/templates/utility.py index 3fdd1433d..be67905e9 100644 --- a/guillotina/contrib/templates/utility.py +++ b/guillotina/contrib/templates/utility.py @@ -1,8 +1,8 @@ import logging -from importlib import import_module -from pathlib import Path from concurrent.futures import ThreadPoolExecutor from functools import partial +from importlib import import_module +from pathlib import Path from jinja2 import BaseLoader, Environment, FileSystemLoader, select_autoescape from jinja2.exceptions import TemplateNotFound diff --git a/guillotina/documentation/sphinx/__init__.py b/guillotina/documentation/sphinx/__init__.py index ec8c97a88..a8ffa8a2c 100644 --- a/guillotina/documentation/sphinx/__init__.py +++ b/guillotina/documentation/sphinx/__init__.py @@ -12,8 +12,7 @@ from docutils.parsers.rst import directives # type: ignore from zope.interface import Interface -from guillotina import __version__ -from guillotina import routes +from guillotina import __version__, routes from guillotina._settings import app_settings from guillotina.component import query_multi_adapter from guillotina.content import load_cached_schema From 869d4076659e985e372314ffc41fcc0e4d959f18 Mon Sep 17 00:00:00 2001 From: nilbacardit26 Date: Mon, 30 Mar 2026 18:19:20 +0200 Subject: [PATCH 28/31] keep existing invalidation going on mcp, ignore new ones --- guillotina/contrib/mcp/backend.py | 50 ++++++++++++++++++++++++--- guillotina/contrib/mcp/subscribers.py | 5 +-- 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/guillotina/contrib/mcp/backend.py b/guillotina/contrib/mcp/backend.py index 36a1cc9b2..e540e8c41 100644 --- a/guillotina/contrib/mcp/backend.py +++ b/guillotina/contrib/mcp/backend.py @@ -1,3 +1,4 @@ +import asyncio import hashlib import json import logging @@ -46,6 +47,8 @@ def __init__(self, settings: Optional[Dict[str, Any]] = None): self._register_default_tools() self._register_default_resources() self._key_cache_redis_prefix = "mcp_tool_cache:v1" + self._queue_invalidations = asyncio.Queue(maxsize=1) + self._worker_task: asyncio.Task = None async def initialize(self, app): self._cache_disabled = True @@ -55,6 +58,37 @@ async def initialize(self, app): self._cache_disabled = False except Exception: logger.info("redis not enabled to cache") + self._worker_task = asyncio.create_task(self.invalidate_cache_worker()) + + async def invalidate_cache_worker(self): + while True: + try: + reason_cache = await self._queue_invalidations.get() + except asyncio.CancelledError: + logger.info("Invalidation cache task cancelled") + return + + try: + await self._invalidate_cache(reason_cache) + except Exception: + logger.error("Error in invalidating cache", exc_info=True) + await asyncio.sleep(0.5) + finally: + self._queue_invalidations.task_done() + + async def finalize(self, app=None): + # Stop invalidate cache task + if self._worker_task is not None: + self._worker_task.cancel() + await asyncio.gather(self._worker_task, return_exceptions=True) + + def schedule_invalidate_cache(self, reason: str = "manual"): + # Keep the existing task running, ignore the new one, drop the + # Queue is size 1 + try: + self._queue_invalidations.put_nowait(reason) + except asyncio.QueueFull: + pass def _register_default_tools(self) -> None: for ( @@ -119,7 +153,9 @@ def list_tools(self) -> List[Dict[str, Any]]: "inputSchema": tool.input_schema, "cacheable": tool.cacheable, } - for tool in sorted(self._tools.values(), key=lambda registered: registered.name) + for tool in sorted( + self._tools.values(), key=lambda registered: registered.name + ) ] # ── Resource management ────────────────────────────────────────── @@ -158,7 +194,9 @@ def list_resources(self) -> List[Dict[str, Any]]: for res in sorted(self._resources.values(), key=lambda r: r.name) ] - async def read_resource(self, resource_name: str, context: Any, request: Any) -> Dict[str, Any]: + async def read_resource( + self, resource_name: str, context: Any, request: Any + ) -> Dict[str, Any]: clean_name = str(resource_name or "").strip() if clean_name not in self._resources: raise ValueError(f"Unknown MCP resource: {resource_name}") @@ -166,7 +204,9 @@ async def read_resource(self, resource_name: str, context: Any, request: Any) -> return await resource.handler(request) def _cache_key(self, tool_name: str, arguments: Dict[str, Any]) -> str: - payload = json.dumps(arguments, sort_keys=True, separators=(",", ":"), default=str) + payload = json.dumps( + arguments, sort_keys=True, separators=(",", ":"), default=str + ) digest = hashlib.sha256(payload.encode("utf-8")).hexdigest() return f"{self._key_cache_redis_prefix}:{tool_name}:{digest[:16]}" @@ -213,7 +253,9 @@ async def invoke( async def invalidate_cache(self, reason: str = "manual") -> None: if self._cache_disabled is False: - keys_to_delete = await self._driver_redis.keys_startswith(self._key_cache_redis_prefix) + keys_to_delete = await self._driver_redis.keys_startswith( + self._key_cache_redis_prefix + ) await self._driver_redis.delete_all(keys_to_delete) def metadata(self) -> Dict[str, Any]: diff --git a/guillotina/contrib/mcp/subscribers.py b/guillotina/contrib/mcp/subscribers.py index 559e2df55..56aeeabc3 100644 --- a/guillotina/contrib/mcp/subscribers.py +++ b/guillotina/contrib/mcp/subscribers.py @@ -1,5 +1,3 @@ -import asyncio - from guillotina import configure from guillotina.component import query_utility from guillotina.contrib.mcp.interfaces import IMCPToolRegistry @@ -19,5 +17,4 @@ async def invalidate_mcp_cache_on_content_change(obj, event): if registry is None: return object_id = getattr(obj, "id", None) or getattr(obj, "__name__", None) or "unknown" - coroutine = registry.invalidate_cache(reason=f"{event.__class__.__name__}:{object_id}") - asyncio.create_task(coroutine) + registry.schedule_invalidate_cache(reason=f"{event.__class__.__name__}:{object_id}") From ef98951fe9b616b741b4f3d7da5978198a7540d0 Mon Sep 17 00:00:00 2001 From: nilbacardit26 Date: Mon, 30 Mar 2026 18:25:49 +0200 Subject: [PATCH 29/31] black --- guillotina/contrib/mcp/backend.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/guillotina/contrib/mcp/backend.py b/guillotina/contrib/mcp/backend.py index e540e8c41..8e12c2d25 100644 --- a/guillotina/contrib/mcp/backend.py +++ b/guillotina/contrib/mcp/backend.py @@ -153,9 +153,7 @@ def list_tools(self) -> List[Dict[str, Any]]: "inputSchema": tool.input_schema, "cacheable": tool.cacheable, } - for tool in sorted( - self._tools.values(), key=lambda registered: registered.name - ) + for tool in sorted(self._tools.values(), key=lambda registered: registered.name) ] # ── Resource management ────────────────────────────────────────── @@ -194,9 +192,7 @@ def list_resources(self) -> List[Dict[str, Any]]: for res in sorted(self._resources.values(), key=lambda r: r.name) ] - async def read_resource( - self, resource_name: str, context: Any, request: Any - ) -> Dict[str, Any]: + async def read_resource(self, resource_name: str, context: Any, request: Any) -> Dict[str, Any]: clean_name = str(resource_name or "").strip() if clean_name not in self._resources: raise ValueError(f"Unknown MCP resource: {resource_name}") @@ -204,9 +200,7 @@ async def read_resource( return await resource.handler(request) def _cache_key(self, tool_name: str, arguments: Dict[str, Any]) -> str: - payload = json.dumps( - arguments, sort_keys=True, separators=(",", ":"), default=str - ) + payload = json.dumps(arguments, sort_keys=True, separators=(",", ":"), default=str) digest = hashlib.sha256(payload.encode("utf-8")).hexdigest() return f"{self._key_cache_redis_prefix}:{tool_name}:{digest[:16]}" @@ -253,9 +247,7 @@ async def invoke( async def invalidate_cache(self, reason: str = "manual") -> None: if self._cache_disabled is False: - keys_to_delete = await self._driver_redis.keys_startswith( - self._key_cache_redis_prefix - ) + keys_to_delete = await self._driver_redis.keys_startswith(self._key_cache_redis_prefix) await self._driver_redis.delete_all(keys_to_delete) def metadata(self) -> Dict[str, Any]: From f3c2c7ea3b6b9306713654fd09b43f21930042e9 Mon Sep 17 00:00:00 2001 From: nilbacardit26 Date: Mon, 30 Mar 2026 18:28:48 +0200 Subject: [PATCH 30/31] mypy --- guillotina/contrib/mcp/backend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guillotina/contrib/mcp/backend.py b/guillotina/contrib/mcp/backend.py index 8e12c2d25..750c9b911 100644 --- a/guillotina/contrib/mcp/backend.py +++ b/guillotina/contrib/mcp/backend.py @@ -47,7 +47,7 @@ def __init__(self, settings: Optional[Dict[str, Any]] = None): self._register_default_tools() self._register_default_resources() self._key_cache_redis_prefix = "mcp_tool_cache:v1" - self._queue_invalidations = asyncio.Queue(maxsize=1) + self._queue_invalidations: asyncio.Queue[str] = asyncio.Queue(maxsize=1) self._worker_task: asyncio.Task = None async def initialize(self, app): From 9c258bf267fe24560ec5cea7f465a9c8106f81c2 Mon Sep 17 00:00:00 2001 From: nilbacardit26 Date: Mon, 30 Mar 2026 18:40:35 +0200 Subject: [PATCH 31/31] passing tests --- CHANGELOG.rst | 4 ++++ guillotina/contrib/mcp/backend.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3668342d6..fca3beb22 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,9 @@ CHANGELOG 7.0.7 (unreleased) ------------------ +- REAKING CHANGE: Drop support for Python 3.8 and 3.9. Guillotina is + now tested and supported on Python 3.10, 3.11, and 3.12. + [nilbacardit26] - Add `guillotina.contrib.mcp` with low-level MCP server integration (`mcp.server.lowlevel`), tool registry utility, MCP services, cache invalidation subscribers, and tests/docs coverage. @@ -11,6 +14,7 @@ CHANGELOG fallback to `async_items` when catalog is unavailable. - Upgrade the pytest stack so the CI test environment stays compatible with the optional MCP SDK and its AnyIO pytest plugin on Python 3.10+. + [finalchaz, nilbacardit26] - Docs: Update documentation and configuration settings - Chore: Update sphinx-guillotina-theme version to 1.0.9 [rboixaderg] diff --git a/guillotina/contrib/mcp/backend.py b/guillotina/contrib/mcp/backend.py index 750c9b911..840c9be40 100644 --- a/guillotina/contrib/mcp/backend.py +++ b/guillotina/contrib/mcp/backend.py @@ -69,7 +69,7 @@ async def invalidate_cache_worker(self): return try: - await self._invalidate_cache(reason_cache) + await self.invalidate_cache(reason_cache) except Exception: logger.error("Error in invalidating cache", exc_info=True) await asyncio.sleep(0.5)