pomcorn 0.8.0__tar.gz → 0.8.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pomcorn might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pomcorn
3
- Version: 0.8.0
3
+ Version: 0.8.1
4
4
  Summary: Base implementation of Page Object Model
5
5
  Home-page: https://pypi.org/project/pomcorn/
6
6
  License: MIT
@@ -119,7 +119,6 @@ Below is the code that opens ``PyPI.org``, searches for packages by name and pri
119
119
 
120
120
  class PackageList(ListComponent[Package, PyPIPage]):
121
121
 
122
- item_class = Package
123
122
  relative_item_locator = locators.ClassLocator("snippet__name")
124
123
 
125
124
  @property
@@ -93,7 +93,6 @@ Below is the code that opens ``PyPI.org``, searches for packages by name and pri
93
93
 
94
94
  class PackageList(ListComponent[Package, PyPIPage]):
95
95
 
96
- item_class = Package
97
96
  relative_item_locator = locators.ClassLocator("snippet__name")
98
97
 
99
98
  @property
@@ -1,4 +1,13 @@
1
- from typing import Generic, TypeVar, get_args, overload
1
+ from inspect import isclass
2
+ from typing import (
3
+ Any,
4
+ Generic,
5
+ Literal,
6
+ TypeVar,
7
+ get_args,
8
+ get_origin,
9
+ overload,
10
+ )
2
11
 
3
12
  from . import locators
4
13
  from .element import XPathElement
@@ -8,6 +17,17 @@ from .web_view import WebView
8
17
  TPage = TypeVar("TPage", bound=Page)
9
18
 
10
19
 
20
+ class _EmptyValue:
21
+ """Singleton to use as default value for empty class attribute."""
22
+
23
+ def __bool__(self) -> Literal[False]:
24
+ """Allow `EmptyValue` to be used in bool expressions."""
25
+ return False
26
+
27
+
28
+ EmptyValue: Any = _EmptyValue()
29
+
30
+
11
31
  class Component(Generic[TPage], WebView):
12
32
  """The class to represent a page component that depends on base locator.
13
33
 
@@ -182,31 +202,30 @@ class ListComponent(Generic[ListItemType, TPage], Component[TPage]):
182
202
 
183
203
  """
184
204
 
205
+ _item_class: type[ListItemType] = EmptyValue
206
+
185
207
  item_locator: locators.XPathLocator | None = None
186
208
  relative_item_locator: locators.XPathLocator | None = None
187
209
 
188
- def __init__(
189
- self,
190
- page: TPage,
191
- base_locator: locators.XPathLocator | None = None,
192
- wait_until_visible: bool = True,
193
- ):
194
- super().__init__(page, base_locator, wait_until_visible)
195
- if item_class := getattr(self, "item_class", None):
196
- import warnings
197
-
198
- warnings.warn(
199
- DeprecationWarning(
200
- "\nSpecifying `item_class` attribute in `ListComponent` "
201
- f"({self.__class__}) is DEPRECATED. It is now "
202
- "automatically substituted from Generic[ListItemType]. "
203
- "Ability to specify this attribute will be removed soon.",
204
- ),
205
- stacklevel=2,
206
- )
207
- self._item_class = item_class
208
- else:
209
- self._item_class = self._get_list_item_class()
210
+ def __init_subclass__(cls) -> None:
211
+ """Run logic for getting/overriding item_class attr for subclasses."""
212
+ super().__init_subclass__()
213
+
214
+ # If class has valid `_item_class` attribute from a parent class
215
+ if cls.is_valid_item_class(cls._item_class):
216
+ # We leave using of parent `item_class`
217
+ return
218
+
219
+ # Try to get `item_class` from first generic variable
220
+ list_item_class = cls.get_list_item_class()
221
+
222
+ if not list_item_class:
223
+ # If `item_class` is not specified in generic we leave it empty
224
+ # because it maybe not specified in base class but will be
225
+ # specified in child
226
+ return
227
+
228
+ cls._item_class = list_item_class
210
229
 
211
230
  @property
212
231
  def base_item_locator(self) -> locators.XPathLocator:
@@ -257,6 +276,32 @@ class ListComponent(Generic[ListItemType, TPage], Component[TPage]):
257
276
  )
258
277
  return items
259
278
 
279
+ @classmethod
280
+ def get_list_item_class(cls) -> type[ListItemType] | None:
281
+ """Return class passed in `Generic[ListItemType]`."""
282
+ base_class = next(
283
+ _class
284
+ for _class in cls.__orig_bases__ # type: ignore
285
+ if isclass(get_origin(_class))
286
+ and issubclass(get_origin(_class), ListComponent)
287
+ )
288
+
289
+ # Get first generic variable and return it if it is valid item class
290
+ item_class = get_args(base_class)[0]
291
+ if cls.is_valid_item_class(item_class):
292
+ return item_class
293
+
294
+ return None
295
+
296
+ @classmethod
297
+ def is_valid_item_class(cls, item_class: Any) -> bool:
298
+ """Check that specified ``item_class`` is valid.
299
+
300
+ Valid `item_class` should be a class and subclass of ``Component``.
301
+
302
+ """
303
+ return isclass(item_class) and issubclass(item_class, Component)
304
+
260
305
  def get_item_by_text(self, text: str) -> ListItemType:
261
306
  """Get list item by text."""
262
307
  locator = self.base_item_locator.extend_query(
@@ -264,10 +309,6 @@ class ListComponent(Generic[ListItemType, TPage], Component[TPage]):
264
309
  )
265
310
  return self._item_class(page=self.page, base_locator=locator)
266
311
 
267
- def _get_list_item_class(self) -> type[ListItemType]:
268
- """Return class passed in `Generic[ListItemType]`."""
269
- return get_args(self.__orig_bases__[0])[0] # type: ignore
270
-
271
312
  def __repr__(self) -> str:
272
313
  return (
273
314
  "ListComponent("
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "pomcorn"
3
- version = "0.8.0"
3
+ version = "0.8.1"
4
4
  description = "Base implementation of Page Object Model"
5
5
  authors = [
6
6
  "Saritasa <pypi@saritasa.com>",
@@ -64,7 +64,7 @@ sphinx = ">= 7.2.6"
64
64
  sphinx-rtd-theme = ">= 1.3.0rc1"
65
65
  # Support mermaid diagrams
66
66
  # https://github.com/mgaitan/sphinxcontrib-mermaid/tree/master
67
- sphinxcontrib-mermaid = "^0.9.2"
67
+ sphinxcontrib-mermaid = ">=0.9.2,<1.1.0"
68
68
 
69
69
  [tool.poetry.group.demo.dependencies]
70
70
  pytest = ">= 7.4.2"
@@ -171,6 +171,11 @@ pytest-parametrize-names-type = "list"
171
171
  pytest-parametrize-values-type = "list"
172
172
  pytest-parametrize-values-row-type = "list"
173
173
 
174
+ per-file-ignores = [
175
+ # Ignore using lambda in `item_class` tests
176
+ "tests/list_component/test_item_class.py: E731",
177
+ ]
178
+
174
179
  [tool.mypy]
175
180
  # mypy configurations: https://mypy.readthedocs.io/en/latest/config_file.html
176
181
  # https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes