-
Notifications
You must be signed in to change notification settings - Fork 127
try claude - add a basic config and play around with some quality of life changes i was offputting #584
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
9f4ce17
to
d1815d2
Compare
@nicoddemus please have a look, this is one of the changes i was dreading to start a while back - we have similar ones in pytest |
src/pluggy/_manager.py
Outdated
except Exception: # pragma: no cover | ||
impl_attr = None | ||
|
||
if impl_attr is not None: |
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.
this one is a artifact i still need to resolve, but the followup on those is much lighter than the heavy lifting before
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.
Overall the changes look OK, however, my review was not through.
For the "ready for review" version of the PR, I believe a clean commit history is a must. There is a good amount of code that is introduced in some commits, only to be removed later.
@@ -66,7 +67,9 @@ known-local-folder = ["pluggy"] | |||
lines-after-imports = 2 | |||
|
|||
[tool.setuptools_scm] | |||
version_file = "src/pluggy/_version.py" | |||
|
|||
[tool.uv] |
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.
Please update docs/index.rst
, Development
section, about using uv
.
i added greenlet for the asnyc support as complete recoloring was insane |
@nicoddemus this is partially ready for discussion, i'll create a new branc hand cherry pick myself up over the course of the week |
Agree with adding Claude's conf, can't review the refactor though.
99be2a2
to
f93156a
Compare
b9bec1d
to
978a61b
Compare
i just forced myself to relearn git revlog |
i made https://gist.github.com/RonnyPfannschmidt/e90dec6540d6be45d63793493fe0b811and fixed things |
844dffb
to
e8ce9f7
Compare
also add uv based tooling to ease things Co-Authored-By: Claude <noreply@anthropic.com> - add ruff/mypy - allow claude to run some commands directly
Introduces a class-based approach to replace the HookimplOpts TypedDict in future versions, with backward compatibility methods. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
…action - Add HookimplMarker.get_hookconfig() method that returns HookimplConfiguration directly - Update test files to use new method instead of accessing dynamic attributes - Fix mypy type errors in test_multicall.py and test_hookcaller.py - Update CLAUDE.md to use 'uv run mypy src/' command 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Remove HookImpl.opts deprecated attribute - Remove HookimplConfiguration.to_opts() method (no longer used) - Update benchmark tests to use hookimpl.get_hookconfig() directly - Eliminates internal HookimplOpts conversions while preserving public API 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Update HookimplMarker to store HookimplConfiguration objects directly instead of HookimplOpts dicts on decorated functions - Add new internal _parse_hookimpl method that works with HookimplConfiguration and handles both new-style configs and legacy dict-based configs - Convert parse_hookimpl_opts to compatibility shim that returns None, allowing legacy projects like pytest to override while modern code uses structured configuration throughout - Update test to use direct attribute access instead of dict .get() method - Eliminate redundant dict-to-config conversions in plugin registration 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Add new ProjectSpec class that manages hook markers and plugin manager creation - ProjectSpec encapsulates HookspecMarker, HookimplMarker, and PluginManager creation - Support for custom PluginManager subclasses via plugin_manager_cls parameter - All components share the same project_name ensuring consistent behavior - Update marker classes and PluginManager to accept str or ProjectSpec instances - Use module references (_project.ProjectSpec) to avoid circular imports - Add comprehensive test suite covering all ProjectSpec functionality 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Remove normalize_hookimpl_opts function as modern pluggy uses HookimplConfiguration directly - Remove HookimplConfiguration.from_opts class method (backward compatibility no longer needed) - Simplify _parse_hookimpl method by removing dict-based configuration handling - Use HookimplConfiguration constructor directly instead of factory method - Maintain compatibility with parse_hookimpl_opts override for subclasses like pytest - Remove unnecessary normalize_hookimpl_opts import from _manager.py 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Add HookspecConfiguration class mirroring HookimplConfiguration pattern - Replace HookspecOpts TypedDict usage with structured class internally - Add backward compatibility for HookCaller.set_specification method - Support both positional/keyword HookspecOpts and new HookspecConfiguration - Add comprehensive tests for new configuration system - Deprecate parse_hookspec_opts method to return None by default 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Move hook configuration extraction methods from HookspecMarker and HookimplMarker to ProjectSpec to provide a unified API for accessing hook configurations. Changes: - Add ProjectSpec.get_hookspec_config() and get_hookimpl_config() methods - Remove HookspecMarker._get_hookconfig() and HookimplMarker.get_hookconfig() - Update PluginManager to use ProjectSpec methods for configuration extraction - Use getattr with default None instead of exception handling for better performance - Update tests to use the new ProjectSpec-based API 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Update the benchmark file to use the new ProjectSpec.get_hookimpl_config() method instead of the removed HookimplMarker.get_hookconfig() method. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Create dedicated HistoricHookCaller class for historic hooks - Remove historic functionality from regular HookCaller class - Implement handover mechanism in PluginManager to convert HookCaller to HistoricHookCaller when historic specs are detected - Update _SubsetHookCaller to properly delegate historic operations - Add HistoricHookCaller to public API exports - Historic hooks now have simplified implementation ordering (no wrapper support) - Improves separation of concerns and performance for non-historic hooks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Create HookCaller protocol defining public interface for all hook callers - Rename HookCaller to NormalHookCaller for non-historic hook handling - Make HistoricHookCaller standalone (no inheritance) - Rename _SubsetHookCaller to SubsetHookCaller and make standalone - Delegate argument verification to HookSpec.verify_all_args_are_provided() - SubsetHookCaller now maintains same invariants as proxied hook caller: - Historic: only allows call_historic(), blocks __call__() and call_extra() - Normal: allows __call__() and call_extra(), blocks call_historic() - Both: block set_specification() as read-only proxies - Update type hints throughout codebase to use protocol interface - Maintain backward compatibility with _HookCaller and _SubsetHookCaller aliases All tests pass (142/142) and code quality checks pass. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Split the large _hooks.py file (1132 lines) into three focused modules: - _hook_config.py: Configuration classes and type definitions (HookspecOpts, HookimplOpts, HookspecConfiguration, HookimplConfiguration) - _hook_markers.py: Hook decorators and specifications (HookspecMarker, HookimplMarker, HookSpec, varnames utility) - _hook_callers.py: Hook caller implementations and HookImpl (HookCaller protocol, NormalHookCaller, HistoricHookCaller, SubsetHookCaller, HookRelay, HookImpl) The original _hooks.py now serves as a backward compatibility module that re-exports all symbols, maintaining 100% API compatibility. Updated all internal imports across the codebase to use the new module structure while preserving existing public interfaces. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
…lots__ classes This change improves type safety and code organization by: - Moving all Final annotations from constructors to class level for consistency - Adding proper type annotations to all __slots__ classes - Simplifying constructor assignments by removing redundant type annotations Classes updated: - HistoricHookCaller: 5 Final attributes moved to class level - NormalHookCaller: 4 Final attributes moved to class level - HookImpl: 11 Final attributes moved to class level - HookspecMarker: _project_spec moved to class level with Final - HookimplMarker: _project_spec moved to class level with Final - HookspecConfiguration: 4 Final attributes moved to class level - HookimplConfiguration: 6 Final attributes moved to class level - HookRelay: Added __dict__ type annotation - SubsetHookCaller: Added type annotations for _orig and _remove_plugins - HookSpec: Added type annotations for all 8 attributes - Result: Added type annotations for all 3 attributes 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Split single _hookimpls list into _normal_hookimpls and _wrapper_hookimpls - Add _insert_hookimpl_into_list() utility to eliminate code duplication - Use unpacking syntax [*first, *second] for cleaner list combinations - Simplifies hook management by removing complex splitpoint calculations - Maintains all existing ordering semantics and test compatibility 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This change introduces type-safe separation between normal hook implementations and wrapper implementations by: - Creating WrapperImpl subclass for wrapper/hookwrapper implementations - Modifying _multicall to handle normal and wrapper implementations separately - Updating hook callers to maintain separate lists and enforce type constraints - Adding factory method in HookimplConfiguration for proper type instantiation - Ensuring historic hooks reject wrapper implementations at registration time The separation improves type safety, clarifies execution flow, and maintains backward compatibility while providing better error messages for misuse. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Move the duplicated argument extraction and error handling logic from _multicall into a private _get_call_args() method on HookImpl. This eliminates code duplication between wrapper and normal hook implementation processing paths and centralizes the argument verification logic. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
…hook pattern The _multicall function had intricate generator-based wrapper teardown logic that was difficult to understand and maintain. This refactoring introduces a completion hook pattern that simplifies the wrapper invocation process. Key changes: - Add CompletionHook type alias with (result, exception) -> (result, exception) signature - Implement setup_and_get_completion_hook() method in WrapperImpl class - Unify old-style and new-style wrapper handling using run_old_style_hookwrapper adapter - Replace complex teardown loop with simple completion hook calls - Adjust warning stacklevel from 6 to 7 for correct source attribution The completion hook pattern makes wrapper teardown explicit and easier to debug while maintaining full backward compatibility with both hookwrapper and wrapper implementations. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Split the large _hooks.py file (1132 lines) into three focused modules: - _hook_config.py: Configuration classes and type definitions (HookspecOpts, HookimplOpts, HookspecConfiguration, HookimplConfiguration) - _hook_markers.py: Hook decorators and specifications (HookspecMarker, HookimplMarker, HookSpec, varnames utility) - _hook_callers.py: Hook caller implementations and HookImpl (HookCaller protocol, NormalHookCaller, HistoricHookCaller, SubsetHookCaller, HookRelay, HookImpl) The original _hooks.py now serves as a backward compatibility module that re-exports all symbols, maintaining 100% API compatibility. Updated all internal imports across the codebase to use the new module structure while preserving existing public interfaces. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
…lots__ classes This change improves type safety and code organization by: - Moving all Final annotations from constructors to class level for consistency - Adding proper type annotations to all __slots__ classes - Simplifying constructor assignments by removing redundant type annotations Classes updated: - HistoricHookCaller: 5 Final attributes moved to class level - NormalHookCaller: 4 Final attributes moved to class level - HookImpl: 11 Final attributes moved to class level - HookspecMarker: _project_spec moved to class level with Final - HookimplMarker: _project_spec moved to class level with Final - HookspecConfiguration: 4 Final attributes moved to class level - HookimplConfiguration: 6 Final attributes moved to class level - HookRelay: Added __dict__ type annotation - SubsetHookCaller: Added type annotations for _orig and _remove_plugins - HookSpec: Added type annotations for all 8 attributes - Result: Added type annotations for all 3 attributes 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Split single _hookimpls list into _normal_hookimpls and _wrapper_hookimpls - Add _insert_hookimpl_into_list() utility to eliminate code duplication - Use unpacking syntax [*first, *second] for cleaner list combinations - Simplifies hook management by removing complex splitpoint calculations - Maintains all existing ordering semantics and test compatibility 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This change introduces type-safe separation between normal hook implementations and wrapper implementations by: - Creating WrapperImpl subclass for wrapper/hookwrapper implementations - Modifying _multicall to handle normal and wrapper implementations separately - Updating hook callers to maintain separate lists and enforce type constraints - Adding factory method in HookimplConfiguration for proper type instantiation - Ensuring historic hooks reject wrapper implementations at registration time The separation improves type safety, clarifies execution flow, and maintains backward compatibility while providing better error messages for misuse. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
…hook pattern The _multicall function had intricate generator-based wrapper teardown logic that was difficult to understand and maintain. This refactoring introduces a completion hook pattern that simplifies the wrapper invocation process. Key changes: - Add CompletionHook type alias with (result, exception) -> (result, exception) signature - Implement setup_and_get_completion_hook() method in WrapperImpl class - Unify old-style and new-style wrapper handling using run_old_style_hookwrapper adapter - Replace complex teardown loop with simple completion hook calls - Adjust warning stacklevel from 6 to 7 for correct source attribution The completion hook pattern makes wrapper teardown explicit and easier to debug while maintaining full backward compatibility with both hookwrapper and wrapper implementations. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
e8ce9f7
to
6570d43
Compare
to return awaitable objects that are automatically awaited when running in an | ||
async context. | ||
""" | ||
|
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.
@simonw i'd appreciate early feedback on this wrt utility for datasette
its not the deep integration like I wanted/hoped to implement at first
however its a directly working integration that doesn’t break on color mixing - but it does have the caveat of breaking never officieally supported threading patterns some people use
i currently intend to clean up history and then have this land and release
result = await pm.run_async(lambda: pm.hook.some_hook()) | ||
""" | ||
|
||
def wrapper() -> _T: |
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.
What is the purpose of this wrapper?
I changed the bottom of the function to return await self._async_submitter.run(func)
(ignoring the wrapper), and all tests still pass.
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.
This can be removed as i made the submitter mandatory after adding that api
Im not yet that far with the history trim down
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 figured as much, but given I noticed it, figured it better to leave a comment.
else: | ||
raise RuntimeError("require_await called outside of async context") | ||
|
||
async def run(self, sync_func: Callable[[], _T]) -> _T: |
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.
Sorry if this question is obvious—I don’t have much experience with greenlets.
Isn’t the purpose of using greenlet
to have multiple worker greenlets and switch between them as they execute? In other words, if one worker is busy and can’t proceed, we can switch to another worker in the meantime.
If that’s correct, what does greenlet
actually bring to the table here? As I understand it, we only have two greenlets: the main one and a worker. The main greenlet runs a while loop, switching to the worker greenlet to obtain a new awaitable.
From my (admittedly limited) understanding, to support async hooks in PluggyManager
, it seems we only need to explicitly handle the awaitable objects returned by the hooks. For example:
async def run_async2(self, func: Callable[[], _T]) -> _T:
results = func()
for index, result in enumerate(results):
if inspect.isawaitable(result):
result = await result
results[index] = result
return results
If we replace pm.run_async
with pm.run_async2
in test_async.py
, all the tests still pass. What am I missing here?
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.
Greenlet allows to switch stacks
Which allows to switch from 10 sync calls in back to a async function that can await a awaitable
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.
The run function will pass into asyncio trio or anyio. Then it will switch back into the sync function
And now any sync call below can send a sync call to the outer async function
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.
Thanks, @RonnyPfannschmidt, but it’s still not clear to me how greenlet
fits into this.
In other words, what does run_async
do that run_async2
does not? From my understanding, they seem functionally equivalent—is that correct?
If not, we should create a test case that only works with run_async
but fails with run_async2
. That would help clarify why greenlet
is necessary here.
Adds CLAUDE.md file with development commands and architecture overview for future Claude Code instances.
🤖 Generated with Claude Code