chore: rename project coven → hack-house ⛧
Rebrand the Rust client crate (coven/ → hh/, package+binary "hack-house"), README, CLI strings, and branch (coven → hack-house). Gitea repo renamed cmd-chat → hack-house to match. Crypto/server logic unchanged; selftest + golden-vector test still green, binary is now `hack-house`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
from sanic import Request
|
||||
from sanic.exceptions import SanicException
|
||||
|
||||
|
||||
def extract_request(*args) -> Request:
|
||||
request: Request
|
||||
if args and isinstance(args[0], Request):
|
||||
request = args[0]
|
||||
elif len(args) > 1:
|
||||
request = args[1]
|
||||
else:
|
||||
raise SanicException("Request could not be found")
|
||||
return request
|
||||
@@ -0,0 +1,124 @@
|
||||
import re
|
||||
|
||||
|
||||
def clean_route_name(name: str) -> str:
|
||||
parts = name.split(".", 1)
|
||||
name = parts[-1]
|
||||
for target in ("_", ".", " "):
|
||||
name = name.replace(target, " ")
|
||||
|
||||
return name.title()
|
||||
|
||||
|
||||
def get_uri_filter(app):
|
||||
"""
|
||||
Return a filter function that takes a URI and returns whether it should
|
||||
be filter out from the swagger documentation or not.
|
||||
|
||||
Arguments:
|
||||
app: The application to take `config.API_URI_FILTER` from. Possible
|
||||
values for this config option are: `slash` (to keep URIs that
|
||||
end with a `/`), `all` (to keep all URIs). All other values
|
||||
default to keep all URIs that don't end with a `/`.
|
||||
|
||||
Returns:
|
||||
`True` if the URI should be *filtered out* from the swagger
|
||||
documentation, and `False` if it should be kept in the documentation.
|
||||
"""
|
||||
choice = getattr(app.config, "API_URI_FILTER", None)
|
||||
|
||||
if choice == "slash":
|
||||
# Keep URIs that end with a /.
|
||||
return lambda uri: not uri.endswith("/")
|
||||
|
||||
if choice == "all":
|
||||
# Keep all URIs.
|
||||
return lambda uri: False
|
||||
|
||||
# Keep URIs that don't end with a /, (special case: "/").
|
||||
return lambda uri: len(uri) > 1 and uri.endswith("/")
|
||||
|
||||
|
||||
def remove_nulls(dictionary, deep=True):
|
||||
"""
|
||||
Removes all null values from a dictionary.
|
||||
"""
|
||||
return {
|
||||
k: remove_nulls(v, deep) if deep and isinstance(v, dict) else v
|
||||
for k, v in dictionary.items()
|
||||
if v is not None
|
||||
}
|
||||
|
||||
|
||||
def remove_nulls_from_kwargs(**kwargs):
|
||||
return remove_nulls(kwargs, deep=False)
|
||||
|
||||
|
||||
def get_blueprinted_routes(app):
|
||||
for blueprint in app.blueprints.values():
|
||||
if not hasattr(blueprint, "routes"):
|
||||
continue
|
||||
|
||||
for route in blueprint.routes:
|
||||
if hasattr(route.handler, "view_class"):
|
||||
# before sanic 21.3, route.handler could be a number of
|
||||
# different things, so have to type check
|
||||
for http_method in route.methods:
|
||||
_handler = getattr(
|
||||
route.handler.view_class, http_method.lower(), None
|
||||
)
|
||||
if _handler:
|
||||
yield (blueprint.name, _handler)
|
||||
else:
|
||||
yield (blueprint.name, route.handler)
|
||||
|
||||
|
||||
def get_all_routes(app, skip_prefix):
|
||||
uri_filter = get_uri_filter(app)
|
||||
|
||||
for group in app.router.groups.values():
|
||||
uri = f"/{group.path}"
|
||||
|
||||
# prior to sanic 21.3 routes came in both forms
|
||||
# (e.g. /test and /test/ )
|
||||
# after sanic 21.3 routes come in one form,
|
||||
# with an attribute "strict",
|
||||
# so we simulate that ourselves:
|
||||
|
||||
uris = [uri]
|
||||
if not group.strict and len(uri) > 1:
|
||||
alt = uri[:-1] if uri.endswith("/") else f"{uri}/"
|
||||
uris.append(alt)
|
||||
|
||||
for uri in uris:
|
||||
if uri_filter(uri):
|
||||
continue
|
||||
|
||||
if skip_prefix and group.raw_path.startswith(
|
||||
skip_prefix.lstrip("/")
|
||||
):
|
||||
continue
|
||||
|
||||
for parameter in group.params.values():
|
||||
uri = re.sub(
|
||||
f"<{parameter.name}.*?>",
|
||||
f"{{{parameter.name}}}",
|
||||
uri,
|
||||
)
|
||||
|
||||
for route in group:
|
||||
if getattr(route.extra, "static", False):
|
||||
continue
|
||||
|
||||
method_handlers = [
|
||||
(method, route.handler) for method in route.methods
|
||||
]
|
||||
|
||||
_, name = route.name.split(".", 1)
|
||||
yield (
|
||||
uri,
|
||||
name,
|
||||
route.params.values(),
|
||||
method_handlers,
|
||||
route.requirements.get("host"),
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
import re
|
||||
|
||||
|
||||
CAMEL_TO_SNAKE_PATTERNS = (
|
||||
re.compile(r"(.)([A-Z][a-z]+)"),
|
||||
re.compile(r"([a-z0-9])([A-Z])"),
|
||||
)
|
||||
|
||||
|
||||
def camel_to_snake(name: str) -> str:
|
||||
for pattern in CAMEL_TO_SNAKE_PATTERNS:
|
||||
name = pattern.sub(r"\1_\2", name)
|
||||
return name.lower()
|
||||
@@ -0,0 +1,79 @@
|
||||
import types
|
||||
import typing
|
||||
|
||||
from inspect import isclass
|
||||
|
||||
|
||||
try:
|
||||
UnionType = types.UnionType # type: ignore
|
||||
except AttributeError:
|
||||
UnionType = type("UnionType", (), {}) # type: ignore
|
||||
|
||||
try:
|
||||
from pydantic import BaseModel
|
||||
|
||||
PYDANTIC = True
|
||||
except ImportError:
|
||||
PYDANTIC = False
|
||||
|
||||
try:
|
||||
import attrs # noqa
|
||||
|
||||
ATTRS = True
|
||||
except ImportError:
|
||||
ATTRS = False
|
||||
|
||||
try:
|
||||
from msgspec import Struct
|
||||
|
||||
MSGSPEC = True
|
||||
except ImportError:
|
||||
MSGSPEC = False
|
||||
|
||||
|
||||
def is_generic(item):
|
||||
return (
|
||||
isinstance(item, typing._GenericAlias)
|
||||
or isinstance(item, UnionType)
|
||||
or hasattr(item, "__origin__")
|
||||
)
|
||||
|
||||
|
||||
def is_optional(item):
|
||||
if is_generic(item):
|
||||
args = typing.get_args(item)
|
||||
return len(args) == 2 and type(None) in args
|
||||
return False
|
||||
|
||||
|
||||
def is_pydantic(model):
|
||||
return PYDANTIC and (
|
||||
issubclass(model, BaseModel) or hasattr(model, "__pydantic_model__")
|
||||
)
|
||||
|
||||
|
||||
def is_attrs(model):
|
||||
return ATTRS and (hasattr(model, "__attrs_attrs__"))
|
||||
|
||||
|
||||
def is_msgspec(model):
|
||||
return MSGSPEC and issubclass(model, Struct)
|
||||
|
||||
|
||||
def flat_values(
|
||||
item: typing.Union[dict[str, typing.Any], typing.Iterable[typing.Any]],
|
||||
) -> set[typing.Any]:
|
||||
values = set()
|
||||
if isinstance(item, dict):
|
||||
item = item.values()
|
||||
for value in item:
|
||||
if isinstance(value, dict) or isinstance(value, list):
|
||||
values.update(flat_values(value))
|
||||
else:
|
||||
values.add(value)
|
||||
return values
|
||||
|
||||
|
||||
def contains_annotations(d: dict[str, typing.Any]) -> bool:
|
||||
values = flat_values(d)
|
||||
return any(isclass(q) or is_generic(q) for q in values)
|
||||
@@ -0,0 +1,44 @@
|
||||
import re
|
||||
|
||||
|
||||
# Expression from https://github.com/pypa/packaging
|
||||
VERSION_PATTERN = r"""
|
||||
v?
|
||||
(?:
|
||||
(?:(?P<epoch>[0-9]+)!)? # epoch
|
||||
(?P<release>[0-9]+(?:\.[0-9]+)*) # release segment
|
||||
(?P<pre> # pre-release
|
||||
[-_\.]?
|
||||
(?P<pre_l>(a|b|c|rc|alpha|beta|pre|preview))
|
||||
[-_\.]?
|
||||
(?P<pre_n>[0-9]+)?
|
||||
)?
|
||||
(?P<post> # post release
|
||||
(?:-(?P<post_n1>[0-9]+))
|
||||
|
|
||||
(?:
|
||||
[-_\.]?
|
||||
(?P<post_l>post|rev|r)
|
||||
[-_\.]?
|
||||
(?P<post_n2>[0-9]+)?
|
||||
)
|
||||
)?
|
||||
(?P<dev> # dev release
|
||||
[-_\.]?
|
||||
(?P<dev_l>dev)
|
||||
[-_\.]?
|
||||
(?P<dev_n>[0-9]+)?
|
||||
)?
|
||||
)
|
||||
(?:\+(?P<local>[a-z0-9]+(?:[-_\.][a-z0-9]+)*))? # local version
|
||||
"""
|
||||
PATTERN = re.compile(
|
||||
r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE
|
||||
)
|
||||
|
||||
|
||||
def get_version(version) -> tuple[int, ...]:
|
||||
match = PATTERN.search(version)
|
||||
if not match:
|
||||
raise ValueError(f"Invalid version: {version}")
|
||||
return tuple(map(int, match.group("release").split(".")))
|
||||
Reference in New Issue
Block a user