mops 3.5.2__tar.gz → 3.5.3__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.
Files changed (54) hide show
  1. {mops-3.5.2 → mops-3.5.3}/PKG-INFO +2 -2
  2. {mops-3.5.2 → mops-3.5.3}/README.md +1 -1
  3. {mops-3.5.2 → mops-3.5.3}/mops/__init__.py +1 -1
  4. {mops-3.5.2 → mops-3.5.3}/mops/base/driver_wrapper.py +25 -3
  5. {mops-3.5.2 → mops-3.5.3}/mops/base/element.py +2 -2
  6. {mops-3.5.2 → mops-3.5.3}/mops/base/page.py +1 -1
  7. {mops-3.5.2 → mops-3.5.3}/mops/mixins/internal_mixin.py +41 -13
  8. {mops-3.5.2 → mops-3.5.3}/mops.egg-info/PKG-INFO +2 -2
  9. {mops-3.5.2 → mops-3.5.3}/mops/abstraction/driver_wrapper_abc.py +0 -0
  10. {mops-3.5.2 → mops-3.5.3}/mops/abstraction/element_abc.py +0 -0
  11. {mops-3.5.2 → mops-3.5.3}/mops/abstraction/mixin_abc.py +0 -0
  12. {mops-3.5.2 → mops-3.5.3}/mops/abstraction/page_abc.py +0 -0
  13. {mops-3.5.2 → mops-3.5.3}/mops/base/group.py +0 -0
  14. {mops-3.5.2 → mops-3.5.3}/mops/exceptions.py +0 -0
  15. {mops-3.5.2 → mops-3.5.3}/mops/js_scripts.py +0 -0
  16. {mops-3.5.2 → mops-3.5.3}/mops/keyboard_keys.py +0 -0
  17. {mops-3.5.2 → mops-3.5.3}/mops/mixins/capabilities.py +0 -0
  18. {mops-3.5.2 → mops-3.5.3}/mops/mixins/driver_mixin.py +0 -0
  19. {mops-3.5.2 → mops-3.5.3}/mops/mixins/native_context.py +0 -0
  20. {mops-3.5.2 → mops-3.5.3}/mops/mixins/objects/box.py +0 -0
  21. {mops-3.5.2 → mops-3.5.3}/mops/mixins/objects/driver.py +0 -0
  22. {mops-3.5.2 → mops-3.5.3}/mops/mixins/objects/location.py +0 -0
  23. {mops-3.5.2 → mops-3.5.3}/mops/mixins/objects/locator.py +0 -0
  24. {mops-3.5.2 → mops-3.5.3}/mops/mixins/objects/locator_type.py +0 -0
  25. {mops-3.5.2 → mops-3.5.3}/mops/mixins/objects/scrolls.py +0 -0
  26. {mops-3.5.2 → mops-3.5.3}/mops/mixins/objects/size.py +0 -0
  27. {mops-3.5.2 → mops-3.5.3}/mops/mixins/objects/visual_comaprison_mixin.py +0 -0
  28. {mops-3.5.2 → mops-3.5.3}/mops/mixins/objects/wait_result.py +0 -0
  29. {mops-3.5.2 → mops-3.5.3}/mops/playwright/play_driver.py +0 -0
  30. {mops-3.5.2 → mops-3.5.3}/mops/playwright/play_element.py +0 -0
  31. {mops-3.5.2 → mops-3.5.3}/mops/playwright/play_page.py +0 -0
  32. {mops-3.5.2 → mops-3.5.3}/mops/selenium/core/core_driver.py +0 -0
  33. {mops-3.5.2 → mops-3.5.3}/mops/selenium/core/core_element.py +0 -0
  34. {mops-3.5.2 → mops-3.5.3}/mops/selenium/core/core_page.py +0 -0
  35. {mops-3.5.2 → mops-3.5.3}/mops/selenium/driver/mobile_driver.py +0 -0
  36. {mops-3.5.2 → mops-3.5.3}/mops/selenium/driver/web_driver.py +0 -0
  37. {mops-3.5.2 → mops-3.5.3}/mops/selenium/elements/mobile_element.py +0 -0
  38. {mops-3.5.2 → mops-3.5.3}/mops/selenium/elements/web_element.py +0 -0
  39. {mops-3.5.2 → mops-3.5.3}/mops/selenium/pages/mobile_page.py +0 -0
  40. {mops-3.5.2 → mops-3.5.3}/mops/selenium/pages/web_page.py +0 -0
  41. {mops-3.5.2 → mops-3.5.3}/mops/selenium/sel_utils.py +0 -0
  42. {mops-3.5.2 → mops-3.5.3}/mops/shared_utils.py +0 -0
  43. {mops-3.5.2 → mops-3.5.3}/mops/utils/decorators.py +0 -0
  44. {mops-3.5.2 → mops-3.5.3}/mops/utils/internal_utils.py +0 -0
  45. {mops-3.5.2 → mops-3.5.3}/mops/utils/logs.py +0 -0
  46. {mops-3.5.2 → mops-3.5.3}/mops/utils/previous_object_driver.py +0 -0
  47. {mops-3.5.2 → mops-3.5.3}/mops/utils/selector_synchronizer.py +0 -0
  48. {mops-3.5.2 → mops-3.5.3}/mops/visual_comparison.py +0 -0
  49. {mops-3.5.2 → mops-3.5.3}/mops.egg-info/SOURCES.txt +0 -0
  50. {mops-3.5.2 → mops-3.5.3}/mops.egg-info/dependency_links.txt +0 -0
  51. {mops-3.5.2 → mops-3.5.3}/mops.egg-info/requires.txt +0 -0
  52. {mops-3.5.2 → mops-3.5.3}/mops.egg-info/top_level.txt +0 -0
  53. {mops-3.5.2 → mops-3.5.3}/pyproject.toml +0 -0
  54. {mops-3.5.2 → mops-3.5.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mops
3
- Version: 3.5.2
3
+ Version: 3.5.3
4
4
  Summary: Wrapper of Selenium, Appium and Playwright with single API
5
5
  Author-email: Podolian Vladimir <vladimir.podolyan64@gmail.com>
6
6
  License: MIT
@@ -44,7 +44,7 @@ Requires-Dist: Pillow>=12.1.0
44
44
  </p>
45
45
 
46
46
  <p align="center">
47
- <a href="https://mops.readthedocs.io"><img alt="Documentation Status" src="https://img.shields.io/readthedocs/mops?logo=readthedocs&labelColor=%232E353B&label=docs"></a>
47
+ <a href="https://mops.readthedocs.io"><img alt="Documentation Status" src="https://readthedocs.org/projects/mops/badge/?version=latest"></a>
48
48
  <a href="https://github.com/CustomEnv/mops/actions/workflows/static_tests.yml"><img alt="GitHub Actions Workflow Status" src="https://img.shields.io/github/actions/workflow/status/CustomEnv/mops/static_tests.yml?branch=master&logo=github&label=Unit%20Tests&labelColor=%232E353B"></a>
49
49
  <a href="https://github.com/CustomEnv/mops/actions/workflows/playwright_tests.yml"><img alt="GitHub Actions Workflow Status" src="https://img.shields.io/github/actions/workflow/status/CustomEnv/mops/playwright_tests.yml?branch=master&logo=github&label=Playwright%20Tests&labelColor=%232E353B"></a>
50
50
  </p>
@@ -11,7 +11,7 @@
11
11
  </p>
12
12
 
13
13
  <p align="center">
14
- <a href="https://mops.readthedocs.io"><img alt="Documentation Status" src="https://img.shields.io/readthedocs/mops?logo=readthedocs&labelColor=%232E353B&label=docs"></a>
14
+ <a href="https://mops.readthedocs.io"><img alt="Documentation Status" src="https://readthedocs.org/projects/mops/badge/?version=latest"></a>
15
15
  <a href="https://github.com/CustomEnv/mops/actions/workflows/static_tests.yml"><img alt="GitHub Actions Workflow Status" src="https://img.shields.io/github/actions/workflow/status/CustomEnv/mops/static_tests.yml?branch=master&logo=github&label=Unit%20Tests&labelColor=%232E353B"></a>
16
16
  <a href="https://github.com/CustomEnv/mops/actions/workflows/playwright_tests.yml"><img alt="GitHub Actions Workflow Status" src="https://img.shields.io/github/actions/workflow/status/CustomEnv/mops/playwright_tests.yml?branch=master&logo=github&label=Playwright%20Tests&labelColor=%232E353B"></a>
17
17
  </p>
@@ -1,4 +1,4 @@
1
1
  """Wrapper of Selenium, Appium and Playwright with a single API."""
2
2
 
3
- __version__ = '3.5.2'
3
+ __version__ = '3.5.3'
4
4
  __project_name__ = 'mops'
@@ -15,8 +15,6 @@ from mops.abstraction.driver_wrapper_abc import DriverWrapperABC
15
15
  from mops.exceptions import DriverWrapperException
16
16
  from mops.js_scripts import storage_get_items_js, storage_set_item_js
17
17
  from mops.mixins.internal_mixin import InternalMixin
18
- from mops.mixins.objects.box import Box
19
- from mops.mixins.objects.driver import Driver
20
18
  from mops.mixins.objects.visual_comaprison_mixin import hide_before_screenshot, reveal_after_screenshot
21
19
  from mops.playwright.play_driver import PlayDriver
22
20
  from mops.selenium.driver.mobile_driver import MobileDriver
@@ -37,6 +35,7 @@ if TYPE_CHECKING:
37
35
 
38
36
  class DriverWrapperSessions:
39
37
  all_sessions: ClassVar[list[DriverWrapper]] = []
38
+ _MIN_SESSIONS_FOR_COMPARISON: ClassVar[int] = 2
40
39
 
41
40
  @classmethod
42
41
  def add_session(cls, driver_wrapper: DriverWrapper) -> None:
@@ -87,6 +86,29 @@ class DriverWrapperSessions:
87
86
  """
88
87
  return any(cls.all_sessions)
89
88
 
89
+ @classmethod
90
+ def has_different_driver_types(cls) -> bool:
91
+ """
92
+ Check if the session pool contains different driver types
93
+ (e.g., Selenium + Appium, Selenium + Playwright).
94
+
95
+ :return: :obj:`bool` - :obj:`True` if at least two sessions use different driver types,
96
+ otherwise :obj:`False`.
97
+ """
98
+ if len(cls.all_sessions) < cls._MIN_SESSIONS_FOR_COMPARISON:
99
+ return False
100
+
101
+ driver_types = set()
102
+ for session in cls.all_sessions:
103
+ if session.is_playwright:
104
+ driver_types.add('playwright')
105
+ elif session.is_appium:
106
+ driver_types.add('appium')
107
+ elif session.is_selenium:
108
+ driver_types.add('selenium')
109
+
110
+ return len(driver_types) > 1
111
+
90
112
 
91
113
  class DriverWrapper(InternalMixin, Logging, DriverWrapperABC):
92
114
  """
@@ -478,7 +500,7 @@ class DriverWrapper(InternalMixin, Logging, DriverWrapperABC):
478
500
  )
479
501
  raise DriverWrapperException(msg)
480
502
 
481
- self._set_static(self._base_cls)
503
+ self._set_static(self._base_cls, with_shadow=False)
482
504
  self._base_cls.__init__(self, driver_container=self.__driver_container)
483
505
 
484
506
  for name, value in self.__dict__.items():
@@ -66,7 +66,7 @@ class ElementMeta(ABCMeta):
66
66
  @functools.wraps(orig_init)
67
67
  def wrapped_init(self: Any, *args: Any, **kw: Any) -> None:
68
68
  orig_init(self, *args, **kw)
69
- if type(self) is cls and getattr(self, '_initialized', False):
69
+ if getattr(self, '_initialized', False):
70
70
  self._modify_sub_elements()
71
71
 
72
72
  cls.__init__ = wrapped_init
@@ -181,7 +181,7 @@ class Element(DriverMixin, InternalMixin, Logging, ElementABC, metaclass=Element
181
181
  )
182
182
  raise DriverWrapperException(msg)
183
183
 
184
- self._set_static(self._base_cls)
184
+ self._set_static(self._base_cls, with_shadow=True)
185
185
  self._base_cls.__init__(self)
186
186
  self._initialized = True
187
187
 
@@ -108,7 +108,7 @@ class Page(DriverMixin, InternalMixin, Logging, PageABC):
108
108
  )
109
109
  raise DriverWrapperException(msg)
110
110
 
111
- self._set_static(self._base_cls)
111
+ self._set_static(self._base_cls, with_shadow=True)
112
112
  self._base_cls.__init__(self)
113
113
 
114
114
  @cached_property
@@ -6,9 +6,11 @@ from typing import Any
6
6
  from mops.utils.internal_utils import (
7
7
  extract_all_named_objects,
8
8
  extract_named_objects,
9
- is_driver_wrapper,
10
9
  )
11
10
 
11
+ _shadow_classes: dict[tuple[type, str], type] = {}
12
+ _class_configured: dict[type, type] = {}
13
+
12
14
 
13
15
  def get_element_info(element: Any, label: str = 'Selector=') -> str:
14
16
  """
@@ -47,6 +49,7 @@ def get_driver_instance(driver_type: type, instance: type) -> bool:
47
49
 
48
50
  class InternalMixin:
49
51
  driver: None
52
+ driver_wrapper: None
50
53
 
51
54
  def _driver_is_instance(self, instance: type) -> bool:
52
55
  """Check if the current driver is an instance of the given type."""
@@ -56,33 +59,58 @@ class InternalMixin:
56
59
  if not hasattr(self, var):
57
60
  setattr(self, var, value)
58
61
 
59
- def _get_protected_attrs(self: Any, current_obj_cls: type) -> set:
60
- if not is_driver_wrapper(self):
61
- return set(get_all_static_attributes(current_obj_cls))
62
-
62
+ def _get_protected_attrs(self: Any, current_obj_cls: type) -> frozenset:
63
63
  if '_framework_attrs' not in current_obj_cls.__dict__:
64
- current_obj_cls._framework_attrs = set(get_all_static_attributes(current_obj_cls))
64
+ current_obj_cls._framework_attrs = frozenset(get_all_static_attributes(current_obj_cls))
65
65
 
66
66
  return current_obj_cls.__dict__['_framework_attrs']
67
67
 
68
- def _set_static(self: Any, cls: type) -> None:
68
+ def _set_static(self: Any, cls: type, with_shadow: bool) -> None:
69
69
  """
70
- Set static from base cls (Web/Mobile/Play Element/Page etc.)
70
+ Set attributes from base cls onto the class. Uses per-driver shadow
71
+ classes when multiple driver types are active.
71
72
 
72
73
  :return: None
73
74
  """
74
- current_obj_cls = self.__class__
75
+ obj_cls = self.__class__
75
76
 
76
- if current_obj_cls.__dict__.get('_configured') is cls:
77
+ if _class_configured.get(obj_cls) is cls:
77
78
  return
78
79
 
79
- protected = self._get_protected_attrs(current_obj_cls)
80
+ protected = self._get_protected_attrs(obj_cls)
81
+
82
+ if with_shadow and self.driver_wrapper.session.has_different_driver_types():
83
+ obj_cls = self._set_shadow_class(protected)
80
84
 
81
85
  for name, value in get_static_attributes(cls).items():
82
86
  if name not in protected:
83
- setattr(current_obj_cls, name, value)
87
+ setattr(obj_cls, name, value)
88
+
89
+ _class_configured[obj_cls] = cls
84
90
 
85
- current_obj_cls._configured = cls
91
+ def _set_shadow_class(self, protected: frozenset) -> type:
92
+ """
93
+ Create or reuse a per-driver shadow subclass. The given *protected*
94
+ set was computed from the original class before any attributes were
95
+ injected, so the shadow class starts with the same baseline.
96
+
97
+ :param protected: pre-computed _framework_attrs of the original class.
98
+ :return: the shadow class (a subtype of the original class).
99
+ """
100
+ original_cls = self.__class__
101
+ key = (original_cls, self.driver_wrapper._base_cls.__name__)
102
+ obj_cls = _shadow_classes.get(key)
103
+ if not obj_cls:
104
+ obj_cls = type(
105
+ original_cls.__name__,
106
+ (original_cls,),
107
+ {'_shadow_class': True, '_framework_attrs': protected},
108
+ )
109
+ _shadow_classes[key] = obj_cls
110
+
111
+ self.__class__ = obj_cls
112
+
113
+ return obj_cls
86
114
 
87
115
  def _repr_builder(self: Any) -> str | None:
88
116
  class_name = self.__class__.__name__
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mops
3
- Version: 3.5.2
3
+ Version: 3.5.3
4
4
  Summary: Wrapper of Selenium, Appium and Playwright with single API
5
5
  Author-email: Podolian Vladimir <vladimir.podolyan64@gmail.com>
6
6
  License: MIT
@@ -44,7 +44,7 @@ Requires-Dist: Pillow>=12.1.0
44
44
  </p>
45
45
 
46
46
  <p align="center">
47
- <a href="https://mops.readthedocs.io"><img alt="Documentation Status" src="https://img.shields.io/readthedocs/mops?logo=readthedocs&labelColor=%232E353B&label=docs"></a>
47
+ <a href="https://mops.readthedocs.io"><img alt="Documentation Status" src="https://readthedocs.org/projects/mops/badge/?version=latest"></a>
48
48
  <a href="https://github.com/CustomEnv/mops/actions/workflows/static_tests.yml"><img alt="GitHub Actions Workflow Status" src="https://img.shields.io/github/actions/workflow/status/CustomEnv/mops/static_tests.yml?branch=master&logo=github&label=Unit%20Tests&labelColor=%232E353B"></a>
49
49
  <a href="https://github.com/CustomEnv/mops/actions/workflows/playwright_tests.yml"><img alt="GitHub Actions Workflow Status" src="https://img.shields.io/github/actions/workflow/status/CustomEnv/mops/playwright_tests.yml?branch=master&logo=github&label=Playwright%20Tests&labelColor=%232E353B"></a>
50
50
  </p>
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes