Skip to content

Commit 5eed2dc

Browse files
committed
create new export() method in PageObjectRegistry
1 parent 8e805a7 commit 5eed2dc

File tree

21 files changed

+203
-6
lines changed

21 files changed

+203
-6
lines changed

docs/intro/pop.rst

+37-6
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ by simply importing them:
118118
page = FurnitureProductPage(response)
119119
item = page.to_item()
120120
121+
.. _`pop-recommended-requirements`:
122+
121123
Recommended Requirements
122124
~~~~~~~~~~~~~~~~~~~~~~~~
123125

@@ -143,13 +145,15 @@ inside of ``ecommerce-page-objects/ecommerce_page_objects/__init__.py``:
143145

144146
.. code-block:: python
145147
146-
from web_poet import default_registry, consume_modules
148+
from web_poet import default_registry
147149
148-
# This allows all of the OverrideRules declared inside the package
149-
# using @handle_urls to be properly discovered and loaded.
150-
consume_modules(__package__)
150+
REGISTRY = default_registry.export(__package__)
151151
152-
REGISTRY = default_registry
152+
The :meth:`~.PageObjectRegistry.export` method returns a new instance of
153+
:class:`~.PageObjectRegistry` which contains only the :class:`~.OverrideRule`
154+
from the given package. This means that if there are other **POPs** using the
155+
recommended ``default_registry``, any :class:`~.OverrideRule` that are not part
156+
of the packages are not included.
153157

154158
This allows any developer using a **POP** to easily access all of the
155159
:class:`~.OverrideRule` using the convention of accessing it via the
@@ -173,8 +177,35 @@ This allows any developer using a **POP** to easily access all of the
173177

174178
However, it is **recommended** to use the instances of
175179
:class:`~.PageObjectRegistry` to leverage the validation logic for its
176-
contents.
180+
contents, as well as its other functionalities.
181+
182+
Lastly, when trying to repackage multiple **POPs** into a single unifying **POP**
183+
which contains all of the :class:`~.OverrideRule`, it can easily be packaged
184+
as:
185+
186+
.. code-block:: python
187+
188+
from web_poet import PageObjectRegistry
189+
190+
import base_A_package
191+
import base_B_package
192+
193+
# If on Python 3.9+
194+
combined_rules = base_A_package.REGISTRY | base_B_package.REGISTRY
195+
196+
# If on lower Python versions
197+
combined_rules = {**base_A_package.REGISTRY, **base_B_package.REGISTRY}
198+
199+
REGISTRY = PageObjectRegistry(combined_rules)
200+
201+
Note that you can also opt to use only a subset of the :class:`~.OverrideRule`
202+
by selecting the specific ones in ``combined_rules`` before creating a new
203+
:class:`~.PageObjectRegistry` instance. An **inclusion** rule is preferred than
204+
an **exclusion** rule (see **Tip #4** in the :ref:`conventions-and-best-practices`).
205+
You can use :meth:`~.PageObjectRegistry.search_overrides` when selecting the
206+
rules.
177207

208+
.. _`conventions-and-best-practices`:
178209

179210
Conventions and Best Practices
180211
------------------------------

tests/test_pop.py

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
"""This tests the expected behavior of importing multiple different POPs such
2+
that they don't leak the OverrideRules from other POPs that use the same
3+
``default_registry``.
4+
5+
Packacking and exporting a given POP should be resilient from such cases.
6+
7+
In particular, this tests the :meth:`PageObjectRegistry.export` functionality.
8+
"""
9+
10+
def test_base_A():
11+
from tests_pop import base_A_package
12+
13+
reg = base_A_package.REGISTRY
14+
15+
assert len(reg) == 2
16+
assert base_A_package.site_1.A_Site1 in reg
17+
assert base_A_package.site_2.A_Site2 in reg
18+
19+
20+
def test_base_B():
21+
from tests_pop import base_B_package
22+
23+
reg = base_B_package.REGISTRY
24+
25+
assert len(reg) == 2
26+
assert base_B_package.site_2.B_Site2 in reg
27+
assert base_B_package.site_3.B_Site3 in reg
28+
29+
30+
def test_improved_A():
31+
from tests_pop import improved_A_package, base_A_package
32+
33+
reg = improved_A_package.REGISTRY
34+
35+
assert len(reg) == 3
36+
assert improved_A_package.site_1.A_Improved_Site1 in reg
37+
assert improved_A_package.base_A_package.site_1.A_Site1 in reg
38+
assert improved_A_package.base_A_package.site_2.A_Site2 in reg
39+
40+
41+
def test_combine_A_B():
42+
from tests_pop import combine_A_B_package, base_A_package, base_B_package
43+
44+
reg = combine_A_B_package.REGISTRY
45+
46+
assert len(reg) == 4
47+
assert combine_A_B_package.base_A_package.site_1.A_Site1 in reg
48+
assert combine_A_B_package.base_A_package.site_2.A_Site2 in reg
49+
assert combine_A_B_package.base_B_package.site_2.B_Site2 in reg
50+
assert combine_A_B_package.base_B_package.site_3.B_Site3 in reg
51+
52+
53+
def test_combine_A_B_subset():
54+
from tests_pop import combine_A_B_subset_package, improved_A_package, base_B_package
55+
56+
reg = combine_A_B_subset_package.REGISTRY
57+
58+
assert len(reg) == 2
59+
assert combine_A_B_subset_package.improved_A_package.site_1.A_Improved_Site1 in reg
60+
assert combine_A_B_subset_package.base_B_package.site_3.B_Site3 in reg

tests_pop/__init__.py

Whitespace-only changes.

tests_pop/base_A_package/__init__.py

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from web_poet import default_registry
2+
3+
REGISTRY = default_registry.export(__package__)

tests_pop/base_A_package/base.py

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
class BasePage:
2+
...

tests_pop/base_A_package/site_1.py

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from web_poet import handle_urls
2+
3+
from .base import BasePage
4+
5+
6+
@handle_urls("site_1.com", overrides=BasePage)
7+
class A_Site1:
8+
...

tests_pop/base_A_package/site_2.py

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from web_poet import handle_urls
2+
3+
from .base import BasePage
4+
5+
6+
@handle_urls("site_2.com", overrides=BasePage)
7+
class A_Site2:
8+
...

tests_pop/base_B_package/__init__.py

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from web_poet import default_registry
2+
3+
REGISTRY = default_registry.export(__package__)

tests_pop/base_B_package/base.py

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
class BasePage:
2+
...

tests_pop/base_B_package/site_2.py

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from web_poet import handle_urls
2+
3+
from .base import BasePage
4+
5+
6+
@handle_urls("site_2.com", overrides=BasePage)
7+
class B_Site2:
8+
...

tests_pop/base_B_package/site_3.py

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from web_poet import handle_urls
2+
3+
from .base import BasePage
4+
5+
6+
@handle_urls("site_3.com", overrides=BasePage)
7+
class B_Site3:
8+
...
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
"""This POP simply wants to repackage POP "A" and "B" into one unifying package."""
2+
3+
from web_poet import PageObjectRegistry
4+
5+
from . import base_A_package
6+
from . import base_B_package
7+
8+
combined = {**base_A_package.REGISTRY, **base_B_package.REGISTRY}
9+
REGISTRY = PageObjectRegistry(combined)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../base_A_package
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../base_B_package
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"""This POP simply wants to repackage POP "A" and "B" into one unifying package."""
2+
3+
from web_poet import PageObjectRegistry
4+
5+
from . import improved_A_package
6+
from . import base_B_package
7+
8+
rules_A_improved = improved_A_package.REGISTRY.search_overrides(
9+
use=improved_A_package.site_1.A_Improved_Site1 # type:ignore
10+
)
11+
rules_B = base_B_package.REGISTRY.search_overrides(
12+
use=base_B_package.site_3.B_Site3 # type: ignore
13+
)
14+
15+
combined_rules = rules_A_improved + rules_B
16+
REGISTRY = PageObjectRegistry.from_override_rules(combined_rules)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../base_B_package
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../improved_A_package
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from web_poet import default_registry
2+
3+
REGISTRY = default_registry.export(__package__)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../base_A_package
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from web_poet import handle_urls
2+
3+
from .base_A_package.base import BasePage
4+
from .base_A_package.site_1 import A_Site1
5+
6+
7+
@handle_urls("site_1.com", overrides=BasePage)
8+
class A_Improved_Site1(A_Site1):
9+
... # some improvements here after subclassing the original one.

web_poet/overrides.py

+22
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import importlib.util
55
import warnings
66
import pkgutil
7+
from copy import deepcopy
78
from collections import deque
89
from dataclasses import dataclass, field
910
from operator import attrgetter
@@ -121,6 +122,27 @@ def from_override_rules(
121122
"""
122123
return cls({rule.use: rule for rule in rules})
123124

125+
def export(self, package: str) -> PageObjectRegistry:
126+
"""Returns a new :class:`~.PageObjectRegistry` instance containing
127+
:class:`~.OverrideRule` which are only found in the provided **package**.
128+
129+
This is used in cases wherein all of the :class:`~.OverrideRule` in a
130+
given **Page Object Project (POP)** should be placed inside a dedicated
131+
registry for packaging. See :ref:`POP Recommended Requirements
132+
<pop-recommended-requirements>` for more info about this.
133+
134+
Note that the :func:`~.consume_modules` will be called on the said
135+
**package** which adds any undiscovered :class:`~.OverrideRule` to the
136+
original :class:`~.PageObjectRegistry` instance. There's no need to worry
137+
about unrelated rules from being added since it wouldn't happen if the
138+
given registry's ``@handle_urls`` annotation wasn't the one used.
139+
"""
140+
backup_state = deepcopy(self)
141+
self.clear()
142+
rules = self.get_overrides(consume=package)
143+
self = backup_state
144+
return self.from_override_rules(rules)
145+
124146
def handle_urls(
125147
self,
126148
include: Strings,

0 commit comments

Comments
 (0)