# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['xpath_helper']

package_data = \
{'': ['*']}

setup_kwargs = {
    'name': 'xpath-helper',
    'version': '0.1.1',
    'description': 'A simple and chainnable API to build complicated XPath queries without the hassle.',
    'long_description': '<p align="center">\n  <img src="https://raw.githubusercontent.com/jrebecchi/xpath-helper/main/docs/_static/logo-with-text.png" height="100px" alt="xpath-helper"/>\n</p>\n<p align="center">\n  <i>Build complicated XPath queries without the hassle - JavaScript & Python</i><br/><br/>\n  <a href="https://jrebecchi.github.io/xpath-helper/python/index.html">\n    <img src="https://img.shields.io/badge/docs-master-blue.svg?style=flat">\n  </a>\n  <a href="https://codecov.io/gh/jrebecchi/xpath-helper">\n    <img src="https://codecov.io/gh/jrebecchi/xpath-helper/branch/main/graph/badge.svg?token=0TJOZ64X10"/>\n  </a>\n  <a href="https://github.com/jrebecchi/xpath-helper/actions/workflows/python-ci.yml">\n    <img src="https://github.com/jrebecchi/xpath-helper/actions/workflows/python-ci.yml/badge.svg">\n  </a>\n</p>\n\nA chainable API to build complex XPath queries along the different [XPath axes](https://jrebecchi.github.io/xpath-helper/xpath-axes.html). Available both in Python and JavaScript.\n\n- [**Documentation**](https://jrebecchi.github.io/xpath-helper/python/index.html) — Consult the quick start guide and the online documentation.\n\n## Installation\n`xpath-helper` requires python 3.5+ and can be installed using pip:\n```bash\npip install xpath-helper\n```\n## Quick-start\nYou can chain method call on the different [XPath axes](https://jrebecchi.github.io/xpath-helper/xpath-axes.html) and easily add filters.\n```python\nfrom xpath_helper import xh, filter\n\n# Find a paragraph <p> containing a CSS class \'very-nice-p\'\np = xh.get_element_by_tag(\'p\', filter.attribute_contains(\'class\', \'very-nice-p\'))\nstr(p) # "//p[contains(@class, \'very-nice-p\')]"\n\n# Find the paragraph that is following the above one\nnext_p = p.get_following_sibling_by_tag(\'p\')\nstr(next_p) # "//p[contains(@class, \'very-nice-p\')]/following-sibling::p"\n\n# Find the modal containing a button with text "Register" \nmodal = xh.get_element(filter.value_equals(\'Register\')).get_ancestor(filter.attribute_equals(\'class\', \'modal\'))\nstr(modal) # "//*[text() = \'Register\']/ancestor::*[@class=\'modal\']"\n\n# An elaborated filter with a boolean expression\nli = xh.get_element_by_tag("li", filter.and_operator(\n  filter.or_operator(\n    filter.value_contains("JavaScript"), filter.value_contains("Python")\n  ),\n  filter.has_attribute("data-description")\n))\nstr(li) # "//li[((text()[contains(., \'JavaScript\')] or text()[contains(., \'Python\')]) and @data-description)]"\n```\n\n## Chaining\n\nXPath natively lets your build complex queries chaining them along its different axes. Read this [article to understand the different XPath axes](https://jrebecchi.github.io/xpath-helper/xpath-axes.html).\n\nThis library let you do exactly the same by chaining method calls along the different axes: [`descendant`](https://jrebecchi.github.io/xpath-helper/python/api.html#xpath_helper.XPathHelper.get_descendant) aliased as [`element`](https://jrebecchi.github.io/xpath-helper/python/api.html#xpath_helper.XPathHelper.get_element), [`descendant-or-self`](https://jrebecchi.github.io/xpath-helper/python/api.html#xpath_helper.XPathHelper.get_descendant_or_self), [`child`](https://jrebecchi.github.io/xpath-helper/python/api.html#xpath_helper.XPathHelper.get_child), [`parent`](https://jrebecchi.github.io/xpath-helper/python/api.html#xpath_helper.XPathHelper.get_parent), [`ancestor`](https://jrebecchi.github.io/xpath-helper/python/api.html#xpath_helper.XPathHelper.get_ancestor), [`ancestor-or-self`](https://jrebecchi.github.io/xpath-helper/python/api.html#xpath_helper.XPathHelper.get_ancestor_or_self), [`preceding`](https://jrebecchi.github.io/xpath-helper/python/api.html#xpath_helper.XPathHelper.get_preceding), [`preceding-sibling`](https://jrebecchi.github.io/xpath-helper/python/api.html#xpath_helper.XPathHelper.get_preceding_sibling), [`following`](https://jrebecchi.github.io/xpath-helper/python/api.html#xpath_helper.XPathHelper.get_following), [`following-sibling`](https://jrebecchi.github.io/xpath-helper/python/api.html#xpath_helper.XPathHelper.get_following_sibling).\n\nFor each axis, xpath-helper provides 3 methods, like for instance `get_element(filter)`, `get_element_by_tag(tag, filter)`, `get_element_by_svg_tag(svgTag, filter)` for the `descendant`  axis aliased as `element`.\n\n***The complete filter API can be found [here](https://jrebecchi.github.io/xpath-helper/python/api.html#module-xpath_helper).***\n\n\n```python\nfrom xpath_helper import xh, filter\n\n# Find an element into the page, move to its parent, \n# find a brother node of the parent positioned after it.\nel = xh.get_element_by_tag(\n    \'p\', filter.attribute_contains(\'class\', \'very-nice-p\')\n).get_parent().get_following_sibling_by_tag(\'p\')\nstr(el) # "//p[contains(@class, \'very-nice-p\')]/../following-sibling::p"\n\n# Find an element into the page, move to its ancestor \n# containing \'very-nice-p\' ass CSS class, \n# find a brother node of the ancestor positioned before it.\nel = xh.get_element_by_tag(\n    \'p\', filter.attribute_contains(\'class\', \'very-nice-p\')\n).get_ancestor_by_tag(\n  \'div\'\n).get_preceding_sibling(filter.has_attribute(\'data-foo-bar\'))\nstr(el) # "//p[contains(@class, \'very-nice-p\')]/../following-sibling::p//p[contains(@class, \'very-nice-p\')]/ancestor::div/preceding-sibling::*[@data-foo-bar]"\n```\n\nIt is also possible to keep a relative path in a variable and re-use it after.\n```python\nfrom xpath_helper import xh, filter\n# Store the path of a modal window\nmodal = xh.get_element(filter.attribute_contains(\'class\', \'modal\'))\n# Find the Submit button inside the modal window\nsubmit_button = modal.get_element_by_tag(\'button\', filter.value_equals(\'Submit\'))\n# Find the Cancel button inside the modal window\ncancel_button = modal.get_element_by_tag(\'button\', filter.value_equals(\'Cancel\'))\n```\n## Filters\nTo select elements more precisely you can add filters: [on attributes](https://jrebecchi.github.io/xpath-helper/python/api.html#xpath_helper.filter.EmptyFilter.attribute_contains), on [element values](https://jrebecchi.github.io/xpath-helper/python/api.html#xpath_helper.filter.EmptyFilter.value_contains), [element position](https://jrebecchi.github.io/xpath-helper/python/api.html#xpath_helper.filter.EmptyFilter.get), and combining them with conditional operators: [`and_operator(...)`](https://jrebecchi.github.io/xpath-helper/python/api.html#xpath_helper.filter.ValidExpressionFilter.and_operator), [`or_operator(...)`](https://jrebecchi.github.io/xpath-helper/python/api.html#xpath_helper.filter.ValidExpressionFilter.or_operator), and [`not_operator(...)`](https://jrebecchi.github.io/xpath-helper/python/api.html#xpath_helper.filter.EmptyFilter.not_operator).\n\n***The complete filter API can be found [here](https://jrebecchi.github.io/xpath-helper/python/api.html#filter-filter).***\n\n### Attributes\nFind below a few examples of filters on attributes.\n\n```python\nfrom xpath_helper import xh, filter\n# Looks for an element that has a class attribute equals to \'foo\'\nel = xh.get_element(filter.attribute_equals(\'class\', \'foo\'))\n# Looks for an element that has a class attribute containing \'bar\'\nel = xh.get_element(filter.attribute_contains(\'class\', \'bar\'))\n# Looks for an element that has the attribute \'alt\'\nimg = xh.get_element_by_tag(\'img\', filter.has_attribute(\'alt\'))\n# Looks for all the li element with a data-attribute superior to 3\nli = xh.get_element_by_tag(\'li\', filter.attribute_greater_than(\'data-index\', 3))\n```\n### Values\nFind below a few examples of filters on node values.\n```python\nfrom xpath_helper import xh, filter\n\n# Looks for a button whose text is \'Submit\'\nbutton = xh.get_element_by_tag(\'button\', filter.value_equals(\'Submit\'))\n# Looks for an element whose text contains \'foobar\'\nel = xh.get_element(filter.value_contains(\'foobar\'))\n# Looks for all the li element with a value superior to 3\nli = xh.get_element_by_tag(\'li\', filter.value_greater_than(3))\n```\n### Position\nFind below a few examples of filters on node position.\n```python\nfrom xpath_helper import xh, filter\n\n# Looks for the first li element in ul list\nfirst = xh.get_element_by_tag(\'ul\').get_element_by_tag(\'li\', filter.get_first())\n# Looks for the first li element in ul list\nlast = xh.get_element_by_tag(\'ul\').get_element_by_tag(\'li\', filter.get_last())\n# Looks for the third li element in ul list\nthird = xh.get_element_by_tag(\'ul\').get_element_by_tag(\'li\', filter.get(3))\n```\n### Conditional expression\nFind below a few examples of filters with conditional expression.\n\n```python\nfrom xpath_helper import xh, filter\n\n# Find an element that has a CSS class \'a-link\' and contains an attribute href\nel = xh.get_element(\n  filter.attribute_contains(\'class\', \'a-link\').and_operator(\n    filter.has_attribute(\'href\')\n  )\n)\nstr(el) # "//*[contains(@class, \'a-link\') and (@href)]"\n\n# Find an element that has a CSS class \'foo\' or a CSS class \'bar\'\nel = xh.get_element(\n  filter.attribute_contains(\'class\', \'foo\').or_operator(\n    filter.attribute_contains(\'class\', \'bar\')\n  )\n)\nstr(el) # "//*[contains(@class, \'foo\') or (contains(@class, \'bar\'))]"\n\n# Build complex logical expression combining and & or\nel = xh.get_element(\n  filter.and_operator(\n    filter.or_operator(\n      filter.value_contains("JavaScript"),\n      filter.value_contains("Pyhton")\n    ),\n      filter.value_contains("package")\n  )\n)\nstr(el) # "//*[((text()[contains(., \'JavaScript\')] or text()[contains(., \'Pyhton\')]) and text()[contains(., \'package\')])]"\n```\n## SVG\nNavigating into SVG elements from an HTML file can be tricky with XPath, that is why a subset of functions have been added. They are all ending with `...by_svg_tag` and can be used as below.\n\n```python\nfrom xpath_helper import xh, filter\n\n# Store the path of a modal window\npath = xh.get_element_by_svg_tag(\'path\', filter.attribute_equals(\'d\', \'M 310 130 L 90 130 L 90 183.63\'))\nstr(path) # "//*[local-name() = \'path\'][@d=\'M 310 130 L 90 130 L 90 183.63\']"\n\n# Find the Submit button inside the modal window\ng = xh.get_element_by_svg_tag(\n  \'path\', filter.attribute_equals(\'id\', \'id-path\')\n).get_ancestor_by_svg_tag(\'g\')\nstr(g) # "//*[local-name() = \'path\'][@id=\'id-path\']/ancestor::*[local-name() = \'g\']"\n```',
    'author': 'Johann Rebecchi',
    'author_email': 'jrebecchi@protonmail.com',
    'maintainer': None,
    'maintainer_email': None,
    'url': 'https://github.com/jrebecchi/xpath-helper',
    'packages': packages,
    'package_data': package_data,
    'python_requires': '>=3.5',
}


setup(**setup_kwargs)
