Repository: Stranger6667/pyanyapi

Issue:
Improve children syntax 
As we discussed =D

Add supporting for childrens parse for every parent element

Example settings: 
```python
class IMDBParser(HTMLParser):
    settings = {
        'films_blocks': {
            'base': '//div[@class="media"]',
            'children': {
                'title': '/h4',
                'link': '/span/a/@href'
            }
        }
    }
```
Example usage:
```
parsed_elements.film_blocks[0].title
```
or something like dictionary with childs in Element object.


Discussion:
codecov-io: ## [Current coverage][1] is `100.00%`
> Merging **#33** into **master** will not affect coverage as of [`13e366f`][3]


```diff
@@            master     #33   diff @@
======================================
  Files            7       7       
  Stmts          348     351     +3
  Branches        36      38     +2
  Methods          0       0       
======================================
+ Hit            348     351     +3
  Partial          0       0       
  Missed           0       0       
```

> Review entire [Coverage Diff][4] as of [`13e366f`][3]


[1]: https://codecov.io/github/Stranger6667/pyanyapi?ref=13e366f7c2915fc8f7f7e7c1396ac9772f6942e8
[2]: https://codecov.io/github/Stranger6667/pyanyapi/features/suggestions?ref=13e366f7c2915fc8f7f7e7c1396ac9772f6942e8
[3]: https://codecov.io/github/Stranger6667/pyanyapi/commit/13e366f7c2915fc8f7f7e7c1396ac9772f6942e8
[4]: https://codecov.io/github/Stranger6667/pyanyapi/compare/b1f0bc477bb850dc39c4646ff359e59d764a9f77...13e366f7c2915fc8f7f7e7c1396ac9772f6942e8

> Powered by [Codecov](https://codecov.io). Updated on successful CI builds.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 016a9b7..ecfdb61 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@ History
 ----------------
 
 * Fixed `lxml` installation on PyPy (#34).
+* Add support for subparsers (#32).
 
 0.5.3 - 30.10.2015
 ----------------
diff --git a/README.rst b/README.rst
index ad508c4..7600418 100644
--- a/README.rst
+++ b/README.rst
@@ -105,6 +105,36 @@ list. Here comes "base-children" setup style.
     >>> api.test
     ['123 ', ' 234']
 
+There is another option to interact with sub-elements. Sub parsers!
+
+.. code:: python
+
+    from pyanyapi import HTMLParser
+
+
+    class SubParser(HTMLParser):
+        settings = {
+            'href': 'string(//@href)',
+            'text': 'string(//text())'
+        }
+
+
+    class Parser(HTMLParser):
+        settings = {
+            'elem': {
+                'base': './/a',
+                'parser': SubParser
+            }
+        }
+
+    >>> api = Parser().parse('<html><body><a href='#test'>test</body></html>')
+    >>> api.elem[0].href
+    #test
+    >>> api.elem[0].text
+    test
+
+Also you can pass sub parsers as classes or like instances.
+
 Settings inheritance
 ~~~~~~~~~~~~~~~~~~~~
 
diff --git a/pyanyapi/interfaces.py b/pyanyapi/interfaces.py
index 121f684..53734a7 100644
--- a/pyanyapi/interfaces.py
+++ b/pyanyapi/interfaces.py
@@ -141,6 +141,12 @@ class XPathInterface(BaseInterface):
             child_query = settings.get('children')
             if child_query:
                 return [self.maybe_strip(''.join(element.xpath(child_query))) for element in result]
+            sub_parser = settings.get('parser')
+            if sub_parser:
+                return [
+                    (sub_parser() if callable(sub_parser) else sub_parser).parse(etree.tostring(element))
+                    for element in result
+                ]
             return result
 
         return self.parse(settings)


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
index 06f9542..d127796 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -4,7 +4,7 @@ import sys
 
 import pytest
 
-from pyanyapi import JSONParser, RegExpParser, CombinedParser, interface_property, interface_method
+from pyanyapi import JSONParser, RegExpParser, CombinedParser, interface_property, interface_method, HTMLParser
 
 
 class EmptyValuesParser(CombinedParser):
@@ -74,6 +74,13 @@ class ChildParser(ParentParser):
     }
 
 
+class SubParser(HTMLParser):
+    settings = {
+        'href': 'string(//@href)',
+        'text': 'string(//text())'
+    }
+
+
 class SimpleParser(RegExpParser):
     settings = {
         'test': '\d+.\d+',
diff --git a/tests/test_parsers.py b/tests/test_parsers.py
index e13a167..163b42c 100644
--- a/tests/test_parsers.py
+++ b/tests/test_parsers.py
@@ -4,12 +4,21 @@ import re
 import pytest
 
 from ._compat import patch
-from .conftest import ChildParser, SimpleParser, lxml_is_supported, lxml_is_not_supported
-from pyanyapi import XMLObjectifyParser, XMLParser, JSONParser, YAMLParser, RegExpParser, AJAXParser, CSVParser
+from .conftest import ChildParser, SubParser, SimpleParser, lxml_is_supported, lxml_is_not_supported
+from pyanyapi import (
+    XMLObjectifyParser,
+    XMLParser,
+    JSONParser,
+    YAMLParser,
+    RegExpParser,
+    AJAXParser,
+    CSVParser,
+    HTMLParser,
+)
 from pyanyapi.exceptions import ResponseParseError
 
 
-HTML_CONTENT = "<html><body><a href='#test'></body></html>"
+HTML_CONTENT = "<html><body><a href='#test'>test</body></html>"
 XML_CONTENT = '''<?xml version="1.0" encoding="UTF-8"?>
 <response>
 <id>32e9a4a2</id>
@@ -289,3 +298,21 @@ def test_csv_parser_error():
     parsed = CSVParser({'test': '1:1'}).parse(123)
     with pytest.raises(ResponseParseError):
         parsed.test
+
+
+@lxml_is_supported
+@pytest.mark.parametrize('sub_parser', (SubParser, SubParser()))
+def test_children(sub_parser):
+
+    class Parser(HTMLParser):
+        settings = {
+            'elem': {
+                'base': './/a',
+                'parser': sub_parser
+            }
+        }
+
+    api = Parser().parse(HTML_CONTENT)
+    sub_api = api.elem[0]
+    assert sub_api.href == '#test'
+    assert sub_api.text == 'test'