pomcorn 0.7.5__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.7.5
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, 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
 
@@ -177,21 +197,36 @@ class ListComponent(Generic[ListItemType, TPage], Component[TPage]):
177
197
  * all
178
198
  * get_item_by_text()
179
199
 
180
- Waits for `base_item_locator` property to be implemented and value of
181
- `item_class` to be set.
182
-
183
200
  Waits for `base_item_locator` property to be overridden or one of the
184
201
  attributes (`item_locator` or `relative_item_locator`) to be specified.
185
202
 
186
- The `item_class` attribute is also required.
187
-
188
203
  """
189
204
 
190
- item_class: type[ListItemType]
205
+ _item_class: type[ListItemType] = EmptyValue
191
206
 
192
207
  item_locator: locators.XPathLocator | None = None
193
208
  relative_item_locator: locators.XPathLocator | None = None
194
209
 
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
229
+
195
230
  @property
196
231
  def base_item_locator(self) -> locators.XPathLocator:
197
232
  """Get the base locator of list item.
@@ -236,21 +271,49 @@ class ListComponent(Generic[ListItemType, TPage], Component[TPage]):
236
271
 
237
272
  items: list[ListItemType] = []
238
273
  for locator in self.iter_locators(self.base_item_locator):
239
- items.append(self.item_class(page=self.page, base_locator=locator))
274
+ items.append(
275
+ self._item_class(page=self.page, base_locator=locator),
276
+ )
240
277
  return items
241
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
+
242
305
  def get_item_by_text(self, text: str) -> ListItemType:
243
306
  """Get list item by text."""
244
307
  locator = self.base_item_locator.extend_query(
245
308
  extra_query=f"[contains(.,'{text}')]",
246
309
  )
247
- return self.item_class(page=self.page, base_locator=locator)
310
+ return self._item_class(page=self.page, base_locator=locator)
248
311
 
249
312
  def __repr__(self) -> str:
250
313
  return (
251
314
  "ListComponent("
252
315
  f"component={self.__class__}, "
253
- f"item_class={self.item_class}, "
316
+ f"item_class={self._item_class}, "
254
317
  f"base_item_locator={self.base_item_locator}, "
255
318
  f"count={self.count}, "
256
319
  f"items={self.all}, "
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "pomcorn"
3
- version = "0.7.5"
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