feat: indie status page MVP -- FastAPI + SQLite
- 8 DB models (services, incidents, monitors, subscribers, etc.) - Full CRUD API for services, incidents, monitors - Public status page with live data - Incident detail page with timeline - API key authentication - Uptime monitoring scheduler - 13 tests passing - TECHNICAL_DESIGN.md with full spec
This commit is contained in:
commit
902133edd3
4655 changed files with 1342691 additions and 0 deletions
94
venv/lib/python3.11/site-packages/packaging/errors.py
Normal file
94
venv/lib/python3.11/site-packages/packaging/errors.py
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
import dataclasses
|
||||
import sys
|
||||
import typing
|
||||
|
||||
__all__ = ["ExceptionGroup"]
|
||||
|
||||
|
||||
def __dir__() -> list[str]:
|
||||
return __all__
|
||||
|
||||
|
||||
if sys.version_info >= (3, 11): # pragma: no cover
|
||||
from builtins import ExceptionGroup
|
||||
else: # pragma: no cover
|
||||
|
||||
class ExceptionGroup(Exception):
|
||||
"""A minimal implementation of :external:exc:`ExceptionGroup` from Python 3.11.
|
||||
|
||||
If :external:exc:`ExceptionGroup` is already defined by Python itself,
|
||||
that version is used instead.
|
||||
"""
|
||||
|
||||
message: str
|
||||
exceptions: list[Exception]
|
||||
|
||||
def __init__(self, message: str, exceptions: list[Exception]) -> None:
|
||||
self.message = message
|
||||
self.exceptions = exceptions
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"{self.__class__.__name__}({self.message!r}, {self.exceptions!r})"
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class _ErrorCollector:
|
||||
"""
|
||||
Collect errors into ExceptionGroups.
|
||||
|
||||
Used like this:
|
||||
|
||||
collector = _ErrorCollector()
|
||||
# Add a single exception
|
||||
collector.error(ValueError("one"))
|
||||
|
||||
# Supports nesting, including combining ExceptionGroups
|
||||
with collector.collect():
|
||||
raise ValueError("two")
|
||||
collector.finalize("Found some errors")
|
||||
|
||||
Since making a collector and then calling finalize later is a common pattern,
|
||||
a convenience method ``on_exit`` is provided.
|
||||
"""
|
||||
|
||||
errors: list[Exception] = dataclasses.field(default_factory=list, init=False)
|
||||
|
||||
def finalize(self, msg: str) -> None:
|
||||
"""Raise a group exception if there are any errors."""
|
||||
if self.errors:
|
||||
raise ExceptionGroup(msg, self.errors)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def on_exit(self, msg: str) -> typing.Generator[_ErrorCollector, None, None]:
|
||||
"""
|
||||
Calls finalize if no uncollected errors were present.
|
||||
|
||||
Uncollected errors are raised normally.
|
||||
"""
|
||||
yield self
|
||||
self.finalize(msg)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def collect(self, *err_cls: type[Exception]) -> typing.Generator[None, None, None]:
|
||||
"""
|
||||
Context manager to collect errors into the error list.
|
||||
|
||||
Must be inside loops, as only one error can be collected at a time.
|
||||
"""
|
||||
error_classes = err_cls or (Exception,)
|
||||
try:
|
||||
yield
|
||||
except ExceptionGroup as error:
|
||||
self.errors.extend(error.exceptions)
|
||||
except error_classes as error:
|
||||
self.errors.append(error)
|
||||
|
||||
def error(
|
||||
self,
|
||||
error: Exception,
|
||||
) -> None:
|
||||
"""Add an error to the list."""
|
||||
self.errors.append(error)
|
||||
Loading…
Add table
Add a link
Reference in a new issue