Skip to content

fix(handlers): support non-attribute-writable callables in wrap_handler - #3341

Open
Alok Pandey (alok-108) wants to merge 1 commit into
microsoft:mainfrom
alok-108:fix/wrap-handler-non-writable-callables
Open

Alok Pandey (alok-108) wants to merge 1 commit into
microsoft:mainfrom
alok-108:fix/wrap-handler-non-writable-callables

Conversation

@alok-108

@alok-108 Alok Pandey (alok-108) commented Nov 13, 2026 •

Copy link
Copy Markdown

Fixes #3340

Problem

ImplToApiMapping.wrap_handler() cached wrappers by mutating _pw_impl_instance_* attributes onto handler or handler.__self__ via setattr(). This raised AttributeError when registering:

  • built-in functions and bound methods (list.append, print, len, set.__contains__, re.Pattern.match)
  • C method-wrapper objects (str.__eq__)
  • __slots__ / @dataclass(slots=True) bound methods and callable instances
  • @dataclass(frozen=True) bound methods and callable instances

Solution

  • Cache wrapped handlers per ImplToApiMapping instance in a weakref.WeakValueDictionary instead of setting _pw_impl_instance_* attributes on user objects.
  • Key Python bound methods (inspect.ismethod) and built-in/C bound methods (BuiltinFunctionType / MethodWrapperType with __self__) by receiver identity (id(__self__)) and underlying function/qualname metadata so repeated attribute lookups (messages.append, allowed.__contains__, collector.handle) return the identical wrapper for remove_listener() / unroute(), while keeping distinct instances isolated and preventing reference-cycle leaks.
  • Fall back to id(handler) for standalone functions, lambdas, callable instances (including unhashable slotted/frozen callables), and mocks (MagicMock).
  • Add sync and async regression tests in tests/sync/python/test_handlers.py and tests/async/python/test_handlers.py.

Verification

  • SKIP=pyright pre-commit run --files playwright/_impl/_impl_to_api_mapping.py tests/sync/python/test_handlers.py tests/async/python/test_handlers.py: all checked hooks passed (black, mypy, flake8, isort, Check License Header, AST/whitespace checks; pyright run separately below)
  • npx --yes pyright@1.1.403 playwright/_impl/_impl_to_api_mapping.py tests/sync/python/test_handlers.py tests/async/python/test_handlers.py: 0 errors, 0 warnings, 0 informations
  • mypy --platform linux playwright: Success: no issues found in 105 source files
  • python scripts/check_test_registry.py: passed
  • PW_CHECK_PROTOCOL=1 pytest tests/sync/python/ tests/sync/page/test_page_route.py tests/sync/page/test_page_event_console.py tests/sync/page/test_page_expose_function.py --browser=chromium: 136 passed, 4 skipped (3 Windows SIGALRM, 1 WebKit-only), 1 xfailed
  • PW_CHECK_PROTOCOL=1 pytest tests/async/python/test_handlers.py tests/async/python/test_listeners.py tests/async/python/test_console.py tests/async/python/test_issues.py tests/async/python/test_asyncio.py tests/async/page/test_page_route.py tests/async/page/test_page_event_console.py tests/async/page/test_page_expose_function.py --browser=chromium: 109 passed, 2 skipped (1 Python 3.14 PEP 649, 1 WebKit-only), 1 xfailed
  • PW_CHECK_PROTOCOL=1 pytest tests/common/ --browser=chromium: 2 passed, 2 skipped (Windows SIGINT)

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

@alok-108
Alok Pandey (alok-108) force-pushed the fix/wrap-handler-non-writable-callables branch from c5622ea to 0576b49 Compare November 13, 2026 08:43

This branch has not been deployed

No deployments
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

Successfully merging this pull request may close these issues.

[Bug]: AttributeError when registering built-in callables, __slots__, or frozen objects as handlers

1 participant