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

packages = \
['hrepr']

package_data = \
{'': ['*'], 'hrepr': ['style/*']}

install_requires = \
['ovld>=0.2.7,<0.3.0']

extras_require = \
{':python_version >= "3.6" and python_version < "3.7"': ['dataclasses>=0.8,<0.9']}

setup_kwargs = {
    'name': 'hrepr',
    'version': '0.3.6',
    'description': 'Extensible HTML representation for Python objects.',
    'long_description': '# hrepr\n\n`hrepr` outputs HTML/pretty representations for Python objects.\n\n✅ Nice, colourful representations of lists, dicts, dataclasses, booleans...<br/>\n✅ Ridiculously extensible and configurable<br/>\n✅ Handles recursive data structures<br/>\n✅ Compatible with Jupyter notebooks<br/>\n✅ Also pretty-prints to the terminal<br/>\n\n<img src="https://raw.githubusercontent.com/breuleux/hrepr/master/images/hrepr1.png" width="400px"><img src="https://raw.githubusercontent.com/breuleux/hrepr/master/images/hrepr2.png" width="400px">\n\nI suggest studying the two example files to learn `hrepr`:\n\n* `python examples/exhibit.py > exhibit.html` (and then view the HTML file)\n* `python examples/pretty.py`\n\nAlso see the Jupyter notebook at `examples/Basics.ipynb`, but keep in mind that GitHub can\'t display it properly because of the injected styles/scripts.\n\n\n## Install\n\n```python\npip install hrepr\n```\n\n\n## Usage\n\n```python\nfrom hrepr import hrepr\nobj = {\'potatoes\': [1, 2, 3], \'bananas\': {\'cantaloups\': 8}}\n\n# Print the HTML representation of obj\nprint(hrepr(obj))\n\n# Wrap the representation in <html><body> tags and embed the default\n# css style files in a standalone page, which is saved to obj.html\nhrepr.page(obj, file="obj.html")\n```\n\nIn a Jupyter Notebook, you can return `hrepr(obj)` from any cell and it will show its representation for you. You can also write `display_html(hrepr(obj))`.\n\n\n## Custom representations\n\nA custom representation for an object can be defined using the following three methods (it is not necessary to define all of them, only those that are relevant to your case):\n\n* `__hrepr__(self, H, hrepr)` returns the normal HTML representation.\n    * Use `H.span["some-class"](some-content, some_attr=some_value)` to generate HTML.\n    * Use `hrepr(self.x)` to generate the representation for some subfield `x`.\n    * `hrepr.config` contains any keyword arguments given in the top level call to `hrepr`. For instance, if you call `hrepr(obj, blah=3)`, then `hrepr.config.blah == 3` in all calls to `__hrepr__` down the line (the default value for all keys is `None`).\n* `__hrepr_short__(self, H, hrepr)` returns a *short* representation, ideally of a constant size.\n    * The output of this method is used when we hit max depth, or for repeated references.\n    * Only include bare minimum information. Short means short.\n* `__hrepr_resources__(cls, H)` is a **classmethod** that returns resources common to all instances of the class (typically a stylesheet or a script).\n    * When generating a page, the resources will go in `<head>`.\n    * You can return a list of resources.\n\nNo dependency on `hrepr` is necessary.\n\nFor example:\n\n```python\nclass Person:\n    def __init__(self, name, age, job):\n        self.name = name\n        self.age = age\n        self.job = job\n\n    @classmethod\n    def __hrepr_resources__(cls, H):\n        # Note: you might need to add "!important" next to some rules if\n        # they conflict with defaults from hrepr\'s own CSS.\n        return H.style("""\n            .person {\n                background: magenta !important;\n                border-color: magenta !important;\n            }\n            .person-short { font-weight: bold; color: green; }\n        """)\n\n    def __hrepr__(self, H, hrepr):\n        # H.instance is a special kind of tag to format data like an instance.\n        # Notice how we call the hrepr parameter on self.age and self.job to\n        # format them.\n        return H.instance["person"](\n            H.pair("age", hrepr(self.age), delimiter=" ↦ "),\n            H.pair("job", hrepr(self.job), delimiter=" ↦ "),\n            # The "type" represents the header for the "instance"\n            type=self.name,\n            # "vertical=True" means we\'ll display the pairs as a table with\n            # the delimiters aligned, instead of sticking them horizontally\n            # next to each other\n            vertical=True,\n        )\n\n    def __hrepr_short__(self, H, hrepr):\n        # H.atom is really mostly like H.span, but the textual representation\n        # of H.atom(x) through pprint is "x" whereas H.span(x) would be \n        # "<span>x</span>".\n        return H.atom["person-short"](self.name)\n```\n\n<img src="https://raw.githubusercontent.com/breuleux/hrepr/master/images/hrepr3.png" width="600px">\n\nNote how this also gave us a textual representation *for free*, using `hrepr.pprint`. This feature requires using special tags like `H.instance`, `H.bracketed`, `H.pair` or `H.atom` -- other ones will pretty-print like HTML -- but it\'s neat regardless!\n\n\n## References\n\n`hrepr` (and `hrepr.pprint` for that matter) can handle circular references. Furthermore, if an object is found at several places in a structure, only the first occurrence will be printed in full, and any other will be a numeric reference mapped to the short representation for the object. It looks like this:\n\n<img src="https://raw.githubusercontent.com/breuleux/hrepr/master/images/hrepr4.png" width="600px">\n\nThe `shortrefs` and `norefs` configuration keys control the representation of references:\n\n<img src="https://raw.githubusercontent.com/breuleux/hrepr/master/images/hrepr5.png" width="600px">\n\n`norefs` is ignored when there are circular references.\n\n\n## HTML generation\n\nGenerate HTML using the `H` parameter to `__hrepr__`, or import it and use it directly:\n\n```python\nfrom hrepr import H\nhtml = H.span["bear"](\n    "Only ", H.b("YOU"), " can prevent forest fires!",\n    style="color: brown;"\n)\nprint(html)\n# <span class="bear" style="color: brown;">Only <b>YOU</b> can prevent forest fires!</span>\n```\n\n`H` can be built incrementally: if you have an element, you can call it to add children, index it to add classes, and so on. For instance:\n\n```python\nfrom hrepr import H\nhtml = H.span()\nhtml = html("Only ")\nhtml = html(style="color: brown;")["bear"]\nhtml = html(H.b("YOU"), " can prevent forest fires!")\nprint(html)\n# <span class="bear" style="color: brown;">Only <b>YOU</b> can prevent forest fires!</span>\n```\n\nThis can be handy if you want to tweak generated HTML a little. For example, `hrepr(obj)["fox"]` will tack on the class `fox` to the representation of the object.\n\n\n### Special tags\n\nStandard tags like `H.span`, `H.div`, `H.strong`, etc. are handled according to standards. But there are some special tags which are postprocessed by the hrepr "backend". The HTML backend will reduce them to standard tags, whereas the pprint backend will display them like Python data structures.\n\n* `H.instance(*children, type=<str>, delimiter=<str>, short=<bool>, horizontal=<bool>, vertical=<bool>)`\n    * Represents some kind of object\n    * `type`: the name of the object (or class name)\n        * It is not *necessarily* a string, it can also be a tag.\n    * `delimiter`: the delimiter between elements, defaults to a comma. The HTML formatter ignores this.\n    * `short/horizontal/vertical`: the layout/style\n* `H.bracketed(*children, type=<str>, open=<str>, close=<str>, delimiter=<str>, short=<bool>, horizontal=<bool>, vertical=<bool>)`\n    * `type`: the name of the object (or class name), which is NOT displayed. Instead, the class `hreprt-<name>` is given to the element.\n    * `open/close`: the opening and closing brackets.\n    * `delimiter`: the delimiter between elements, defaults to a comma. The HTML formatter ignores this.\n    * `short/horizontal/vertical`: the layout/style. Lists use horizontal, dicts use vertical, and the short representations use short.\n* `H.pair(x, y, delimiter=<str>)`: a key -> value mapping. They are handled specially inside of `bracketed` and `instance` so that the delimiters are aligned.\n* `H.atom(element, type=<str>)`: essentially equivalent to `H.span["hreprt-<type>"](element)`, or `repr(element)` for pprint.\n\n\n### Including JavaScript libraries\n\nTo make it a bit easier to include and use JavaScript libraries, there is a new tag called `H.javascript` that uses RequireJS under the hood:\n\n* `H.javascript(export=<name>, src=<path>)` declares an import of the script at the given path (you can use a CDN link) and exports it under the given name.\n* `H.javascript(<code>, require=<name/list>, export=<name>)` will wait for the required modules (the names in the require list are the ones given in the export field of other javascript tags) and will provide them to your script.\n\nOptionally you can name a variable as the export, so that it can be required by other scripts.\n\n\n### Constructed elements\n\nEach element gains a new attribute, `constructor`, which must name a function exported using a `javascript` tag. If provided, the function is called with the element as the first argument, and the `options` attribute as the second element.\n\nTo illustrate, here\'s an example of requiring and using Plotly:\n\n```python\nclass Plot:\n    def __init__(self, data):\n        self.data = data\n\n    @classmethod\n    def __hrepr_resources__(cls, H):\n        return [\n            H.javascript(\n                export="plotly",\n                src="https://cdn.plot.ly/plotly-latest.min.js",\n            ),\n            H.javascript(\n                """\n                function PlotlyPlot(element, data) {\n                    plotly.newPlot(element, data);\n                }\n                """,\n                require="plotly",\n                export="PlotlyPlot",\n            ),\n        ]\n\n    def __hrepr__(self, H, hrepr):\n        return H.div(\n            constructor="PlotlyPlot",\n            options=[{"x": list(range(len(self.data))), "y": list(self.data)}],\n        )\n```\n\n* `__hrepr_resources__` declares two resources:\n  * The plotly library, loaded from a CDN, and exported as `plotly`.\n  * A small snippet of code that requires `plotly` and declares a `PlotlyPlot` function that takes an element and some data.\n* `__hrepr__` returns a div that has `PlotlyPlot` as the constructor. It will be called as `new PlotlyPlot(element, options)` (this is so it may equally be defined as an ES6 class if you wish). The options serialized as JSON and dumped into the call -- they are the "data" argument in make_plot.\n\nWe can then test it:\n\n<img src="https://raw.githubusercontent.com/breuleux/hrepr/master/images/hrepr6.png" width="600px">\n\nAlternatively, you can define custom elements using the [custom element API](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements). You can put the definition in a `<javascript>` tag that requires the library you want to use, and then simply use `H.my_element(...)` to instantiate it. The underscore in the Python attribute will become a dash, so hrepr will generate `<my-element>...</my-element>`.\n\n## Customize hrepr\n\n### Mixins\n\nIf you want to *really* customize hrepr, you can use mixins. They look like a bit of black magic, but they\'re simple enough:\n\n```python\n# ovld is one of the dependencies of hrepr\nfrom ovld import ovld, has_attribute\nfrom hrepr import hrepr, Hrepr\n\nclass MyMixin(Hrepr):\n    # Change the representation of integers\n\n    def hrepr_resources(self, cls: int):\n        # Note: in hrepr_resources, cls is the int type, not an integer\n        return self.H.style(".my-integer { color: fuchsia; }")\n\n    def hrepr(self, n: int):\n        return self.H.span["my-integer"]("The number ", str(n))\n\n    # Specially handle any object with a "quack" method\n\n    def hrepr(self, duck: has_attribute("quack")):\n        return self.H.span("🦆")\n```\n\n<img src="https://raw.githubusercontent.com/breuleux/hrepr/master/images/hrepr7.png" width="600px">\n\nThe annotation for a rule can either be a type, `ovld.has_attribute`, or pretty much any function wrapped with the `ovld.meta` decorator, as long as the function operates on classes. See the documentation for [ovld](https://github.com/breuleux/ovld#other-features) for more information.\n\nAnd yes, you can define `hrepr` multiple times inside the class, as long as they have distinct annotations and you inherit from `Hrepr`. You can also define `hrepr_short` or `hrepr_resources` the same way.\n\n### Postprocessors\n\n`hrepr` can be given a postprocessor that is called on the representation of any object. You can use this to do things like highlighting specific objects:\n\n```python\nfrom hrepr import H\n\nstyle = H.style(".highlight { border: 3px solid red !important; }")\n\ndef highlight(x):\n    def postprocess(element, obj, hrepr):\n        if obj == x:\n            # Adds the "highlight" class and attaches a style\n            return element["highlight"].fill(resources=style)\n        else:\n            return element\n\n    return postprocess\n\nhrepr([1, 2, [3, 4, 2]], postprocess=highlight(2))\n```\n\n<img src="https://raw.githubusercontent.com/breuleux/hrepr/master/images/hrepr8.png" width="600px">\n\n\n### hrepr variants\n\nTo put this all together, you can create a *variant* of `hrepr`:\n\n```python\nhrepr2 = hrepr.variant(mixins=MyMixin, postprocess=highlight(2))\nhrepr2([1, 2, 3])  # Will use the mixins and postprocessor\n```\n\n\n### Configure the hrepr function itself\n\nAlternatively, you can configure the main `hrepr`:\n\n```python\nhrepr.configure(mixins=MyMixin, postprocess=highlight(2))\n```\n\nBut keep in mind that unlike the variant, the above will modify `hrepr` for everything else as well.\n',
    'author': 'Olivier Breuleux',
    'author_email': 'breuleux@gmail.com',
    'maintainer': None,
    'maintainer_email': None,
    'url': 'https://github.com/breuleux/hrepr',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'extras_require': extras_require,
    'python_requires': '>=3.6,<4.0',
}


setup(**setup_kwargs)
