-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- the `rats.devtools` package no longer has commands in it, but has libraries for creating commands - existing commands were moved to more specific sub-packages - rats.ci - rats.docs - rats.pycharm - i removed the existing `@command` decorator and went back to the vanilla `@click.command()` decorator - the current api is verbose but i should be able to re-implement the simpler decorators in another pr - started a general purpose `rats.annotations` package for non-container specific object annotations - added support for cli sub-commands, with tab-completion support, and lazy-loading of command groups - fixed and re-enabled api docs generation for `rats-apps` - beginnings of a command_tree package --------- Co-authored-by: Andrew FigPope <[email protected]>
- Loading branch information
Showing
71 changed files
with
2,441 additions
and
485 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
""" | ||
General purpose library to attach annotations to functions. | ||
Annotations are typically, but not exclusively, attached using decorators. | ||
""" | ||
|
||
from ._functions import ( | ||
AnnotationsContainer, | ||
DecoratorType, | ||
GroupAnnotations, | ||
T_GroupType, | ||
annotation, | ||
get_annotations, | ||
get_class_annotations, | ||
) | ||
|
||
__all__ = [ | ||
"annotation", | ||
"DecoratorType", | ||
"AnnotationsContainer", | ||
"get_annotations", | ||
"get_class_annotations", | ||
"GroupAnnotations", | ||
"T_GroupType", | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
# type: ignore | ||
from __future__ import annotations | ||
|
||
from collections import defaultdict | ||
from collections.abc import Callable | ||
from functools import cache | ||
from typing import Any, Generic, ParamSpec, TypeVar | ||
from typing import NamedTuple as tNamedTuple | ||
|
||
from typing_extensions import NamedTuple | ||
|
||
T_GroupType = TypeVar("T_GroupType", bound=NamedTuple) | ||
|
||
|
||
class GroupAnnotations(NamedTuple, Generic[T_GroupType]): | ||
"""The list of T_GroupType objects identified by a given name in a namespace.""" | ||
|
||
name: str | ||
"""The name of the annotation, typically the name of the function in a class""" | ||
namespace: str | ||
"""All the groups in a namespace are expected to be of the same T_GroupType.""" | ||
groups: tuple[T_GroupType, ...] | ||
|
||
|
||
class AnnotationsContainer(NamedTuple): | ||
""" | ||
Holds metadata about the annotated service provider. | ||
Loosely inspired by: https://peps.python.org/pep-3107/. | ||
""" | ||
|
||
annotations: tuple[GroupAnnotations[...], ...] | ||
|
||
@staticmethod | ||
def empty() -> AnnotationsContainer: | ||
return AnnotationsContainer(annotations=()) | ||
|
||
def with_group( | ||
self, | ||
namespace: str, | ||
group_id: T_GroupType, | ||
) -> AnnotationsContainer: | ||
return AnnotationsContainer( | ||
annotations=tuple( | ||
[ | ||
annotation_group | ||
for x in self.with_namespace(namespace) | ||
for annotation_group in x | ||
if group_id in annotation_group.groups | ||
] | ||
), | ||
) | ||
|
||
def with_namespace( | ||
self, | ||
namespace: str, | ||
) -> AnnotationsContainer: | ||
return AnnotationsContainer( | ||
annotations=tuple([x for x in self.annotations if x.namespace == namespace]), | ||
) | ||
|
||
|
||
class AnnotationsBuilder: | ||
_group_ids: dict[str, set[Any]] | ||
|
||
def __init__(self) -> None: | ||
self._group_ids = defaultdict(set) | ||
|
||
def add(self, namespace: str, group_id: NamedTuple | tNamedTuple) -> None: | ||
self._group_ids[namespace].add(group_id) | ||
|
||
def make(self, name: str) -> AnnotationsContainer: | ||
return AnnotationsContainer( | ||
annotations=tuple( | ||
[ | ||
GroupAnnotations[Any](name=name, namespace=namespace, groups=tuple(groups)) | ||
for namespace, groups in self._group_ids.items() | ||
] | ||
), | ||
) | ||
|
||
|
||
DecoratorType = TypeVar("DecoratorType", bound=Callable[..., Any]) | ||
|
||
|
||
def annotation( | ||
namespace: str, | ||
group_id: NamedTuple | tNamedTuple, | ||
) -> Callable[[DecoratorType], DecoratorType]: | ||
def decorator(fn: DecoratorType) -> DecoratorType: | ||
if not hasattr(fn, "__rats_annotations__"): | ||
fn.__rats_annotations__ = AnnotationsBuilder() # type: ignore[reportFunctionMemberAccess] | ||
|
||
fn.__rats_annotations__.add(namespace, group_id) # type: ignore[reportFunctionMemberAccess] | ||
|
||
return fn | ||
|
||
return decorator | ||
|
||
|
||
@cache | ||
def get_class_annotations(cls: type) -> AnnotationsContainer: | ||
tates = [] | ||
|
||
for method_name in dir(cls): | ||
method = getattr(cls, method_name) | ||
if not hasattr(method, "__rats_annotations__"): | ||
continue | ||
|
||
tates.extend(method.__rats_annotations__.make(method_name).annotations) | ||
|
||
return AnnotationsContainer(annotations=tuple(tates)) | ||
|
||
|
||
P = ParamSpec("P") | ||
|
||
|
||
def get_annotations(fn: Callable[..., Any]) -> AnnotationsContainer: | ||
builder: AnnotationsBuilder = getattr( | ||
fn, | ||
"__rats_annotations__", | ||
AnnotationsBuilder(), | ||
) | ||
|
||
return builder.make(fn.__name__) |
Empty file.
Oops, something went wrong.