diff --git a/demo/pages/search_page/components/package_list.py b/demo/pages/search_page/components/package_list.py index 8ae70c4..0d8a5fa 100644 --- a/demo/pages/search_page/components/package_list.py +++ b/demo/pages/search_page/components/package_list.py @@ -13,7 +13,7 @@ class PackageList(ListComponent[Package, PyPIPage]): # By default `ListComponent` have `item_class` attribute with stored first # Generic variable (Package in current case). But during # development/inheritance/addition of other generic variables it may become - # unavailable. + # invalid. # In this case it is necessary to explicitly set `item_class` attribute: # item_class = Package diff --git a/docs/developer_interface.rst b/docs/developer_interface.rst index 4550b4a..8e723ab 100644 --- a/docs/developer_interface.rst +++ b/docs/developer_interface.rst @@ -19,9 +19,72 @@ Page Components ******************************************************************************* -.. automodule:: pomcorn.component +.. autoclass:: pomcorn.component.Component :members: +.. autoclass:: pomcorn.component.ListComponent + :members: + +Invalid ``item_class`` case +------------------------------------------------------------------------------- + +Here is an example of a case where the automatic filling of ``item_class`` +attribute of a generic variable may be incorrect: + +.. code-block:: python + + from typing import Generic, TypeVar + + from pomcorn import Component, ListComponent, Page, locators + from selenium import webdriver as selenium_webdriver + + # pypi search page + url = "https://pypi.org/search/?q=saritasa&o=" + + # Set browser's language to English + options = selenium_webdriver.ChromeOptions() + prefs = {"intl.accept_languages": "en,en_U"} + options.add_experimental_option("prefs", prefs) + + # Open page + webdriver = selenium_webdriver.Chrome(options) + page = Page.open(webdriver, app_root=url) + + # Prepare out item class component + class ExpectedItemClass(Component[Page]):... + + # Implement list component from which another component with generic variable will inherit + TPage = TypeVar("TPage") + class ListItem(Generic[TPage], ListComponent[ExpectedItemClass, TPage]): + base_locator = locators.PropertyLocator( + prop="aria-label", + value="Search results", + ) + relative_base_locator = locators.TagNameLocator("li") + + # Create a descendant and pass a generic variable to it + class InheritedList(ListItem[Page]):... + + # Check current `item_class` + print(f"`InheritedList.item_class` is `{InheritedList(page).item_class}` now") + # `InheritedList.item_class` is `` now + # Instead of specified `` + + webdriver.close() + + +To solve this problem just specify ``item_class`` right in ``InheritedList``: + +.. code-block:: python + + ... + class InheritedList(ListItem[Page]):... + item_class = ExpectedItemClass + + # Check current `item_class` + print(f"`InheritedList.item_class` is `{InheritedList(page).item_class}` now") + # `InheritedList.item_class` is `` now + PomcornElement ******************************************************************************* diff --git a/pomcorn/component.py b/pomcorn/component.py index 8460f3c..67a81cd 100644 --- a/pomcorn/component.py +++ b/pomcorn/component.py @@ -183,6 +183,19 @@ class ListComponent(Generic[ListItemType, TPage], Component[TPage]): """ item_class: type[ListItemType] + """By default this attr is automatically filled by first generic variable. + + But when you develop/inherit/add other generic variables, it may become + invalid. So if you encounter strange behavior of ``item_class``, reconsider + generics/inheritance or just override `item_class` directly in the class. + + An example of such a case is given in the documentation: + http://pomcorn.rtfd.io/en/latest/developer_interface.html#invalid-item-class-case + + If specified `item_class` and first generic variable match - you will see + a warning that you can delete this overridden attribute. + + """ item_locator: locators.XPathLocator | None = None relative_item_locator: locators.XPathLocator | None = None