-
Notifications
You must be signed in to change notification settings - Fork 0
[#885] Scaffolding for external registers plugins #921
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #921 +/- ##
==========================================
- Coverage 90.28% 89.92% -0.36%
==========================================
Files 214 223 +9
Lines 6885 7068 +183
Branches 696 703 +7
==========================================
+ Hits 6216 6356 +140
- Misses 669 712 +43
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| <li>{{ error.message }}</li> | ||
| {% endfor %} | ||
| </ul> | ||
| <p>{% translate 'The configuration is complete.' %}</p> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A checklist with succeeded checks ✔️ shows that checks were performed.
If misconfigured, or configuration drift occurred (e.g. updates), and somehow no checks are collected or only a subset, then all checks pass, but health may not be okay.
Perhaps that's all impossible here, but the user doesn't know that.
Maybe we can have a default in the library
that uses `<details>` and `<summary>` tags to- not overwhelming with data
- still provide access to that data to help end-user, application manager, dev ops and 1st line support spot issues.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved the template tag to the library. I think it still needs some work, but it is functional for now.
...end/src/openarchiefbeheer/external_registers/contrib/openklant/setup_configuration/models.py
Show resolved
Hide resolved
backend/src/openarchiefbeheer/external_registers/contrib/openklant/plugin.py
Outdated
Show resolved
Hide resolved
| ) | ||
|
|
||
|
|
||
| register = Registry() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do need to bind the PluginT type var, or it will be a Registry[Unknown]
| register = Registry() | |
| register: Registry[AbstractBasePlugin] = Registry() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PS, to prevent the Unknown/Any judgement, you can set a default type if none is annotated:
class Registry[PluginT: AbstractBasePlugin = AbstractBasePlugin]:The syntax is GenericType[TypeVarName: UpperBoundType = DefaultType]. where GenericType can be a function, class or method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes! I did this in the end: Registry[AbstractBasePlugin[object]] because AbstractBasePlugin also has a generic type 😵💫
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm. If we remove the bound from Registry, you can reuse it for the health checks, and have a Registry[HealthCheckPlugin] of which the __iter__ method is a valid checks_collector.... Oh wait. The __call__ does more than just adding to the collection.
| def __call__(self, identifier: str) -> Callable[[type[PluginT]], type[PluginT]]: | ||
| def decorator(plugin_cls: type[PluginT]) -> type[PluginT]: | ||
| if identifier in self._registry: | ||
| raise ValueError( | ||
| f"The unique identifier '{identifier}' is already present " | ||
| "in the registry." | ||
| ) | ||
|
|
||
| self._registry[identifier] = plugin_cls(identifier=identifier) | ||
| return plugin_cls | ||
|
|
||
| return decorator |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We want to express that the decorator doesn't just return some type[PluginT] but the same that was passed in.
| def __call__(self, identifier: str) -> Callable[[type[PluginT]], type[PluginT]]: | |
| def decorator(plugin_cls: type[PluginT]) -> type[PluginT]: | |
| if identifier in self._registry: | |
| raise ValueError( | |
| f"The unique identifier '{identifier}' is already present " | |
| "in the registry." | |
| ) | |
| self._registry[identifier] = plugin_cls(identifier=identifier) | |
| return plugin_cls | |
| return decorator | |
| def __call__[T: type[PluginT]](self, identifier: str) -> Callable[[T], T]: | |
| def decorator(plugin_cls: T) -> T: | |
| if identifier in self._registry: | |
| raise ValueError( | |
| f"The unique identifier '{identifier}' is already present " | |
| "in the registry." | |
| ) | |
| self._registry[identifier] = plugin_cls(identifier=identifier) | |
| return plugin_cls | |
| return decorator |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I get that TypeVar constraint type cannot be generic (since pluginT is generic). I can't get it to be happy with [T: type[AbstractBasePlugin]] instead 😩
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed. We can drop the generic from Registry, and replace all occurrences of PluginT with AbstractBasePlugin. (or define it as a an alias type PluginT = AbastractBasePlugin for shorthand... or rename it to BasePlugin, as it is clear that it's an abstract from its inheritance from ABC. But that's a matter of taste.)
| from openarchiefbeheer.external_registers.plugin import AbstractBasePlugin | ||
|
|
||
|
|
||
| class Registry[PluginT: AbstractBasePlugin]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Discussed with Chris:
The [PluginT: AbstractBasePlugin] can be moved to the call function.
And instead of being generic it can be only AbstractBasePlugin in the other places
CharString
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Almost there. 🙂 👍
| def __call__(self, identifier: str) -> Callable[[type[PluginT]], type[PluginT]]: | ||
| def decorator(plugin_cls: type[PluginT]) -> type[PluginT]: | ||
| if identifier in self._registry: | ||
| raise ValueError( | ||
| f"The unique identifier '{identifier}' is already present " | ||
| "in the registry." | ||
| ) | ||
|
|
||
| self._registry[identifier] = plugin_cls(identifier=identifier) | ||
| return plugin_cls | ||
|
|
||
| return decorator |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed. We can drop the generic from Registry, and replace all occurrences of PluginT with AbstractBasePlugin. (or define it as a an alias type PluginT = AbastractBasePlugin for shorthand... or rename it to BasePlugin, as it is clear that it's an abstract from its inheritance from ABC. But that's a matter of taste.)
Fixes #885
Depends on maykinmedia/django-health-checks#1