Skip to content
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

_Result is unnecessary? #260

Closed
bluetech opened this issue Jun 2, 2020 · 9 comments
Closed

_Result is unnecessary? #260

bluetech opened this issue Jun 2, 2020 · 9 comments

Comments

@bluetech
Copy link
Member

bluetech commented Jun 2, 2020

In #257, @maxnikulin brought up an idea which seems very natural but hasn't occurred to me beforehand. I think it will at least be interesting to discuss it on its own.

Background

_Result is used in hookwrappers as follows (using imaginary type annotations for clarity):

from typing import Generator, TypeVar

T = TypeVar("T")
E = TypeVar("E")

@hookimpl(hookwrapper=True)
def my_hook() -> Generator[None, _Result[T, E], None]:
    # ... Some setup code ...

    result = yield

    try:
        value = result.get_result()
    except Exception as exc:
        # ... Use exc ...
    else:
        # ... Use value ...

    # ... Some teardown code ...

The result has three use cases:

  1. Fetch the return value[s] of the hook, if it didn't raise.
  2. Fetch the exception of the hook, if it did raise.
  3. Override (force) the return value/exception to some other return value.

Idea

Python coroutines already provide a way to distinguish between a successful yield (-> coroutine.send(value)) and a throwing yield (-> coroutine.throw(exc)). So in the above, the _Result indirection could have been dispensed with:

@hookimpl(imaginary_hookwrapper=True)
def my_hook() -> Generator[None, T, None]:
    # ... Some setup code ...

    try:
        value = yield
    except Exception as exc:
        # ... Use exc ...
    else:
        # ... Use value ...

    # ... Some teardown code ...

This handles use cases 1 & 2. Regarding use case 3 (forcing a result), use the coroutine return value:

@hookimpl(imaginary_hookwrapper=True)
def my_hook() -> Generator[None, T, T]:
    # ... Some setup code ...

    try:
        value = yield
    except Exception as exc:
        # ... Use exc ...
    else:
        # ... Use value ...

    # ... Some teardown code ...

    return new_value
@RonnyPfannschmidt
Copy link
Member

RonnyPfannschmidt commented Jun 3, 2020

can we call the keyword argument result_processor, mutually exclusive with hookwrapper

an additional win is that we can now use raise new from old nicely

@oremanj
Copy link

oremanj commented Jun 3, 2020

It would be possible to handle this backwards-compatibly by encouraging hookwrappers to say value = (yield).get_result(). pluggy would still construct a _Result object for backcompat reasons, but it would immediately get unwrapped by newer hooks.

Then let the return value of the hookwrapper, or the exception it raises, be used to determine the result for the next layer up. Old code that uses force_result explicitly can still be supported.

@RonnyPfannschmidt
Copy link
Member

@oremanj i dont quite follow, the new hook wrapping mechanism would not use result objects, the old hook wrapping mechanism would not get the new features

@oremanj
Copy link

oremanj commented Jun 3, 2020

If you have two mechanisms, you don't need my suggestion. If you want to stick with only one mechanism, my suggestion is a way to make it more pleasant to code against.

@maxnikulin
Copy link
Contributor

Do not take too serious the following idea.

Even hookwrapper argument could be dropped. If a hook is a generator than it is a wrapper. But with such approach reading of the code requires more attention.

More seriously. Mutually exclusive keyword argument could be enforced by interpreter. It would be a source of confusion that both arguments could not be used simultaneously. Maybe other value of hookwrapper would be better (but likely still verbose enough).

@hookimpl(hookwrapper=pluggy.no_boxing)

@RonnyPfannschmidt
Copy link
Member

i see where you want to go with that idea

in case we want to sort out the different hook control mechanisms, we might want to decompose controll types and ordering types from the current invocation,

as the arguments to hookimpl are bascially just a bag of flags, its entirely reasonable to have mutually exclusive parts, however i beleive a enerally better spelling is not done by special values/enums, but by distinct markers for the different controll flows

for example hookimpl could deprecate hookwrapper as argument alltogether,
and the next gen wrapping would be called something like hookcontroller (good names to be figured later)

as for hook ordering, i would like to see some better control over order relative to other plugins

as for invocation patterns,

it might be cool to have

@hook.spec
def my_hook(...): ...

@hook.control
def my_hook(...):
   try:
      yield
   finally:
      ...

@hook.impl
def my_hook(...):
   if ...:
      return ...

@bluetech
Copy link
Member Author

Even hookwrapper argument could be dropped. If a hook is a generator than it is a wrapper.

This is another obvious-in-retrospect idea. We should summon you more :)

I think it's an interesting avenue to explore, in combination with dropping/deprecating _Result. When I get a chance, I'll try to approach it like this:

  1. Remove hookwrapper and _Result (to start with).
  2. Allow the hookimpl function to be a generator function (detected with inspect.isgeneratorfunction()).
  3. If the function is a generator function, it has hookwrapper-like semantics, but as described in this issue, without _Result.
  4. Add back hookwrapper/_Result support, for backward compatibility.
  5. Favor the new style in the documentation.
  6. Eventually consider deprecating hookwrapper (very disruptive so not for a few years at least).

Hopefully an actual implementation wouldn't poke any holes in this idea...


But with such approach reading of the code requires more attention.

I assume you're referring to reading the hookimpl code, that if we drop the explicit hookwrapper=True, it will be less obvious in a glance whether whether it is a wrapper or a regular impl.

I agree the explicit indication is nice, actually I think Python would have been better if it required something like gen def foo() for generator functions, like async def (and JavaScript function* syntax), but they didn't...

However:

  1. This is a choice the Python devs made.
  2. If type annotations are used, it will be typed as Generator[...], which serves as an indicator.

But if we want the explicitness anyway, we can add a new mutually exclusive boolean (my preference would be just wrapper=True).

@maxnikulin
Copy link
Contributor

I suggested the check if some hook is a generator expecting that such proposal would be considered and conciosly declined.

I admit the argument concerning language design decision. Though I am not brave enough to prefer brevity here. Tests are special kind of code. Most of people do not like writing them or could not spend enough time for them, so quality of the code is lower than in the main part of a project. It is applicable even to hooks that may be a part of local plugins. As the number of tests grows at some certain amount the heap of tests become hardly manageable. Explicitly expressed intentions (even if language allows to omit some details) become invaluable during debugging of obscure failures.

"[hook]wrapper" is a hint related to the purpose, Generator type annotation (or gen def) describes implementation details. The former variant is more friendly to people who rarely
touche tests or testing framework plugins but sometimes have to deal with test failures.

Please, consider validation at startup phase in addition to errors during attempts to run tests, while adding mutually exclusive options (keyword arguments or old style keyword argument vs. implicit detection). In the ideal case, init time errors should be collected and inconsistencies in all hooks should be reported in a single run without execution of any tests. Such approach should be more informative in comparison to immediate exit on first error in the case of tests running by a CI system. It is convenient but not so important for interactive sessions.

Do you have an idea which keyword could be used when it would be decided to make "wrapper" obsolete? Short expressive terms make next choice harder.

@RonnyPfannschmidt
Copy link
Member

i think this is similar to how we first had yield_fixture in pytest as separate tool and later merged the behaviour back into fixture

bluetech added a commit to bluetech/pluggy that referenced this issue Jun 11, 2023
bluetech added a commit to bluetech/pluggy that referenced this issue Jun 12, 2023
bluetech added a commit to bluetech/pluggy that referenced this issue Jun 12, 2023
bluetech added a commit to bluetech/pluggy that referenced this issue Jun 12, 2023
bluetech added a commit to bluetech/pluggy that referenced this issue Jun 13, 2023
bluetech added a commit to bluetech/pluggy that referenced this issue Jun 17, 2023
Fix pytest-dev#260.

Co-authored-by: Maxim Nikulin <manikulin@gmail.com>
a10y pushed a commit to IntrinsicLabsAI/intrinsic-model-server that referenced this issue Jun 27, 2023
Bumps [pluggy](https://github.com/pytest-dev/pluggy) from 1.0.0 to
1.2.0.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/pytest-dev/pluggy/blob/main/CHANGELOG.rst">pluggy's
changelog</a>.</em></p>
<blockquote>
<h1>pluggy 1.2.0 (2023-06-21)</h1>
<h2>Features</h2>
<ul>
<li><code>[#405](pytest-dev/pluggy#405)
&lt;https://github.com/pytest-dev/pluggy/issues/405&gt;</code>_: The
new-style hook wrappers, added in the yanked 1.1.0 release, now require
an explicit <code>wrapper=True</code> designation in the
<code>@hookimpl()</code> decorator.</li>
</ul>
<h1>pluggy 1.1.0 (YANKED)</h1>
<p>.. note::</p>
<p>This release was yanked because unfortunately the implicit new-style
hook wrappers broke some downstream projects.
See <code>[#403](pytest-dev/pluggy#403)
&lt;https://github.com/pytest-dev/pluggy/issues/403&gt;</code>__ for
more information.
This was rectified in the 1.2.0 release.</p>
<h2>Deprecations and Removals</h2>
<ul>
<li><code>[#364](pytest-dev/pluggy#364)
&lt;https://github.com/pytest-dev/pluggy/issues/364&gt;</code>_: Python
3.6 is no longer supported.</li>
</ul>
<h2>Features</h2>
<ul>
<li>
<p><code>[#260](pytest-dev/pluggy#260)
&lt;https://github.com/pytest-dev/pluggy/issues/260&gt;</code>_: Added
&quot;new-style&quot; hook wrappers, a simpler but equally powerful
alternative to the existing <code>hookwrapper=True</code> wrappers.</p>
<p>New-style wrappers are generator functions, similarly to
<code>hookwrapper</code>, but do away with the :class:<code>result
&lt;pluggy._callers._Result&gt;</code> object.
Instead, the return value is sent directly to the <code>yield</code>
statement, or, if inner calls raised an exception, it is raised from the
<code>yield</code>.
The wrapper is expected to return a value or raise an exception, which
will become the result of the hook call.</p>
<p>New-style wrappers are fully interoperable with old-style wrappers.
We encourage users to use the new style, however we do not intend to
deprecate the old style any time soon.</p>
<p>See :ref:<code>hookwrappers</code> for the full documentation.</p>
</li>
<li>
<p><code>[#364](pytest-dev/pluggy#364)
&lt;https://github.com/pytest-dev/pluggy/issues/364&gt;</code>_: Python
3.11 and 3.12 are now officially supported.</p>
</li>
<li>
<p><code>[#394](pytest-dev/pluggy#394)
&lt;https://github.com/pytest-dev/pluggy/issues/394&gt;</code>_: Added
the :meth:<code>~pluggy._callers._Result.force_exception</code> method
to <code>_Result</code>.</p>
<p><code>force_exception</code> allows (old-style) hookwrappers to force
an exception or override/adjust an existing exception of a hook
invocation,
in a properly behaving manner. Using <code>force_exception</code> is
preferred over raising an exception from the hookwrapper,
because raising an exception causes other hookwrappers to be
skipped.</p>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/pytest-dev/pluggy/commit/9060a4e466a8ef08bd737dd75acf1e976b76dc07"><code>9060a4e</code></a>
Preparing release 1.2.0</li>
<li><a
href="https://github.com/pytest-dev/pluggy/commit/926084e262441a3d2ab30ae7aefa463377f4c119"><code>926084e</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pluggy/issues/379">#379</a>
from vitek/spec-conflict</li>
<li><a
href="https://github.com/pytest-dev/pluggy/commit/8103269dac8773bf7702d1494127383bbbe4cfc4"><code>8103269</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pluggy/issues/411">#411</a>
from bluetech/new-style-v2</li>
<li><a
href="https://github.com/pytest-dev/pluggy/commit/3f9f62275d39679638aa2c9b3b9cc02665e9cdfb"><code>3f9f622</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pluggy/issues/408">#408</a>
from bluetech/releasing-pre-test</li>
<li><a
href="https://github.com/pytest-dev/pluggy/commit/e241aed3efdde28e6862386a06d4faf840165650"><code>e241aed</code></a>
Make new-style wrappers use explicit <code>wrapper=True</code></li>
<li><a
href="https://github.com/pytest-dev/pluggy/commit/165c4a71a2d528596d055d82cd1e5a78389dd1c6"><code>165c4a7</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pluggy/issues/410">#410</a>
from bluetech/rm-subset-slots</li>
<li><a
href="https://github.com/pytest-dev/pluggy/commit/fea9daa582c1855c74ef7d4cd5fd923ed2ed8e25"><code>fea9daa</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pluggy/issues/409">#409</a>
from bluetech/pm-no-slots</li>
<li><a
href="https://github.com/pytest-dev/pluggy/commit/b56b4c3a7e9cdcd8b94709f805a2865a08cc6218"><code>b56b4c3</code></a>
hooks: remove a couple of unnecessary slots from
<code>_SubsetHookCaller</code></li>
<li><a
href="https://github.com/pytest-dev/pluggy/commit/2e2a0408cb32563ee0bd610fe414045a39043873"><code>2e2a040</code></a>
Add scripts for downstream testing</li>
<li><a
href="https://github.com/pytest-dev/pluggy/commit/4cfc67669a17b9c94abad2e035526ef2c1a1f4db"><code>4cfc676</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pluggy/issues/407">#407</a>
from pytest-dev/pre-commit-ci-update-config</li>
<li>Additional commits viewable in <a
href="https://github.com/pytest-dev/pluggy/compare/1.0.0...1.2.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=pluggy&package-manager=pip&previous-version=1.0.0&new-version=1.2.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
netbsd-srcmastr pushed a commit to NetBSD/pkgsrc that referenced this issue Jun 28, 2023
pluggy 1.2.0 (2023-06-21)
=========================

Features
--------

- `#405 <https://github.com/pytest-dev/pluggy/issues/405>`_: The new-style hook wrappers, added in the yanked 1.1.0 release, now require an explicit ``wrapper=True`` designation in the ``@hookimpl()`` decorator.


pluggy 1.1.0 (YANKED)
=====================

.. note::

  This release was yanked because unfortunately the implicit new-style hook wrappers broke some downstream projects.
  See `#403 <https://github.com/pytest-dev/pluggy/issues/403>`__ for more information.
  This was rectified in the 1.2.0 release.

Deprecations and Removals
-------------------------

- `#364 <https://github.com/pytest-dev/pluggy/issues/364>`_: Python 3.6 is no longer supported.



Features
--------

- `#260 <https://github.com/pytest-dev/pluggy/issues/260>`_: Added "new-style" hook wrappers, a simpler but equally powerful alternative to the existing ``hookwrapper=True`` wrappers.

  New-style wrappers are generator functions, similarly to ``hookwrapper``, but do away with the :class:`result <pluggy._callers._Result>` object.
  Instead, the return value is sent directly to the ``yield`` statement, or, if inner calls raised an exception, it is raised from the ``yield``.
  The wrapper is expected to return a value or raise an exception, which will become the result of the hook call.

  New-style wrappers are fully interoperable with old-style wrappers.
  We encourage users to use the new style, however we do not intend to deprecate the old style any time soon.

  See :ref:`hookwrappers` for the full documentation.


- `#364 <https://github.com/pytest-dev/pluggy/issues/364>`_: Python 3.11 and 3.12 are now officially supported.


- `#394 <https://github.com/pytest-dev/pluggy/issues/394>`_: Added the :meth:`~pluggy._callers._Result.force_exception` method to ``_Result``.

  ``force_exception`` allows (old-style) hookwrappers to force an exception or override/adjust an existing exception of a hook invocation,
  in a properly behaving manner. Using ``force_exception`` is preferred over raising an exception from the hookwrapper,
  because raising an exception causes other hookwrappers to be skipped.
github-actions bot added a commit to MaRDI4NFDI/open-interfaces that referenced this issue Sep 1, 2023
Bumps [pluggy](https://github.com/pytest-dev/pluggy) from 1.0.0 to
1.2.0.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/pytest-dev/pluggy/blob/main/CHANGELOG.rst">pluggy's
changelog</a>.</em></p>
<blockquote>
<h1>pluggy 1.2.0 (2023-06-21)</h1>
<h2>Features</h2>
<ul>
<li><code>[#405](pytest-dev/pluggy#405)
&lt;https://github.com/pytest-dev/pluggy/issues/405&gt;</code>_: The
new-style hook wrappers, added in the yanked 1.1.0 release, now require
an explicit <code>wrapper=True</code> designation in the
<code>@hookimpl()</code> decorator.</li>
</ul>
<h1>pluggy 1.1.0 (YANKED)</h1>
<p>.. note::</p>
<p>This release was yanked because unfortunately the implicit new-style
hook wrappers broke some downstream projects.
See <code>[#403](pytest-dev/pluggy#403)
&lt;https://github.com/pytest-dev/pluggy/issues/403&gt;</code>__ for
more information.
This was rectified in the 1.2.0 release.</p>
<h2>Deprecations and Removals</h2>
<ul>
<li><code>[#364](pytest-dev/pluggy#364)
&lt;https://github.com/pytest-dev/pluggy/issues/364&gt;</code>_: Python
3.6 is no longer supported.</li>
</ul>
<h2>Features</h2>
<ul>
<li>
<p><code>[#260](pytest-dev/pluggy#260)
&lt;https://github.com/pytest-dev/pluggy/issues/260&gt;</code>_: Added
&quot;new-style&quot; hook wrappers, a simpler but equally powerful
alternative to the existing <code>hookwrapper=True</code> wrappers.</p>
<p>New-style wrappers are generator functions, similarly to
<code>hookwrapper</code>, but do away with the :class:<code>result
&lt;pluggy._callers._Result&gt;</code> object.
Instead, the return value is sent directly to the <code>yield</code>
statement, or, if inner calls raised an exception, it is raised from the
<code>yield</code>.
The wrapper is expected to return a value or raise an exception, which
will become the result of the hook call.</p>
<p>New-style wrappers are fully interoperable with old-style wrappers.
We encourage users to use the new style, however we do not intend to
deprecate the old style any time soon.</p>
<p>See :ref:<code>hookwrappers</code> for the full documentation.</p>
</li>
<li>
<p><code>[#364](pytest-dev/pluggy#364)
&lt;https://github.com/pytest-dev/pluggy/issues/364&gt;</code>_: Python
3.11 and 3.12 are now officially supported.</p>
</li>
<li>
<p><code>[#394](pytest-dev/pluggy#394)
&lt;https://github.com/pytest-dev/pluggy/issues/394&gt;</code>_: Added
the :meth:<code>~pluggy._callers._Result.force_exception</code> method
to <code>_Result</code>.</p>
<p><code>force_exception</code> allows (old-style) hookwrappers to force
an exception or override/adjust an existing exception of a hook
invocation,
in a properly behaving manner. Using <code>force_exception</code> is
preferred over raising an exception from the hookwrapper,
because raising an exception causes other hookwrappers to be
skipped.</p>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/pytest-dev/pluggy/commit/9060a4e466a8ef08bd737dd75acf1e976b76dc07"><code>9060a4e</code></a>
Preparing release 1.2.0</li>
<li><a
href="https://github.com/pytest-dev/pluggy/commit/926084e262441a3d2ab30ae7aefa463377f4c119"><code>926084e</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pluggy/issues/379">#379</a>
from vitek/spec-conflict</li>
<li><a
href="https://github.com/pytest-dev/pluggy/commit/8103269dac8773bf7702d1494127383bbbe4cfc4"><code>8103269</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pluggy/issues/411">#411</a>
from bluetech/new-style-v2</li>
<li><a
href="https://github.com/pytest-dev/pluggy/commit/3f9f62275d39679638aa2c9b3b9cc02665e9cdfb"><code>3f9f622</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pluggy/issues/408">#408</a>
from bluetech/releasing-pre-test</li>
<li><a
href="https://github.com/pytest-dev/pluggy/commit/e241aed3efdde28e6862386a06d4faf840165650"><code>e241aed</code></a>
Make new-style wrappers use explicit <code>wrapper=True</code></li>
<li><a
href="https://github.com/pytest-dev/pluggy/commit/165c4a71a2d528596d055d82cd1e5a78389dd1c6"><code>165c4a7</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pluggy/issues/410">#410</a>
from bluetech/rm-subset-slots</li>
<li><a
href="https://github.com/pytest-dev/pluggy/commit/fea9daa582c1855c74ef7d4cd5fd923ed2ed8e25"><code>fea9daa</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pluggy/issues/409">#409</a>
from bluetech/pm-no-slots</li>
<li><a
href="https://github.com/pytest-dev/pluggy/commit/b56b4c3a7e9cdcd8b94709f805a2865a08cc6218"><code>b56b4c3</code></a>
hooks: remove a couple of unnecessary slots from
<code>_SubsetHookCaller</code></li>
<li><a
href="https://github.com/pytest-dev/pluggy/commit/2e2a0408cb32563ee0bd610fe414045a39043873"><code>2e2a040</code></a>
Add scripts for downstream testing</li>
<li><a
href="https://github.com/pytest-dev/pluggy/commit/4cfc67669a17b9c94abad2e035526ef2c1a1f4db"><code>4cfc676</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pluggy/issues/407">#407</a>
from pytest-dev/pre-commit-ci-update-config</li>
<li>Additional commits viewable in <a
href="https://github.com/pytest-dev/pluggy/compare/1.0.0...1.2.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=pluggy&package-manager=pip&previous-version=1.0.0&new-version=1.2.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

You can trigger a rebase of this PR by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

> **Note**
> Automatic rebases have been disabled on this pull request as it has
been open for over 30 days.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants