mesa-core 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
mesa_core/__init__.py ADDED
@@ -0,0 +1,76 @@
1
+ """mesa-core: reference implementation of the MESA specification."""
2
+
3
+ from mesa_core.conflict import ConflictResolver
4
+ from mesa_core.enforcer import ConfirmationManager, EnforcementResult, MesaEnforcer
5
+ from mesa_core.exceptions import (
6
+ InvalidCursorError,
7
+ MesaEnforcementError,
8
+ MesaError,
9
+ MesaValidationError,
10
+ )
11
+ from mesa_core.inheritance import InheritanceResolver, ProfileExplanation
12
+ from mesa_core.integration_import import import_from_integration
13
+ from mesa_core.migration import migrate_profile
14
+ from mesa_core.privacy import AccessDecision, CallerContext, PrivacyEnforcer
15
+ from mesa_core.profile import (
16
+ DOMAIN_SAFETY_BASELINE,
17
+ HELPER_DOMAINS,
18
+ ControlMode,
19
+ MetadataOrigin,
20
+ OperationalBoundaries,
21
+ PrivacyClassification,
22
+ PrivacyLevel,
23
+ ProfileMetadata,
24
+ SemanticProfile,
25
+ TriggersAutomations,
26
+ )
27
+ from mesa_core.store import DeploymentDefaults, ProfileQueryResult, ProfileStore, QueryRow
28
+ from mesa_core.temporal import TemporalEvaluator, TemporalResult
29
+ from mesa_core.trigger_validator import (
30
+ TriggerValidator,
31
+ ValidationIssue,
32
+ entities_by_role,
33
+ )
34
+ from mesa_core.validation import ValidationReport, validate_document, validate_or_raise
35
+
36
+ __version__ = "1.0.0"
37
+
38
+ __all__ = [
39
+ "DOMAIN_SAFETY_BASELINE",
40
+ "HELPER_DOMAINS",
41
+ "AccessDecision",
42
+ "CallerContext",
43
+ "ConfirmationManager",
44
+ "ConflictResolver",
45
+ "ControlMode",
46
+ "DeploymentDefaults",
47
+ "EnforcementResult",
48
+ "InheritanceResolver",
49
+ "InvalidCursorError",
50
+ "MesaEnforcementError",
51
+ "MesaEnforcer",
52
+ "MesaError",
53
+ "MesaValidationError",
54
+ "MetadataOrigin",
55
+ "OperationalBoundaries",
56
+ "PrivacyClassification",
57
+ "PrivacyEnforcer",
58
+ "PrivacyLevel",
59
+ "ProfileExplanation",
60
+ "ProfileMetadata",
61
+ "ProfileQueryResult",
62
+ "ProfileStore",
63
+ "QueryRow",
64
+ "SemanticProfile",
65
+ "TemporalEvaluator",
66
+ "TemporalResult",
67
+ "TriggerValidator",
68
+ "TriggersAutomations",
69
+ "ValidationIssue",
70
+ "ValidationReport",
71
+ "entities_by_role",
72
+ "import_from_integration",
73
+ "migrate_profile",
74
+ "validate_document",
75
+ "validate_or_raise",
76
+ ]
@@ -0,0 +1,29 @@
1
+ """Storage backends for ProfileStore (Module Proposal 4.3)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from abc import ABC, abstractmethod
6
+ from typing import Any
7
+
8
+
9
+ class StorageBackend(ABC):
10
+ """Abstract storage backend. Host servers may implement their own."""
11
+
12
+ @abstractmethod
13
+ def read(self, key: str) -> dict[str, Any] | None: ...
14
+
15
+ @abstractmethod
16
+ def write(self, key: str, data: dict[str, Any]) -> None: ...
17
+
18
+ @abstractmethod
19
+ def delete(self, key: str) -> None: ...
20
+
21
+ @abstractmethod
22
+ def list_keys(self, prefix: str | None = None) -> list[str]: ...
23
+
24
+
25
+ from mesa_core.backends.jsonfile import JsonFileBackend # noqa: E402
26
+ from mesa_core.backends.memory import MemoryBackend # noqa: E402
27
+ from mesa_core.backends.sqlite import SqliteBackend # noqa: E402
28
+
29
+ __all__ = ["JsonFileBackend", "MemoryBackend", "SqliteBackend", "StorageBackend"]
@@ -0,0 +1,43 @@
1
+ """JSON file storage backend: one file per profile, keyed by URL-quoted key.
2
+
3
+ Quoting (rather than lossy character replacement) keeps the filename-to-key
4
+ mapping reversible, so ``list_keys`` can reconstruct keys exactly.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ from pathlib import Path
11
+ from typing import Any
12
+ from urllib.parse import quote, unquote
13
+
14
+ from mesa_core import backends
15
+
16
+
17
+ class JsonFileBackend(backends.StorageBackend):
18
+ def __init__(self, base_path: str | Path, create_if_missing: bool = True) -> None:
19
+ self.base_path = Path(base_path)
20
+ if create_if_missing:
21
+ self.base_path.mkdir(parents=True, exist_ok=True)
22
+
23
+ def _path(self, key: str) -> Path:
24
+ return self.base_path / f"{quote(key, safe='')}.json"
25
+
26
+ def read(self, key: str) -> dict[str, Any] | None:
27
+ path = self._path(key)
28
+ if not path.exists():
29
+ return None
30
+ data: dict[str, Any] = json.loads(path.read_text())
31
+ return data
32
+
33
+ def write(self, key: str, data: dict[str, Any]) -> None:
34
+ self._path(key).write_text(json.dumps(data, indent=2) + "\n")
35
+
36
+ def delete(self, key: str) -> None:
37
+ self._path(key).unlink(missing_ok=True)
38
+
39
+ def list_keys(self, prefix: str | None = None) -> list[str]:
40
+ keys = sorted(unquote(p.stem) for p in self.base_path.glob("*.json"))
41
+ if prefix is not None:
42
+ keys = [k for k in keys if k.startswith(prefix)]
43
+ return keys
@@ -0,0 +1,29 @@
1
+ """In-memory storage backend. Not persistent; for testing and development."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import copy
6
+ from typing import Any
7
+
8
+ from mesa_core import backends
9
+
10
+
11
+ class MemoryBackend(backends.StorageBackend):
12
+ def __init__(self, initial_data: dict[str, dict[str, Any]] | None = None) -> None:
13
+ self._data: dict[str, dict[str, Any]] = copy.deepcopy(initial_data or {})
14
+
15
+ def read(self, key: str) -> dict[str, Any] | None:
16
+ value = self._data.get(key)
17
+ return copy.deepcopy(value) if value is not None else None
18
+
19
+ def write(self, key: str, data: dict[str, Any]) -> None:
20
+ self._data[key] = copy.deepcopy(data)
21
+
22
+ def delete(self, key: str) -> None:
23
+ self._data.pop(key, None)
24
+
25
+ def list_keys(self, prefix: str | None = None) -> list[str]:
26
+ keys = sorted(self._data)
27
+ if prefix is not None:
28
+ keys = [k for k in keys if k.startswith(prefix)]
29
+ return keys
@@ -0,0 +1,57 @@
1
+ """SQLite storage backend using the standard library.
2
+
3
+ Suitable for large deployments. Async access goes through the ProfileStore's
4
+ ``a``-prefixed methods, which offload to a thread.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ import sqlite3
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+ from mesa_core import backends
15
+
16
+
17
+ class SqliteBackend(backends.StorageBackend):
18
+ def __init__(self, db_path: str | Path) -> None:
19
+ self.db_path = str(db_path)
20
+ with self._connect() as conn:
21
+ conn.execute(
22
+ "CREATE TABLE IF NOT EXISTS profiles (key TEXT PRIMARY KEY, data TEXT NOT NULL)"
23
+ )
24
+
25
+ def _connect(self) -> sqlite3.Connection:
26
+ return sqlite3.connect(self.db_path)
27
+
28
+ def read(self, key: str) -> dict[str, Any] | None:
29
+ with self._connect() as conn:
30
+ row = conn.execute("SELECT data FROM profiles WHERE key = ?", (key,)).fetchone()
31
+ if row is None:
32
+ return None
33
+ data: dict[str, Any] = json.loads(row[0])
34
+ return data
35
+
36
+ def write(self, key: str, data: dict[str, Any]) -> None:
37
+ with self._connect() as conn:
38
+ conn.execute(
39
+ "INSERT INTO profiles (key, data) VALUES (?, ?) "
40
+ "ON CONFLICT(key) DO UPDATE SET data = excluded.data",
41
+ (key, json.dumps(data)),
42
+ )
43
+
44
+ def delete(self, key: str) -> None:
45
+ with self._connect() as conn:
46
+ conn.execute("DELETE FROM profiles WHERE key = ?", (key,))
47
+
48
+ def list_keys(self, prefix: str | None = None) -> list[str]:
49
+ query = "SELECT key FROM profiles"
50
+ params: tuple[Any, ...] = ()
51
+ if prefix is not None:
52
+ query += " WHERE key LIKE ?"
53
+ params = (prefix.replace("%", r"\%").replace("_", r"\_") + "%",)
54
+ query += r" ESCAPE '\'"
55
+ query += " ORDER BY key"
56
+ with self._connect() as conn:
57
+ return [row[0] for row in conn.execute(query, params)]