Repository: apify/crawlee-python
File: src/crawlee/browsers/_playwright_browser_plugin.py (Python)

Code before:
                own context that is destroyed once the page is closed or crashes.
            fingerprint_generator: An optional instance of implementation of `FingerprintGenerator` that is used
                to generate browser fingerprints together with consistent headers.
        """
        config = service_locator.get_configuration()

        # Default browser launch options are based on the configuration.
        default_launch_browser_options: dict[str, Any] = {
            'headless': config.headless,
            'executable_path': config.default_browser_path,
            'chromium_sandbox': not config.disable_browser_sandbox,
        }
        explicit_browser_launch_options = browser_launch_options or {}

        # Map 'chrome' to 'chromium' with the 'chrome' channel.
        if browser_type == 'chrome':
            browser_type = 'chromium'
            # Chromium parameter 'channel' set to 'chrome' enables using installed Google Chrome.
            default_launch_browser_options['channel'] = 'chrome'

            if explicit_browser_launch_options.get(
                'executable_path', default_launch_browser_options.get('executable_path')
            ):
                logger.debug(
                    f'Using browser executable from {default_launch_browser_options["executable_path"]},'
                    f" which takes precedence over 'chrome' channel."
                )

        self._browser_type: BrowserType = browser_type
        self._browser_launch_options: dict[str, Any] = default_launch_browser_options | explicit_browser_launch_options
        self._browser_new_context_options = browser_new_context_options or {}
        self._max_open_pages_per_browser = max_open_pages_per_browser
        self._use_incognito_pages = use_incognito_pages
        self._user_data_dir = user_data_dir

        self._playwright_context_manager = async_playwright()
        self._playwright: Playwright | None = None

        # Flag to indicate the context state.
        self._active = False

        self._fingerprint_generator = fingerprint_generator

    @property
    @override
    def active(self) -> bool:
        return self._active

    @property
    @override
    def browser_type(self) -> BrowserType:

Review comment (bug):
Bug: Debug log message reports wrong executable path

  File: src/crawlee/browsers/_playwright_browser_plugin.py

  The condition checks explicit_browser_launch_options.get('executable_path',
  default_launch_browser_options.get('executable_path')) to determine if an
  executable path is set, but the log message always formats
  default_launch_browser_options["executable_path"]. When the user passes
  executable_path via browser_launch_options, the log will display the wrong
  path (the default one, not the user-provided one).

  Suggestion:

  effective_path = explicit_browser_launch_options.get(
      'executable_path', default_launch_browser_options.get('executable_path')
  )
  if effective_path:
      logger.debug(
          f"Using browser executable from {effective_path},"
          f" which takes precedence over 'chrome' channel."
      )

Code after:
                own context that is destroyed once the page is closed or crashes.
            fingerprint_generator: An optional instance of implementation of `FingerprintGenerator` that is used
                to generate browser fingerprints together with consistent headers.
        """
        config = service_locator.get_configuration()

        # Default browser launch options are based on the configuration.
        default_launch_browser_options: dict[str, Any] = {
            'headless': config.headless,
            'executable_path': config.default_browser_path,
            'chromium_sandbox': not config.disable_browser_sandbox,
        }
        explicit_browser_launch_options = browser_launch_options or {}

        # Map 'chrome' to 'chromium' with the 'chrome' channel.
        if browser_type == 'chrome':
            browser_type = 'chromium'
            # Chromium parameter 'channel' set to 'chrome' enables using installed Google Chrome.
            default_launch_browser_options['channel'] = 'chrome'

            if executable_path := explicit_browser_launch_options.get(
                'executable_path', default_launch_browser_options.get('executable_path')
            ):
                logger.debug(
                    f"Using browser executable from {executable_path}, which takes precedence over 'chrome' channel."
                )

        self._browser_type: BrowserType = browser_type
        self._browser_launch_options: dict[str, Any] = default_launch_browser_options | explicit_browser_launch_options
        self._browser_new_context_options = browser_new_context_options or {}
        self._max_open_pages_per_browser = max_open_pages_per_browser
        self._use_incognito_pages = use_incognito_pages
        self._user_data_dir = user_data_dir

        self._playwright_context_manager = async_playwright()
        self._playwright: Playwright | None = None

        # Flag to indicate the context state.
        self._active = False

        self._fingerprint_generator = fingerprint_generator

    @property
    @override
    def active(self) -> bool:
        return self._active

    @property
    @override
    def browser_type(self) -> BrowserType:
        return self._browser_type