-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
web_poet.page_object decorator #44
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
from typing import Callable, Type | ||
|
||
from web_poet.pages import ItemPage | ||
|
||
|
||
def page_object(func: Callable) -> Type[ItemPage]: | ||
""" | ||
Decorator to create a Page Object from a function. | ||
Note that the original function is destroyed, and | ||
a class is created instead. | ||
|
||
.. code-block:: python | ||
|
||
@web_poet.page_object | ||
def MyPage(resp: HttpResponse): | ||
return {"title": resp.css("title::text").get()} | ||
|
||
is a shortcut for | ||
|
||
.. code-block:: python | ||
|
||
class MyPage(web_poet.ItemPage): | ||
def __init__(self, resp: HttpResponse): | ||
self.resp = resp | ||
|
||
def to_item(self): | ||
return {"title": self.resp.css("title::text").get()} | ||
|
||
""" | ||
class PageObject(ItemPage): | ||
def __init__(self, *args, **kwargs): | ||
# FIXME: save arguments as properly named attributes | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you elaborate? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is optional, to keep the generated class as close as possible to the hand-written class. See example above - MyPage has self.resp attribute. |
||
# FIXME: __init__ method doesn't fail if parameters are wrong | ||
self.args = args | ||
self.kwargs = kwargs | ||
super().__init__() | ||
|
||
# TODO: async def support | ||
def to_item(self): | ||
return func(*self.args, **self.kwargs) | ||
|
||
# FIXME: robustness, edge cases | ||
PageObject.__module__ = func.__module__ | ||
PageObject.__name__ = func.__name__ | ||
PageObject.__qualname__ = func.__qualname__ | ||
PageObject.__doc__ = func.__doc__ | ||
PageObject.__init__.__annotations__ = func.__annotations__ | ||
|
||
# TODO: check that type annotations work properly | ||
# TODO: preserve the original function as an attribute? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it to keep the implementation closer to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was thinking more about this: if you decorate function, you can't use it anymore, it becomes a class with a completely different API; this is weird. https://github.com/scrapinghub/web-poet/pull/44/files#r882977954 could be another way to address it. |
||
return PageObject | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
alternative syntax, which doesn't destroy the function: