Skip to content

[Bug]: Clock.run_for: TypeError: Cannot read properties of undefined (reading 'controller') #2563

@jolly-xw

Description

@jolly-xw

Version

1.46.0

Steps to reproduce

I want to know the correct usage of Clock. Is it to use context.clock.install() and then perform page.clock.run_for() on each page under the context, or is it necessary to also use page.clock.install() on each page?
Here is my code, and you can supplement it with test cases as needed. I am not clear about the usage of Clock in asynchronous scenarios, so my program behaves unexpectedly and produces an error: Clock.run_for: TypeError: Cannot read properties of undefined (reading 'controller'), or it produces the error: Clock.run_for: Target page, context or browser has been closed. These issues are intermittent and cannot be consistently reproduced 100% of the time.

import asyncio
import logging
import json
from playwright.async_api import async_playwright

logger = logging.getLogger(__name__)


class HeadlessWorker:
    def __init__(self,
                 browser_name="chromium"):
        # init
        self.browser_name = browser_name
        self.playwright = None
        self.browser = None
        self.context = None

    async def launch_browser(self) -> None:
        try:
            self.playwright = await async_playwright().start()
            self.browser = await self.playwright[self.browser_name].launch(headless=True)
            self.context = await self.browser.new_context(
                accept_downloads=False,
            )
            **_await self.context.clock.install()_**
        except Exception as err:
            logger.warning("[HeadlessWorker] error info:{}".format(err))

    async def close_browser(self) -> None:
        try:
            await self.browser.close()
            await self.playwright.stop()
        except Exception as err:
            logger.error(
                "close browser failed and err info:{}".format(err))

    async def listen_download_event(self, url: dict) -> bool:

        async with asyncio.Semaphore(5):

            # listen data
            lis_data_tmp = {
                "is_download": False, "download_url": set()
            }

            try:
                page = await self.context.new_page()
            except Exception as err:
                logger.error(
                    "create new page for url:{} and error info: {}".format(
                        url, err))
                return False

            def process_download(download, record) -> None:
                try:
                    record["is_download"] = True
                    record["download_url"].add(download.url)
                except Exception as err:
                    logger.error(
                        "[Error] error info: {}.".format(err)
                    )

            try:
                page.on(
                    "download",
                    lambda download: process_download(
                        download,
                        lis_data_tmp))
            except Exception as err:
                logger.error(
                    "error info: {}".format(err)
                )

            _**try:
                await page.clock.install()
            except Exception as err:
                logger.warning(
                    "[Clock] Install Failed, err info: {}".format(err))
                return False**_
            try:
                await page.goto(url, timeout=6000)
                **_await page.clock.run_for(50000)_**
                await page.wait_for_load_state("networkidle", timeout=15000)
                await asyncio.sleep(1)

            except Exception as err:
                if "net::ERR_ABORTED" in str(err):
                    pass
                else:
                    logger.error(
                        "url:{},[Navigator] error info: {}".format(url, err)
                    )

            await page.close()
            print("*url:{}, download info:{}".format(url, lis_data_tmp))
            return True

    async def run(self, tasks):
        try:
            coroutines = []
            for task in tasks:
                coroutines.append(
                    asyncio.create_task(
                        self.listen_download_event(task)))
            await asyncio.gather(*coroutines)
        except Exception as err:
            logger.error("async error msg: {}".format(err))


if __name__ == "__main__":
    async def start():
        Worker = HeadlessWorker()
        await Worker.launch_browser()
        urls= [
        ]
        await Worker.run(urls)
        await Worker.close_browser()

    asyncio.run(start())

I couldn't find any effective demos in the official documentation; I need a complete example.

Expected behavior

has no error

Actual behavior

error happened

Additional context

No response

Environment

- Operating System: [Windows 10]
- CPU: [intel]
- Browser: [Chromium]
- Python Version: [3.8.5]
- Other info:

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions