Skip to content
Commits on Source (3)
......@@ -5,16 +5,16 @@ A method can raise only its own exceptions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When you want to return an error, you **must** raise only your own exceptions defined in the capability module.
Never let Python raise his exceptions, for example ``KeyError`` if a parameter given to method isn't found in a local
Never let Python raise his exceptions, for example :py:exc:`KeyError` if a parameter given to method isn't found in a local
list.
Prefer returning objects
^^^^^^^^^^^^^^^^^^^^^^^^
Python is an object-oriented language, so when your capability supports entities (for example
:class:`weboob.capabilities.video.BaseVideo` with the :class:`weboob.capabilities.video.CapVideo` capability),
you have to create a class derived from :class:`weboob.capabilities.base.BaseObject`, and create an unique method
to get it (for example :func:`get_video() <weboob.capabilities.video.CapVideo.get_video>`), instead of several methods like
:class:`~weboob.capabilities.video.BaseVideo` with the :class:`~weboob.capabilities.video.CapVideo` capability),
you have to create a class derived from :py:class:`~weboob.capabilities.base.BaseObject`, and create an unique method
to get it (for example :func:`~weboob.capabilities.video.CapVideo.get_video`), instead of several methods like
``get_video_url()``, ``get_video_preview()``, etc.
An object has an unique ID.
......
......@@ -40,6 +40,17 @@ The easiest way to send your patch is to create a fork on `the Weboob Gitlab <ht
request from there. This way, the code review process is easier and continuous integration is run automatically (see
previous section).
.. note::
Using the ``git+ssh`` protocol to clone the repository will only work when connected from an
IPv6 compatible network:
`$ git clone gitlab@git.weboob.org:weboob/weboob.git`
But it is possible to use the ``git+https`` protocol from everywhere (IPv4 and IPv6 networks):
`$ git clone https://git.weboob.org/weboob/weboob.git`
If you prefer good old email patches, just use
::
......
......@@ -27,7 +27,7 @@ Will do::
foo=bar
It also works with :class:`weboob.browser.url.URL` instances::
It also works with :class:`~weboob.browser.url.URL` instances::
browser.myurl.go(data={'foo': 'bar'})
......@@ -98,8 +98,10 @@ Will do::
Have multiple pages on the same URL
-----------------------------------
Define a ``is_here`` attribute in all :class:`weboob.browser.page.Page` class which have the same URL.
Define a ``is_here`` attribute in all :class:`~weboob.browser.pages.Page` class which have the same URL.
The ``is_here`` attr must be a string XPath.
If the XPath is found in the document, the Page will be matched, else other Pages will be checked::
# in browser.py
......@@ -116,7 +118,7 @@ If the XPath is found in the document, the Page will be matched, else other Page
class PageB(HTMLPage):
is_here = '//div[text()="Here are the Bs"]'
If an XPath is not enough, `is_here` can be a method returning a bool::
If an XPath is not enough, ``is_here`` can be a method returning a bool::
class PageA(HTMLPage):
def is_here(self):
......@@ -125,7 +127,8 @@ If an XPath is not enough, `is_here` can be a method returning a bool::
Have a page which is sometimes a ``LoggedPage``, sometimes isn't
----------------------------------------------------------------
:class:`weboob.browser.pages.LoggedPage` just defines ``logged = True`` while other pages define ``logged = False`` by default.
:class:`~weboob.browser.pages.LoggedPage` just defines ``logged = True`` while other pages define ``logged = False`` by default.
To make this attribute variable, use a ``@property`` method returning a ``bool``::
class HomePage(HTMLPage):
......@@ -167,7 +170,7 @@ There are multiple ways to skip elements::
Fix invalid HTML that prevents lxml to be parsed
------------------------------------------------
When the document must be fixed before being parsed, :meth:`weboob.browser.pages.Page.build_doc` can be overridden::
When the document must be fixed before being parsed, :meth:`~weboob.browser.pages.Page.build_doc` can be overridden::
class MyPage(HTMLPage):
def build_doc(self, content):
......@@ -207,11 +210,11 @@ This example code isn't very semantic and could fail silently if columns are cha
obj_id = CleanText('./td[1]')
obj_foo = CleanText('./td[2]')
It can be improved by using the column labels::
It can be improved by using a :class:`~weboob.browser.elements.TableElement` and the column labels::
class MyPage(HTMLPage):
@method
class iter_stuff(ListElement):
class iter_stuff(TableElement):
head_xpath = '//table/tr/th' # where to look for column titles
# these are the column titles from the site
......@@ -231,7 +234,7 @@ Handle multiple tables with similar headers
-------------------------------------------
Sometimes, you might encounter a page with multiple tables to parse. The columns are titled the same, but they aren't at the same column index.
So, it's required to restart :class:`weboob.browser.elements.TableElement` column processing for each table. It's possible to encapsulate elements in other elements::
So, it's required to restart :class:`~weboob.browser.elements.TableElement` column processing for each table. It's possible to encapsulate elements in other elements::
class MultiPage(HTMLPage):
@method
......@@ -275,8 +278,8 @@ When going to next page requires making a ``POST``::
@method
class iter_stuff(ListElement):
def next_page(self):
if self.doc.get('next_page_params'):
return requests.Request('POST', self.page.url, data=self.doc.get('next_page_params'))
if self.page.doc.get('next_page_params'):
return requests.Request('POST', self.page.url, data=self.page.doc.get('next_page_params'))
item_xpath = 'items'
......@@ -335,15 +338,15 @@ Since the pages contain different attributes, info can be merged easily::
return stuff # foo and bar are set
This can also be useful for implementing ``fillobj``.
This can also be useful for implementing :func:`~weboob.tools.backend.Module.fillobj`.
Unfortunately, this doesn't work when multiple objects are parsed (for example, in a ``ListElement``).
Unfortunately, this doesn't work when multiple objects are parsed (for example, in a :class:`~weboob.browser.elements.ListElement`).
In this case, manual merging is required, and linking objects from each page.
Use ``ItemElement`` with non-scalar attributes
----------------------------------------------
Some ``BaseObject`` subclasses have fields of other ``BaseObject`` types, for example::
Some :class:`~weboob.capabilities.base.BaseObject` subclasses have fields of other :class:`~weboob.capabilities.base.BaseObject` types, for example::
class Foo(BaseObject):
name = StringField('name')
......@@ -353,7 +356,7 @@ Some ``BaseObject`` subclasses have fields of other ``BaseObject`` types, for ex
foo = Field('foo', Foo)
multiple = Field('multiple foo objects', list)
They may still be parsed with ``ItemElement``::
They may still be parsed with :class:`~weboob.browser.elements.ItemElement`::
class item(ItemElement):
klass = Bar
......@@ -365,7 +368,7 @@ They may still be parsed with ``ItemElement``::
obj_name = CleanText('span')
This also works for ``ListElement``::
This also works for :class:`~weboob.browser.elements.ListElement`::
class item(ItemElement):
klass = Bar
......
This diff is collapsed.
......@@ -37,7 +37,7 @@ Write a test case
Normal test
-----------
Use the class :class:`TestCase <weboob.tools.test.TestCase>` to derivate it into your new test case. Then, write methods which name starts with ``test_``.
Use the class :class:`~weboob.tools.test.TestCase` to derivate it into your new test case. Then, write methods which name starts with ``test_``.
A test fails when an assertion error is raised. Also, when an other kind of exception is raised, this is an error.
......@@ -46,7 +46,7 @@ You can use ``assert`` to check something, or the base methods ``assertTrue``, `
Backend test
------------
Create a class derivated from :class:`BackendTest <weboob.tools.test.BackendTest>`, and set the ``BACKEND`` class attribute to the name of the backend to test.
Create a class derivated from :class:`~weboob.tools.test.BackendTest`, and set the ``BACKEND`` class attribute to the name of the backend to test.
Then, in your test methods, the ``backend`` attribute will contain the loaded backend. When the class is instancied, it loads every configured backends of the right type, and randomly choose one.
If no one is found, the tests are skipped.
......